Skip to content

Commit

Permalink
Apply de-emphasis after stereo demodulation
Browse files Browse the repository at this point in the history
  • Loading branch information
argilo authored and csete committed Apr 18, 2020
1 parent ca0a615 commit 2910cb6
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 108 deletions.
2 changes: 2 additions & 0 deletions gqrx.pro
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ SOURCES += \
src/dsp/agc_impl.cpp \
src/dsp/correct_iq_cc.cpp \
src/dsp/filter/fir_decim.cpp \
src/dsp/fm_deemph.cpp \
src/dsp/lpf.cpp \
src/dsp/rds/decoder_impl.cc \
src/dsp/rds/parser_impl.cc \
Expand Down Expand Up @@ -156,6 +157,7 @@ HEADERS += \
src/dsp/correct_iq_cc.h \
src/dsp/filter/fir_decim.h \
src/dsp/filter/fir_decim_coef.h \
src/dsp/fm_deemph.h \
src/dsp/lpf.h \
src/dsp/rds/api.h \
src/dsp/rds/parser.h \
Expand Down
2 changes: 1 addition & 1 deletion resources/news.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
NEW: Stereo option for UDP streaming.
NEW: Script to generate AppImage.
FIXED: FM de-emphasis causing audio to be 20 dB quieter than it should be.
FIXED: FM de-emphasis applied incorrectly in WFM stereo receiver.
FIXED: Update waterfall time resolution when FFT settings are changed.
FIXED: Update waterfall time resolution when window is resized.
FIXED: Restore waterfall time span between sessions.
Expand Down Expand Up @@ -344,4 +345,3 @@
1.x

Initial experiments using Python and gr-qtgui.

2 changes: 2 additions & 0 deletions src/dsp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ add_source_files(SRCS_LIST
agc_impl.h
correct_iq_cc.cpp
correct_iq_cc.h
fm_deemph.cpp
fm_deemph.h
lpf.cpp
lpf.h
resampler_xx.cpp
Expand Down
104 changes: 104 additions & 0 deletions src/dsp/fm_deemph.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* -*- c++ -*- */
/*
* Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
* http://gqrx.dk/
*
* Copyright 2020 Clayton Smith VE3IRR.
*
* 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 <gnuradio/filter/firdes.h>
#include <gnuradio/io_signature.h>
#include <iostream>
#include <math.h>
#include "dsp/fm_deemph.h"


/* Create a new instance of fm_deemph and return a boost shared_ptr. */
fm_deemph_sptr make_fm_deemph(float quad_rate, double tau)
{
return gnuradio::get_initial_sptr(new fm_deemph(quad_rate, tau));
}

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. */

fm_deemph::fm_deemph(float quad_rate, double tau)
: gr::hier_block2 ("fm_deemph",
gr::io_signature::make (MIN_IN, MAX_IN, sizeof (float)),
gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (float))),
d_quad_rate(quad_rate)
{
/* de-emphasis */
d_fftaps.resize(2);
d_fbtaps.resize(2);
calculate_iir_taps(tau);
d_deemph = gr::filter::iir_filter_ffd::make(d_fftaps, d_fbtaps, false);

connect(self(), 0, d_deemph, 0);
connect(d_deemph, 0, self(), 0);
}

fm_deemph::~fm_deemph ()
{
}

/*! \brief Set FM de-emphasis time constant.
* \param tau The new time costant.
*/
void fm_deemph::set_tau(double tau)
{
calculate_iir_taps(tau);
d_deemph->set_taps(d_fftaps, d_fbtaps);
}

/*! \brief Calculate taps for FM de-emph IIR filter. */
void fm_deemph::calculate_iir_taps(double tau)
{
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;
}
}
71 changes: 71 additions & 0 deletions src/dsp/fm_deemph.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* -*- c++ -*- */
/*
* Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
* http://gqrx.dk/
*
* Copyright 2020 Clayton Smith VE3IRR.
*
* 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.
*/
#pragma once

#include <gnuradio/filter/iir_filter_ffd.h>
#include <gnuradio/hier_block2.h>
#include <vector>

class fm_deemph;
typedef boost::shared_ptr<fm_deemph> fm_deemph_sptr;

/*! \brief Return a shared_ptr to a new instance of fm_deemph.
* \param quad_rate The input sample rate.
* \param tau De-emphasis time constant in seconds (75us in US, 50us in EUR, 0.0 disables).
*
* This is effectively the public constructor. To avoid accidental use
* of raw pointers, fm_deemph's constructor is private.
* make_rx_dmod_fm is the public interface for creating new instances.
*/
fm_deemph_sptr make_fm_deemph(float quad_rate, double tau=50.0e-6);

/*! \brief FM demodulator.
* \ingroup DSP
*
* This class implements the FM demodulator using the gr_quadrature_demod block.
* It also provides de-emphasis with variable time constant (use 0.0 to disable).
*
*/
class fm_deemph : public gr::hier_block2
{

public:
fm_deemph(float quad_rate, double tau); // FIXME: should be private
~fm_deemph();

void set_tau(double tau);

private:
/* GR blocks */
gr::filter::iir_filter_ffd::sptr d_deemph; /*! De-emphasis IIR filter. */

/* other parameters */
float d_quad_rate; /*! Quadrature rate. */

/* De-emph IIR filter taps */
std::vector<double> d_fftaps; /*! Feed forward taps. */
std::vector<double> d_fbtaps; /*! Feed back taps. */

void calculate_iir_taps(double tau);

};
93 changes: 5 additions & 88 deletions src/dsp/rx_demod_fm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ rx_demod_fm::rx_demod_fm(float quad_rate, float max_dev, double tau)
gr::io_signature::make (MIN_IN, MAX_IN, sizeof (gr_complex)),
gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (float))),
d_quad_rate(quad_rate),
d_max_dev(max_dev),
d_tau(tau)
d_max_dev(max_dev)
{
float gain;

Expand All @@ -60,22 +59,12 @@ rx_demod_fm::rx_demod_fm(float quad_rate, float max_dev, double tau)
d_quad = gr::analog::quadrature_demod_cf::make(gain);

/* de-emphasis */
d_fftaps.resize(2);
d_fbtaps.resize(2);
calculate_iir_taps(d_tau);
d_deemph = gr::filter::iir_filter_ffd::make(d_fftaps, d_fbtaps, false);
d_deemph = make_fm_deemph(d_quad_rate, tau);

/* connect block */
connect(self(), 0, d_quad, 0);
if (d_tau > 1.0e-9)
{
connect(d_quad, 0, d_deemph, 0);
connect(d_deemph, 0, self(), 0);
}
else
{
connect(d_quad, 0, self(), 0);
}
connect(d_quad, 0, d_deemph, 0);
connect(d_deemph, 0, self(), 0);

}

Expand Down Expand Up @@ -108,80 +97,8 @@ void rx_demod_fm::set_max_dev(float max_dev)

/*! \brief Set FM de-emphasis time constant.
* \param tau The new time costant.
*
* \bug Assumes that IIR filter has already been constructed so that we
* can use the set_taps() method.
*/
void rx_demod_fm::set_tau(double tau)
{
if (fabs(tau - d_tau) < 1.0e-9)
{
/* no change */
return;
}

if (tau > 1.0e-9)
{
calculate_iir_taps(tau);
d_deemph->set_taps(d_fftaps, d_fbtaps);

/* check to see if we need to rewire flow graph */
if (d_tau <= 1.0e-9)
{
/* need to put deemph into the flowgraph */
lock();
disconnect(d_quad, 0, self(), 0);
connect(d_quad, 0, d_deemph, 0);
connect(d_deemph, 0, self(), 0);
unlock();
}

d_tau = tau;
}
else
{
#ifndef QT_NO_DEBUG_OUTPUT
std::cerr << "FM de-emphasis tau is 0: " << tau << std::endl;
#endif
/* diable de-emph if conencted */
if (d_tau > 1.0e-9)
{
#ifndef QT_NO_DEBUG_OUTPUT
std::cout << " Disable de-emphasis" << std::endl;
#endif
lock();
disconnect(d_quad, 0, d_deemph, 0);
disconnect(d_deemph, 0, self(), 0);
connect(d_quad, 0, self(), 0);
unlock();
}

d_tau = 0.0;
}
}

/*! \brief Calculate taps for FM de-emph IIR filter. */
void rx_demod_fm::calculate_iir_taps(double tau)
{
// 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;
d_deemph->set_tau(tau);
}
12 changes: 2 additions & 10 deletions src/dsp/rx_demod_fm.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
#pragma once

#include <gnuradio/analog/quadrature_demod_cf.h>
#include <gnuradio/filter/iir_filter_ffd.h>
#include <gnuradio/hier_block2.h>
#include <vector>
#include "dsp/fm_deemph.h"

class rx_demod_fm;
typedef boost::shared_ptr<rx_demod_fm> rx_demod_fm_sptr;
Expand Down Expand Up @@ -61,18 +61,10 @@ class rx_demod_fm : public gr::hier_block2
private:
/* GR blocks */
gr::analog::quadrature_demod_cf::sptr d_quad; /*! The quadrature demodulator block. */
gr::filter::iir_filter_ffd::sptr d_deemph; /*! De-emphasis IIR filter. */
fm_deemph_sptr d_deemph; /*! De-emphasis IIR filter. */
std::vector<float> d_taps; /*! Taps for the PFB resampler. */

/* other parameters */
float d_quad_rate; /*! Quadrature rate. */
float d_max_dev; /*! Max deviation. */
double d_tau; /*! De-emphasis time constant. */

/* De-emph IIR filter taps */
std::vector<double> d_fftaps; /*! Feed forward taps. */
std::vector<double> d_fbtaps; /*! Feed back taps. */

void calculate_iir_taps(double tau);

};
Loading

0 comments on commit 2910cb6

Please sign in to comment.