Skip to content

Commit

Permalink
Step gqrx-sdr#5: First FM stereo implementation works!
Browse files Browse the repository at this point in the history
  • Loading branch information
azorg committed Jun 15, 2012
1 parent f175c3b commit 03c31d1
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 45 deletions.
2 changes: 1 addition & 1 deletion applications/gqrx/receiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ receiver::receiver(const std::string input_device, const std::string audio_devic
sniffer = make_sniffer_f();
/* sniffer_rr is created at each activation. */

set_demod(RX_DEMOD_FMS);
set_demod(RX_DEMOD_FMN);
}


Expand Down
83 changes: 83 additions & 0 deletions dsp/lpf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* -*- c++ -*- */
/*
* Copyright 2011 Alexandru Csete OZ9AEC.
*
* Gqrx is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* Gqrx is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gqrx; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include <cmath>
#include <gr_io_signature.h>
#include <gr_firdes.h>
#include "dsp/lpf.h"

static const int MIN_IN = 1; /* Mininum number of input streams. */
static const int MAX_IN = 1; /* Maximum number of input streams. */
static const int MIN_OUT = 1; /* Minimum number of output streams. */
static const int MAX_OUT = 1; /* Maximum number of output streams. */


/*
* Create a new instance of lpf and return
* a boost shared_ptr. This is effectively the public constructor.
*/
lpf_ff_sptr make_lpf_ff(double sample_rate, double cutoff_freq,
double trans_width, double gain)
{
return gnuradio::get_initial_sptr(new lpf_ff(sample_rate, cutoff_freq,
trans_width, gain));
}


lpf_ff::lpf_ff(double sample_rate, double cutoff_freq,
double trans_width, double gain)
: gr_hier_block2("lpf_ff",
gr_make_io_signature(MIN_IN, MAX_IN, sizeof (float)),
gr_make_io_signature(MIN_OUT, MAX_OUT, sizeof (float))),
d_sample_rate(sample_rate),
d_cutoff_freq(cutoff_freq),
d_trans_width(trans_width),
d_gain(gain)
{
/* generate taps */
d_taps = gr_firdes::low_pass(d_gain, d_sample_rate,
d_cutoff_freq, d_trans_width);

/* create low-pass filter (decimation=1) */
lpf = gr_make_fir_filter_fff(1, d_taps);

/* connect filter */
connect(self(), 0, lpf, 0);
connect(lpf, 0, self(), 0);
}


lpf_ff::~lpf_ff()
{

}


void lpf_ff::set_param(double cutoff_freq, double trans_width)
{
d_cutoff_freq = cutoff_freq;
d_trans_width = trans_width;

/* generate new taps */
d_taps = gr_firdes::low_pass(d_gain, d_sample_rate,
d_cutoff_freq, d_trans_width);

lpf->set_taps(d_taps);
}

86 changes: 86 additions & 0 deletions dsp/lpf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* -*- c++ -*- */
/*
* Copyright 2011 Alexandru Csete OZ9AEC.
*
* Gqrx is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* Gqrx is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gqrx; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef LPF_H
#define LPF_H

#include <gr_hier_block2.h>
#include <gr_firdes.h>
#include <gr_fir_filter_fff.h>


class lpf_ff;

typedef boost::shared_ptr<lpf_ff> lpf_ff_sptr;


/*! \brief Return a shared_ptr to a new instance of lpf.
* \param sample_rate The sample rate (Hz).
* \param cutoff_freq Center of transition band (Hz).
* \param transition_width Width of transition band (Hz).
* \param .
* \param gain Overall gain of filter (typically 1.0).
*
* This is effectively the public constructor. To avoid accidental use
* of raw pointers, lpf's constructor is private.
* make_lpf is the public interface for creating new instances.
*/
lpf_ff_sptr make_lpf_ff(double sample_rate=48000.,
double cutoff_freq=5000.,
double trans_width=1000.,
double gain=1.0);

/*! \brief low-pass filter (LPF) with float taps.
* \ingroup DSP
*
* This class encapsulates a low-pass FIR filter and the code
* required to generate filter taps. It provides a simple
* interface to set the filter parameters.
*
* The user of this class is expected to provide valid parameters and no checks are
* performed by the accessors (though the taps generator from gr_firdes does perform
* some sanity checks and throws std::out_of_range in case of bad parameter).
*/
class lpf_ff : public gr_hier_block2
{
friend lpf_ff_sptr make_lpf_ff(double sample_rate, double cutoff_freq,
double trans_width, double gain);
protected:
lpf_ff(double sample_rate, double cutoff_freq,
double trans_width, double gain);

public:
~lpf_ff();

void set_param(double cutoff_freq, double trans_width);

private:
/* GR blocks */
gr_fir_filter_fff_sptr lpf;

/* other parameters */
std::vector<float> d_taps;
double d_sample_rate;
double d_cutoff_freq;
double d_trans_width;
double d_gain;
};


#endif // LPF_H
101 changes: 85 additions & 16 deletions dsp/stereo_demod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@
* Boston, MA 02110-1301, USA.
*/
#include <gr_io_signature.h>
#include <gr_firdes.h>
#include <math.h>
#include <iostream>
#include <dsp/stereo_demod.h>


/* Create a new instance of stereo_demod and return a boost shared_ptr. */
stereo_demod_sptr make_stereo_demod(float quad_rate, float audio_rate)
stereo_demod_sptr make_stereo_demod(float quad_rate, float audio_rate,
bool stereo)
{
return gnuradio::get_initial_sptr(new stereo_demod(quad_rate, audio_rate));
return gnuradio::get_initial_sptr(new stereo_demod(quad_rate,
audio_rate, stereo));
}


Expand All @@ -37,34 +38,102 @@ static const int MAX_IN = 1; /* Maximum number of input streams. */
static const int MIN_OUT = 2; /* Minimum number of output streams. */
static const int MAX_OUT = 2; /* Maximum number of output streams. */

#define STEREO_DEMOD_PARANOIC

/*! \brief Create stereo demodulator object.
*
* Use make_stereo_demod() instead.
*/
stereo_demod::stereo_demod(float input_rate, float audio_rate)
stereo_demod::stereo_demod(float input_rate, float audio_rate, bool stereo)
: gr_hier_block2("stereo_demod",
gr_make_io_signature (MIN_IN, MAX_IN, sizeof (float)),
gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (float))),
d_input_rate(input_rate),
d_audio_rate(audio_rate)
d_audio_rate(audio_rate),
d_stereo(stereo)
{
//float gain;
lpf0 = make_lpf_ff(d_input_rate, 17e3, 2e3); // FIXME
audio_rr0 = make_resampler_ff(d_audio_rate/d_input_rate);

/* demodulator gain */
//gain = 1.;
//std::cout << "stereo_demod gain: " << gain << std::endl;

// FIXME: cause we have no audio filter...
std::cout << __FUNCTION__ << ": FIXME: audio filter" << std::endl;
audio_rr0 = make_resampler_ff(d_audio_rate/d_input_rate);
if (d_stereo)
{
lpf1 = make_lpf_ff(d_input_rate, 17e3, 2e3); // FIXME
audio_rr1 = make_resampler_ff(d_audio_rate/d_input_rate);

d_tone_taps = gr_firdes::complex_band_pass(
1.0, // gain,
d_input_rate, // sampling_freq
18800., // low_cutoff_freq
19200., // high_cutoff_freq
300.); // transition_width
tone = gr_make_fir_filter_fcc(1, d_tone_taps);

pll = gr_make_pll_refout_cc(0.001, // loop_bw FIXME
2*M_PI * 19200 / input_rate, // max_freq
2*M_PI * 18800 / input_rate); // min_freq

subtone = gr_make_multiply_cc();

lo = gr_make_complex_to_imag();

#ifdef STEREO_DEMOD_PARANOIC
d_pll_taps = gr_firdes::band_pass(
1.0, // gain,
d_input_rate, // sampling_freq
37600., // low_cutoff_freq
38400., // high_cutoff_freq
400.); // transition_width
lo2 = gr_make_fir_filter_fff(1, d_pll_taps);
#endif

mixer = gr_make_multiply_ff();

cdp = gr_make_multiply_const_ff( 2.); // FIXME
cdm = gr_make_multiply_const_ff(-2.); // FIXME

add0 = gr_make_add_ff();
add1 = gr_make_add_ff();

/* connect block */
connect(self(), 0, tone, 0);
connect(tone, 0, pll, 0);
connect(pll, 0, subtone, 0);
connect(pll, 0, subtone, 1);
connect(subtone, 0, lo, 0);

#ifdef STEREO_DEMOD_PARANOIC
connect(lo, 0, lo2, 0);
connect(lo2, 0, mixer, 0);
#else
connect(lo, 0, mixer, 0);
#endif
connect(self(), 0, mixer, 1);

connect(self(), 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(audio_rr0, 0, add0, 0);
connect(cdp, 0, add0, 1);
connect(add0, 0, self(), 0); // left = sum + delta

connect(audio_rr0, 0, add1, 0);
connect(cdm, 0, add1, 1);
connect(add1, 0, self(), 1); // right = sum + delta
}
else // if (!d_stereo)
{
/* connect block */
connect(self(), 0, audio_rr0, 0);
connect(self(), 0, audio_rr1, 0);
connect(self(), 0, lpf0, 0);
connect(lpf0, 0, audio_rr0, 0);
connect(audio_rr0, 0, self(), 0);
connect(audio_rr1, 0, self(), 1);
connect(audio_rr0, 0, self(), 1);
}
}


Expand Down
44 changes: 37 additions & 7 deletions dsp/stereo_demod.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@
#define STEREO_DEMOD_H

#include <gr_hier_block2.h>
#include <gr_firdes.h>
#include <gr_fir_filter_fcc.h>
#include <gr_fir_filter_fff.h>
#include <gr_pll_refout_cc.h>
#include <gr_multiply_cc.h>
#include <gr_multiply_ff.h>
#include <gr_multiply_const_ff.h>
#include <gr_complex_to_xxx.h>
#include <gr_add_ff.h>
#include <vector>
#include "dsp/lpf.h"
#include "dsp/resampler_xx.h"


Expand All @@ -34,12 +44,15 @@ typedef boost::shared_ptr<stereo_demod> stereo_demod_sptr;
/*! \brief Return a shared_ptr to a new instance of stere_demod.
* \param quad_rate The input sample rate.
* \param audio_rate The audio rate.
* \param stereo On/off stereo mode.
*
* This is effectively the public constructor. To avoid accidental use
* of raw pointers, stereo_demod's constructor is private.
* make_stereo_demod is the public interface for creating new instances.
*/
stereo_demod_sptr make_stereo_demod(float quad_rate, float audio_rate);
stereo_demod_sptr make_stereo_demod(float quad_rate=120e3,
float audio_rate=48e3,
bool stereo=true);


/*! \brief FM stereo demodulator.
Expand All @@ -50,22 +63,39 @@ stereo_demod_sptr make_stereo_demod(float quad_rate, float audio_rate);
*/
class stereo_demod : public gr_hier_block2
{
friend stereo_demod_sptr make_stereo_demod(float input_rate, float audio_rate);
friend stereo_demod_sptr make_stereo_demod(float input_rate,
float audio_rate,
bool stereo);

protected:
stereo_demod(float input_rate=120e3, float audio_rate=48e3);
stereo_demod(float input_rate, float audio_rate, bool stereo);

public:
~stereo_demod();

private:
/* GR blocks */
resampler_ff_sptr audio_rr0; /*!< Audio resampler #0. */
resampler_ff_sptr audio_rr1; /*!< Audio resampler #1. */
gr_fir_filter_fcc_sptr tone; /*!< Pilot tone BPF. */
gr_pll_refout_cc_sptr pll; /*!< Pilot tone PLL. */
gr_multiply_cc_sptr subtone; /*!< Stereo subtone. */
gr_complex_to_imag_sptr lo; /*!< Complex tone imag. */
gr_fir_filter_fff_sptr lo2; /*!< Subtone BPF. */
gr_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. */
gr_multiply_const_ff_sptr cdp; /*!< Channel delta (plus). */
gr_multiply_const_ff_sptr cdm; /*!< Channel delta (minus). */
gr_add_ff_sptr add0; /*!< Left stereo channel. */
gr_add_ff_sptr add1; /*!< Right stereo channel. */

/* other parameters */
float d_input_rate; /*! Input rate. */
float d_audio_rate; /*! Audio rate. */
bool d_stereo; /*! On/off stereo mode. */
float d_input_rate; /*! Input rate. */
float d_audio_rate; /*! Audio rate. */
std::vector<gr_complex> d_tone_taps; /*! Tone BPF taps. */
std::vector<float> d_pll_taps; /*! Subtone BPF taps. */
};


Expand Down

0 comments on commit 03c31d1

Please sign in to comment.