TransWikia.com

Implementing FM receiver using GNU Radio C++ API

Amateur Radio Asked by Oleksandr Kravchuk on September 27, 2021

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.

FM Receiver in GNU Radio

#include <gnuradio/analog/quadrature_demod_cf.h>
#include <gnuradio/audio/sink.h>
#include <gnuradio/blocks/null_sink.h>
#include <gnuradio/filter/fir_filter_blk.h>
#include <gnuradio/filter/firdes.h>
#include <gnuradio/filter/freq_xlating_fir_filter.h>
#include <gnuradio/filter/iir_filter_ffd.h>
#include <gnuradio/filter/pfb_arb_resampler_ccf.h>
#include <gnuradio/top_block.h>
#include <osmosdr/source.h>
#include <gnuradio/analog/simple_squelch_cc.h>

void calculate_iir_taps(double tau, float d_quad_rate);

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
    top_block_sptr top_block = make_top_block("fm_radio");

    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);

    // WBFM Receive
    int max_dev = 75e3;
    int quad_rate = 288e3;
    float fm_demod_gain = quad_rate / (2 * M_PI * max_dev);
    analog::quadrature_demod_cf::sptr d_quad = analog::quadrature_demod_cf::make(fm_demod_gain);

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

    calculate_iir_taps(50.0e-6, quad_rate);

    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_filter, 0, d_quad, 0);
    top_block->connect(d_quad, 0, d_deemph, 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;
}

void calculate_iir_taps(double tau, float d_quad_rate)
{
    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;
        double  fs = d_quad_rate;

        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})

One Answer

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.

Answered by Kevin Reid AG6YO on September 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP