Add variable fm low pass filter option

This commit is contained in:
Christian W. Zuckschwerdt 2021-02-05 16:30:31 +01:00
parent 4258610ddf
commit 2eda6ac964
5 changed files with 67 additions and 39 deletions

View file

@ -94,10 +94,15 @@ typedef struct filter_state {
/// FM_Demod state buffer.
typedef struct demodfm_state {
int32_t xr; ///< Last I/Q sample, real part
int32_t xi; ///< Last I/Q sample, imag part
int32_t xf; ///< Last Instantaneous frequency
int32_t yf; ///< Last Instantaneous frequency, low pass filtered
int32_t xr; ///< Last I/Q sample, real part
int32_t xi; ///< Last I/Q sample, imag part
int32_t xf; ///< Last Instantaneous frequency
int32_t yf; ///< Last Instantaneous frequency, low pass filtered
uint32_t rate; ///< Current sample rate
int32_t alp_16[2]; ///< Current low pass filter A coeffs, 16 bit
int32_t blp_16[2]; ///< Current low pass filter B coeffs, 16 bit
int64_t alp_32[2]; ///< Current low pass filter A coeffs, 32 bit
int64_t blp_32[2]; ///< Current low pass filter B coeffs, 32 bit
} demodfm_state_t;
/** Lowpass filter.
@ -119,10 +124,10 @@ void baseband_low_pass_filter(uint16_t const *x_buf, int16_t *y_buf, uint32_t le
@param[in,out] state State to store between chunk processing
@param fpdm Index of filter setting to use
*/
void baseband_demod_FM(uint8_t const *x_buf, int16_t *y_buf, unsigned long num_samples, demodfm_state_t *state, unsigned fpdm);
void baseband_demod_FM(uint8_t const *x_buf, int16_t *y_buf, unsigned long num_samples, uint32_t samp_rate, float low_pass, demodfm_state_t *state);
/// For evaluation.
void baseband_demod_FM_cs16(int16_t const *x_buf, int16_t *y_buf, unsigned long num_samples, demodfm_state_t *state, unsigned fpdm);
void baseband_demod_FM_cs16(int16_t const *x_buf, int16_t *y_buf, unsigned long num_samples, uint32_t samp_rate, float low_pass, demodfm_state_t *state);
/** Initialize tables and constants.
Should be called once at startup.

View file

@ -20,6 +20,7 @@ struct dm_state {
float level_limit;
float min_level;
float min_snr;
float low_pass;
int use_mag_est;
int detect_verbosity;

View file

@ -174,22 +174,32 @@ static int16_t atan2_int16(int32_t y, int32_t x)
return angle;
}
/// [b,a] = butter(1, 0.1) -> 3x tau (95%) ~10 samples, 250k -> 40us, 1024k -> 10us
//static int const alp[2] = {FIX(1.00000), FIX(0.72654)};
//static int const blp[2] = {FIX(0.13673), FIX(0.13673)};
/// [b,a] = butter(1, 0.2) -> 3x tau (95%) ~5 samples, 250k -> 20us, 1024k -> 5us
//static int const alp[2] = {FIX(1.00000), FIX(0.50953)};
//static int const blp[2] = {FIX(0.24524), FIX(0.24524)};
static int32_t const alps_16[][2] = {{FIX(1.00000) >> 1, FIX(0.72654) >> 1},
{FIX(1.00000) >> 1, FIX(0.50953) >> 1}};
static int32_t const blps_16[][2] = {{FIX(0.13673) >> 1, FIX(0.13673) >> 1},
{FIX(0.24524) >> 1, FIX(0.24524) >> 1}};
/// Fast Instantaneous frequency and Low Pass filter, CU8 samples
void baseband_demod_FM(uint8_t const *x_buf, int16_t *y_buf, unsigned long num_samples, demodfm_state_t *state, unsigned fpdm)
void baseband_demod_FM(uint8_t const *x_buf, int16_t *y_buf, unsigned long num_samples, uint32_t samp_rate, float low_pass, demodfm_state_t *state)
{
// Select filter coeffs
const int32_t *alp = alps_16[fpdm];
const int32_t *blp = blps_16[fpdm];
// Select filter coeffs, [b,a] = butter(1, cutoff)
// e.g [b,a] = butter(1, 0.1) -> 3x tau (95%) ~10 samples, 250k -> 40us, 1024k -> 10us
// a = 1.00000, 0.72654; b = 0.13673, 0.13673;
// e.g. [b,a] = butter(1, 0.2) -> 3x tau (95%) ~5 samples, 250k -> 20us, 1024k -> 5us
// a = 1.00000, 0.50953; b = 0.24524, 0.24524;
if (state->rate != samp_rate) {
if (low_pass > 1e4) {
low_pass = low_pass / samp_rate;
} else if (low_pass >= 1.0) {
low_pass = 1e6 / low_pass / samp_rate;
}
fprintf(stderr, "%s: low pass filter for %u Hz at cutoff %.0f Hz, %.1f us\n",
__func__, samp_rate, samp_rate * low_pass, 1e6 / (samp_rate * low_pass));
double ita = 1.0 / tan(0.5 * M_PI * low_pass);
double gain = 1.0 / (1.0 + ita) / 2; // prescaled by div 2
state->alp_16[0] = FIX(1.0);
state->alp_16[1] = FIX((ita - 1.0) * gain); // scaled by -1
state->blp_16[0] = FIX(gain);
state->blp_16[1] = FIX(gain);
state->rate = samp_rate;
}
int32_t const *alp = state->alp_16;
int32_t const *blp = state->blp_16;
// Pre-feed old sample
int16_t x0r = state->xr; // IQ sample: x[n], real
@ -257,23 +267,32 @@ static int32_t atan2_int32(int32_t y, int32_t x)
return angle;
}
/// [b,a] = butter(1, 0.1) -> 3x tau (95%) ~10 samples, 250k -> 40us, 1024k -> 10us
//static int64_t const alp[2] = {FIX32(1.00000), FIX32(0.72654)};
//static int64_t const blp[2] = {FIX32(0.13673), FIX32(0.13673)};
/// [b,a] = butter(1, 0.2) -> 3x tau (95%) ~5 samples, 250k -> 20us, 1024k -> 5us
//static int64_t const alp[2] = {FIX32(1.00000), FIX32(0.50953)};
//static int64_t const blp[2] = {FIX32(0.24524), FIX32(0.24524)};
static int64_t const alps_32[][2] = {{FIX32(1.00000), FIX32(0.72654)},
{FIX32(1.00000), FIX32(0.50953)}};
static int64_t const blps_32[][2] = {{FIX32(0.13673), FIX32(0.13673)},
{FIX32(0.24524), FIX32(0.24524)}};
/// Fast Instantaneous frequency and Low Pass filter, CS16 samples.
void baseband_demod_FM_cs16(int16_t const *x_buf, int16_t *y_buf, unsigned long num_samples, demodfm_state_t *state, unsigned fpdm)
void baseband_demod_FM_cs16(int16_t const *x_buf, int16_t *y_buf, unsigned long num_samples, uint32_t samp_rate, float low_pass, demodfm_state_t *state)
{
// Select filter coeffs
const int64_t *alp = alps_32[fpdm];
const int64_t *blp = blps_32[fpdm];
// Select filter coeffs, [b,a] = butter(1, cutoff)
// e.g [b,a] = butter(1, 0.1) -> 3x tau (95%) ~10 samples, 250k -> 40us, 1024k -> 10us
// a = 1.00000, 0.72654; b = 0.13673, 0.13673;
// e.g. [b,a] = butter(1, 0.2) -> 3x tau (95%) ~5 samples, 250k -> 20us, 1024k -> 5us
// a = 1.00000, 0.50953; b = 0.24524, 0.24524;
if (state->rate != samp_rate) {
if (low_pass > 1e4) {
low_pass = low_pass / samp_rate;
} else if (low_pass >= 1.0) {
low_pass = 1e6 / low_pass / samp_rate;
}
fprintf(stderr, "%s: low pass filter for %u Hz at cutoff %.0f Hz, %.1f us\n",
__func__, samp_rate, samp_rate * low_pass, 1e6 / (samp_rate * low_pass));
double ita = 1.0 / tan(0.5 * M_PI * low_pass);
double gain = 1.0 / (1.0 + ita);
state->alp_32[0] = FIX32(1.0);
state->alp_32[1] = FIX32((ita - 1.0) * gain); // scaled by -1
state->blp_32[0] = FIX32(gain);
state->blp_32[1] = FIX32(gain);
state->rate = samp_rate;
}
int64_t const *alp = state->alp_32;
int64_t const *blp = state->blp_32;
// Pre-feed old sample
int32_t x0r = state->xr; // IQ sample: x[n], real

View file

@ -376,10 +376,11 @@ static void sdr_callback(unsigned char *iq_buf, uint32_t len, void *ctx)
}
if (demod->enable_FM_demod) {
float low_pass = demod->low_pass != 0.0f ? demod->low_pass : fpdm ? 0.2f : 0.1f;
if (demod->sample_size == 1) { // CU8
baseband_demod_FM(iq_buf, demod->buf.fm, n_samples, &demod->demod_FM_state, fpdm);
baseband_demod_FM(iq_buf, demod->buf.fm, n_samples, cfg->samp_rate, low_pass, &demod->demod_FM_state);
} else { // CS16
baseband_demod_FM_cs16((int16_t *)iq_buf, demod->buf.fm, n_samples, &demod->demod_FM_state, fpdm);
baseband_demod_FM_cs16((int16_t *)iq_buf, demod->buf.fm, n_samples, cfg->samp_rate, low_pass, &demod->demod_FM_state);
}
}
@ -1126,6 +1127,8 @@ static void parse_conf_option(r_cfg_t *cfg, int opt, char *arg)
cfg->demod->min_level = arg_float(p + 8, "-Y minlevel: ");
else if (!strncasecmp(p, "minsnr", 6))
cfg->demod->min_snr = arg_float(p + 6, "-Y minsnr: ");
else if (!strncasecmp(p, "filter", 6))
cfg->demod->low_pass = arg_float(p + 6, "-Y filter: ");
else {
fprintf(stderr, "Unknown pulse detector setting: %s\n", p);
usage(1);

View file

@ -135,7 +135,7 @@ int main(int argc, char *argv[])
);
write_buf("bb.lp.am.s16", u16_buf, sizeof(int16_t) * n_samples);
MEASURE("baseband_demod_FM",
baseband_demod_FM(cu8_buf, s16_buf, n_samples, &fm_state, 0);
baseband_demod_FM(cu8_buf, s16_buf, n_samples, 250000, 0.1, &fm_state);
);
write_buf("bb.fm.s16", s16_buf, sizeof(int16_t) * n_samples);
@ -161,7 +161,7 @@ int main(int argc, char *argv[])
//write_buf("bb.fm.s32", s32_buf, sizeof(int32_t) * n_samples);
MEASURE("baseband_demod_FM_cs16",
baseband_demod_FM_cs16(cs16_buf, s16_buf, n_samples, &fm_state, 0);
baseband_demod_FM_cs16(cs16_buf, s16_buf, n_samples, 250000, 0.1, &fm_state);
);
write_buf("bb.cs16.fm.s16", s16_buf, sizeof(int16_t) * n_samples);