This is my first ever experience with GNU Radio. I am trying to implement a simple FM receiver shown below with its C++ API.

I seem to have pedantically deconstructed this receiver into its smallest components and implemented them with GNU Radio's C++ API but does not work.

It runs but I hear no sound which makes me suspect I have misconfigured some component, missed to set some sort of gain, etc.

Any feedback is appreciated. Thank you.

#include <gnuradio/analog/quadrature_demod_cf.h>
#include <osmosdr/source.h>

std::vector<double> d_fftaps;  /*! Feed forward taps. */
std::vector<double> d_fbtaps;  /*! Feed back taps. */

using namespace gr;

int main(int argc, char** argv)
{
int sample_rate = 2.88e6; // audio card sample rate
int audio_decimation = 5;

// Build a top block that ill contain flowgraph blocks

osmosdr::source::sptr osmocom = osmosdr::source::make("rtl=0,repeat=true,throttle=true");
osmocom->set_center_freq(96.3e6);
osmocom->set_sample_rate(sample_rate);
osmocom->set_gain(20, 0);
osmocom->set_if_gain(20, 0);
osmocom->set_bb_gain(20, 0);

// Low pass filter
unsigned int flt_size = 1024;
std::vector<float> low_pass_filter = filter::firdes::low_pass(1, sample_rate, 100e3, 10e3);
filter::pfb_arb_resampler_ccf::sptr d_filter = filter::pfb_arb_resampler_ccf::make(288, low_pass_filter, flt_size);

int max_dev = 75e3;
float fm_demod_gain = quad_rate / (2 * M_PI * max_dev);

d_fftaps.resize(2);
d_fbtaps.resize(2);

filter::iir_filter_ffd::sptr d_deemph = \
filter::iir_filter_ffd::make(d_fftaps, d_fbtaps, false);

int audio_rate = quad_rate / audio_decimation;
int width_of_transition_band = audio_rate / 32;
std::vector<float> d_pll_taps = \
filter::firdes::low_pass(1, quad_rate, audio_rate / 2 - width_of_transition_band, width_of_transition_band);
filter::fir_filter_fff::sptr low_pass_filter2 = \
filter::fir_filter_fff::make(1, d_pll_taps);

// Rational Resamplifier - might not be needed hence not used atm
filter::freq_xlating_fir_filter_ccf::sptr filt = \
gr::filter::freq_xlating_fir_filter_ccf::make(288, {1}, 0.0, sample_rate);

audio::sink::sptr sink = audio::sink::make(48000);

top_block->connect(osmocom, 0, d_filter, 0);
top_block->connect(d_deemph, 0, low_pass_filter2, 0);
top_block->connect(low_pass_filter2, 0, sink, 0);

top_block->run();

return 0;
}

{
if (tau > 1.0e-9)
{
// copied from fm_emph.py in gr-analog
double  w_c;    // Digital corner frequency
double  w_ca;   // Prewarped analog corner frequency
double  k, z1, p1, b0;

w_c = 1.0 / tau;
w_ca = 2.0 * fs * tan(w_c / (2.0 * fs));

// Resulting digital pole, zero, and gain term from the bilinear
// transformation of H(s) = w_ca / (s + w_ca) to
// H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1)
k = -w_ca / (2.0 * fs);
z1 = -1.0;
p1 = (1.0 + k) / (1.0 - k);
b0 = -k / (1.0 - k);

d_fftaps[0] = b0;
d_fftaps[1] = -z1 * b0;
d_fbtaps[0] = 1.0;
d_fbtaps[1] = -p1;
}
else
{
d_fftaps[0] = 1.0;
d_fftaps[1] = 0.0;
d_fbtaps[0] = 0.0;
d_fbtaps[1] = 0.0;
}
}


CMakeLists.txt for those wanting to try it out:

cmake_minimum_required(VERSION 3.8)

include_directories(
$${GNURADIO_ALL_INCLUDE_DIRS}$${Boost_INCLUDE_DIRS}
$ENV{HOME}/.grc_gnuradio ) set(GR_LIBRARIES boost_system gnuradio-blocks gnuradio-filter gnuradio-analog gnuradio-audio gnuradio-runtime gnuradio-pmt gnuradio-osmosdr log4cpp ) add_executable(fm_radio fm_radio.cpp) target_link_libraries(fm_radio${GR_LIBRARIES})

• Hello Oleksandr, and welcome to ham.stackexchange.com! – rclocher3 Jul 13 at 14:01
• Cheers, @rclocher3! – Oleksandr Kravchuk Jul 13 at 16:39

There is at least one error in your code that would result in silence:

std::vector<double> d_fftaps;  /*! Feed forward taps. */
std::vector<double> d_fbtaps;  /*! Feed back taps. */
d_fftaps.resize(2);
d_fbtaps.resize(2);

filter::iir_filter_ffd::sptr d_deemph = \
filter::iir_filter_ffd::make(d_fftaps, d_fbtaps, false);


The taps of this IIR filter are all zero (because you resized the vectors but did not write any values into them), so the output signal will be always zero.

As a quick test to see if this is the sole problem, you can remove the deemphasis filter block entirely; the audio frequency response will be wrong but you'll still be able to tell the difference.

You could also remove the squelch block temporarily, as one fewer thing to go wrong. The absolute minimum you need for a receiver that will pass some coherent signal is a decimating low-pass filter to select the desired signal and adapt the sample rate, and the quadrature demodulator; everything else is refinement.

• Thanks for your assistance. You were right, I have completely forgotten to set up the taps. I have borrowed the code for calculating iir taps from Gqrx and can now hear some noise. The main suspect is my implementation of the WBFM Receive block which unfortunately has no C++ implementation, so I had to copy it from the Python blocks it is comprised of. I have updated the code. Is there an example of a simple FM receiver without usage of WBFM Recive block, so I could study it? Thank you again. – Oleksandr Kravchuk Jul 13 at 1:55
• @OleksandrKravchuk Gqrx is the one example I'd know of to point you at. I recommend focusing on understanding your signal processing stages and testing them individually to ensure they behave as you expect (and you could run them with file input and output blocks and compare the results to GRC/Python versions), rather than looking for examples to copy. – Kevin Reid AG6YO Jul 13 at 2:10