From 93dcce14b4ed28a206e242edae8c94e1f845865c Mon Sep 17 00:00:00 2001 From: Clayton Smith Date: Mon, 28 Dec 2020 08:45:03 -0500 Subject: [PATCH] Fix FM stereo demodulator --- resources/news.txt | 1 + src/dsp/stereo_demod.cpp | 62 ++++++++++++++++------------------------ src/dsp/stereo_demod.h | 11 +++---- src/receivers/wfmrx.cpp | 23 +++++++-------- src/receivers/wfmrx.h | 1 - 5 files changed, 42 insertions(+), 56 deletions(-) diff --git a/resources/news.txt b/resources/news.txt index 286e5a807..46f5aae42 100644 --- a/resources/news.txt +++ b/resources/news.txt @@ -5,6 +5,7 @@ NEW: Display filename when recording audio. FIXED: Audio output doesn't work on MacOS Big Sur. FIXED: DC remove button doesn't work when Demod Off mode is selected. + IMPROVED: Better demodulation of FM stereo signals. 2.14.3: Released December 9, 2020 diff --git a/src/dsp/stereo_demod.cpp b/src/dsp/stereo_demod.cpp index 5f836444a..5a425f5ca 100644 --- a/src/dsp/stereo_demod.cpp +++ b/src/dsp/stereo_demod.cpp @@ -61,26 +61,26 @@ stereo_demod::stereo_demod(float input_rate, float audio_rate, bool stereo, bool if (d_stereo) { - lpf1 = make_lpf_ff(d_input_rate, cutof_freq, 2e3); // FIXME + lpf1 = make_lpf_ff(d_input_rate, cutof_freq, 2e3, -2.1); // FIXME audio_rr1 = make_resampler_ff(d_audio_rate/d_input_rate); deemph1 = make_fm_deemph(d_audio_rate, 50.0e-6); if (!d_oirt) { d_tone_taps = gr::filter::firdes::complex_band_pass( - 1.0, // gain, + 20.0, // gain, d_input_rate, // sampling_freq - 18800., // low_cutoff_freq - 19200., // high_cutoff_freq - 300.); // transition_width + 18980., // low_cutoff_freq + 19020., // high_cutoff_freq + 1000.); // transition_width pll = gr::analog::pll_refout_cc::make(0.001, // loop_bw FIXME - 2*M_PI * 19200 / input_rate, // max_freq - 2*M_PI * 18800 / input_rate); // min_freq + 2*M_PI * 19020 / input_rate, // max_freq + 2*M_PI * 18980 / input_rate); // min_freq subtone = gr::blocks::multiply_cc::make(); } else { d_tone_taps = gr::filter::firdes::complex_band_pass( 1.0, // gain, - d_input_rate, // sampling_freq + d_input_rate, // sampling_freq 31200., // low_cutoff_freq 31300., // high_cutoff_freq 100.); // transition_width @@ -90,62 +90,50 @@ stereo_demod::stereo_demod(float input_rate, float audio_rate, bool stereo, bool } tone = gr::filter::fir_filter_fcc::make(1, d_tone_taps); + delay = gr::blocks::delay::make(sizeof(float), (d_tone_taps.size() - 1) / 2); lo = gr::blocks::complex_to_imag::make(); - d_pll_taps = gr::filter::firdes::band_pass( - 1.0, // gain, - d_input_rate, // sampling_freq - 37600., // low_cutoff_freq - 38400., // high_cutoff_freq - 400.); // transition_width - lo2 = gr::filter::fir_filter_fff::make(1, d_pll_taps); - mixer = gr::blocks::multiply_ff::make(); - cdp = gr::blocks::multiply_const_ff::make( 3.61); // ±0.02 - cdm = gr::blocks::multiply_const_ff::make(-3.61); // ±0.02 - - add0 = gr::blocks::add_ff::make(); - add1 = gr::blocks::add_ff::make(); + add = gr::blocks::add_ff::make(); + sub = gr::blocks::sub_ff::make(); /* connect block */ if (!d_oirt) { connect(self(), 0, tone, 0); + connect(self(), 0, delay, 0); connect(tone, 0, pll, 0); connect(pll, 0, subtone, 0); connect(pll, 0, subtone, 1); connect(subtone, 0, lo, 0); - connect(lo, 0, lo2, 0); - connect(lo2, 0, mixer, 0); + connect(lo, 0, mixer, 0); } else { connect(self(), 0, tone, 0); + connect(self(), 0, delay, 0); connect(tone, 0, pll, 0); connect(pll, 0, lo, 0); connect(lo, 0, mixer, 0); } - connect(self(), 0, mixer, 1); + connect(delay, 0, mixer, 1); - connect(self(), 0, lpf0, 0); - connect(mixer, 0, lpf1, 0); + connect(delay, 0, lpf0, 0); + connect(mixer, 0, lpf1, 0); connect(lpf0, 0, audio_rr0, 0); // sum - connect(lpf1, 0, audio_rr1, 0); - - connect(audio_rr1, 0, cdp, 0); // +delta - connect(audio_rr1, 0, cdm, 0); // -delta + connect(lpf1, 0, audio_rr1, 0); // delta - connect(audio_rr0, 0, add0, 0); - connect(cdp, 0, add0, 1); - connect(add0, 0, deemph0, 0); + connect(audio_rr0, 0, add, 0); + connect(audio_rr1, 0, add, 1); + connect(add, 0, deemph0, 0); connect(deemph0, 0, self(), 0); // left = sum + delta - connect(audio_rr0, 0, add1, 0); - connect(cdm, 0, add1, 1); - connect(add1, 0, deemph1, 0); - connect(deemph1, 0, self(), 1); // right = sum + delta + connect(audio_rr0, 0, sub, 0); + connect(audio_rr1, 0, sub, 1); + connect(sub, 0, deemph1, 0); + connect(deemph1, 0, self(), 1); // right = sum - delta } else // if (!d_stereo) { diff --git a/src/dsp/stereo_demod.h b/src/dsp/stereo_demod.h index 04f755511..a462ce054 100644 --- a/src/dsp/stereo_demod.h +++ b/src/dsp/stereo_demod.h @@ -34,15 +34,18 @@ #include #include #include +#include #else #include #include #include #include +#include #endif #include #include +#include #include #include "dsp/fm_deemph.h" #include "dsp/lpf.h" @@ -94,10 +97,10 @@ class stereo_demod : public gr::hier_block2 private: /* GR blocks */ gr::filter::fir_filter_fcc::sptr tone; /*!< Pilot tone BPF. */ + gr::blocks::delay::sptr delay; /*!< Delay to match pilot tone BPF. */ gr::analog::pll_refout_cc::sptr pll; /*!< Pilot tone PLL. */ gr::blocks::multiply_cc::sptr subtone; /*!< Stereo subtone. */ gr::blocks::complex_to_imag::sptr lo; /*!< Complex tone imag. */ - gr::filter::fir_filter_fff::sptr lo2; /*!< Subtone BPF. */ gr::blocks::multiply_ff::sptr mixer; /*!< Balance mixer. */ lpf_ff_sptr lpf0; /*!< Low-pass filter #0. */ lpf_ff_sptr lpf1; /*!< Low-pass filter #1. */ @@ -105,10 +108,8 @@ class stereo_demod : public gr::hier_block2 resampler_ff_sptr audio_rr1; /*!< Audio resampler #1. */ fm_deemph_sptr deemph0; /*!< FM de-emphasis #0. */ fm_deemph_sptr deemph1; /*!< FM de-emphasis #1. */ - gr::blocks::multiply_const_ff::sptr cdp; /*!< Channel delta (plus). */ - gr::blocks::multiply_const_ff::sptr cdm; /*!< Channel delta (minus). */ - gr::blocks::add_ff::sptr add0; /*!< Left stereo channel. */ - gr::blocks::add_ff::sptr add1; /*!< Right stereo channel. */ + gr::blocks::add_ff::sptr add; /*!< Left stereo channel. */ + gr::blocks::sub_ff::sptr sub; /*!< Right stereo channel. */ /* other parameters */ float d_input_rate; /*! Input rate. */ diff --git a/src/receivers/wfmrx.cpp b/src/receivers/wfmrx.cpp index 15804ae33..abbaf1bb8 100644 --- a/src/receivers/wfmrx.cpp +++ b/src/receivers/wfmrx.cpp @@ -27,7 +27,6 @@ #include "receivers/wfmrx.h" #define PREF_QUAD_RATE 240e3 // Nominal channel spacing is 200 kHz -#define PREF_MIDLE_RATE 120e3 // Midle rate for stereo decoder wfmrx_sptr make_wfmrx(float quad_rate, float audio_rate) { @@ -47,10 +46,9 @@ wfmrx::wfmrx(float quad_rate, float audio_rate) sql = gr::analog::simple_squelch_cc::make(-150.0, 0.001); meter = make_rx_meter_c(DETECTOR_TYPE_RMS); demod_fm = make_rx_demod_fm(PREF_QUAD_RATE, 75000.0, 0.0); - midle_rr = make_resampler_ff(PREF_MIDLE_RATE/PREF_QUAD_RATE); - stereo = make_stereo_demod(PREF_MIDLE_RATE, d_audio_rate, true); - stereo_oirt = make_stereo_demod(PREF_MIDLE_RATE, d_audio_rate, true, true); - mono = make_stereo_demod(PREF_MIDLE_RATE, d_audio_rate, false); + stereo = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, true); + stereo_oirt = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, true, true); + mono = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, false); /* create rds blocks but dont connect them */ rds = make_rx_rds(PREF_QUAD_RATE); @@ -64,8 +62,7 @@ wfmrx::wfmrx(float quad_rate, float audio_rate) connect(filter, 0, meter, 0); connect(filter, 0, sql, 0); connect(sql, 0, demod_fm, 0); - connect(demod_fm, 0, midle_rr, 0); - connect(midle_rr, 0, mono, 0); + connect(demod_fm, 0, mono, 0); connect(mono, 0, self(), 0); // left channel connect(mono, 1, self(), 1); // right channel } @@ -199,19 +196,19 @@ void wfmrx::set_demod(int demod) case WFMRX_DEMOD_MONO: default: - disconnect(midle_rr, 0, mono, 0); + disconnect(demod_fm, 0, mono, 0); disconnect(mono, 0, self(), 0); // left channel disconnect(mono, 1, self(), 1); // right channel break; case WFMRX_DEMOD_STEREO: - disconnect(midle_rr, 0, stereo, 0); + disconnect(demod_fm, 0, stereo, 0); disconnect(stereo, 0, self(), 0); // left channel disconnect(stereo, 1, self(), 1); // right channel break; case WFMRX_DEMOD_STEREO_UKW: - disconnect(midle_rr, 0, stereo_oirt, 0); + disconnect(demod_fm, 0, stereo_oirt, 0); disconnect(stereo_oirt, 0, self(), 0); // left channel disconnect(stereo_oirt, 1, self(), 1); // right channel break; @@ -221,19 +218,19 @@ void wfmrx::set_demod(int demod) case WFMRX_DEMOD_MONO: default: - connect(midle_rr, 0, mono, 0); + connect(demod_fm, 0, mono, 0); connect(mono, 0, self(), 0); // left channel connect(mono, 1, self(), 1); // right channel break; case WFMRX_DEMOD_STEREO: - connect(midle_rr, 0, stereo, 0); + connect(demod_fm, 0, stereo, 0); connect(stereo, 0, self(), 0); // left channel connect(stereo, 1, self(), 1); // right channel break; case WFMRX_DEMOD_STEREO_UKW: - connect(midle_rr, 0, stereo_oirt, 0); + connect(demod_fm, 0, stereo_oirt, 0); connect(stereo_oirt, 0, self(), 0); // left channel connect(stereo_oirt, 1, self(), 1); // right channel break; diff --git a/src/receivers/wfmrx.h b/src/receivers/wfmrx.h index 2ae4d0f23..dafdf2a98 100644 --- a/src/receivers/wfmrx.h +++ b/src/receivers/wfmrx.h @@ -122,7 +122,6 @@ class wfmrx : public receiver_base_cf rx_meter_c_sptr meter; /*!< Signal strength. */ gr::analog::simple_squelch_cc::sptr sql; /*!< Squelch. */ rx_demod_fm_sptr demod_fm; /*!< FM demodulator. */ - resampler_ff_sptr midle_rr; /*!< Resampler. */ stereo_demod_sptr stereo; /*!< FM stereo demodulator. */ stereo_demod_sptr stereo_oirt; /*!< FM stereo oirt demodulator. */ stereo_demod_sptr mono; /*!< FM stereo demodulator OFF. */