Skip to content

Commit

Permalink
Fix FM stereo demodulator
Browse files Browse the repository at this point in the history
  • Loading branch information
argilo committed Dec 28, 2020
1 parent a6d8ddd commit 93dcce1
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 56 deletions.
1 change: 1 addition & 0 deletions resources/news.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 25 additions & 37 deletions src/dsp/stereo_demod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
{
Expand Down
11 changes: 6 additions & 5 deletions src/dsp/stereo_demod.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@
#include <gnuradio/blocks/multiply_ff.h>
#include <gnuradio/blocks/multiply_const_ff.h>
#include <gnuradio/blocks/add_ff.h>
#include <gnuradio/blocks/sub_ff.h>
#else
#include <gnuradio/filter/fir_filter_blk.h>
#include <gnuradio/blocks/multiply.h>
#include <gnuradio/blocks/multiply_const.h>
#include <gnuradio/blocks/add_blk.h>
#include <gnuradio/blocks/sub.h>
#endif

#include <gnuradio/analog/pll_refout_cc.h>
#include <gnuradio/blocks/complex_to_imag.h>
#include <gnuradio/blocks/delay.h>
#include <vector>
#include "dsp/fm_deemph.h"
#include "dsp/lpf.h"
Expand Down Expand Up @@ -94,21 +97,19 @@ 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. */
resampler_ff_sptr audio_rr0; /*!< Audio resampler #0. */
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. */
Expand Down
23 changes: 10 additions & 13 deletions src/receivers/wfmrx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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);
Expand All @@ -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
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
1 change: 0 additions & 1 deletion src/receivers/wfmrx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down

0 comments on commit 93dcce1

Please sign in to comment.