Issue #27: Implement new (and awesome) AGC block.

This commit is contained in:
Alexandru Csete 2011-09-24 18:06:43 +02:00
parent 64ac00da8c
commit 74ba2742c8
7 changed files with 806 additions and 8 deletions

413
dsp/agc_impl.cpp Normal file
View File

@ -0,0 +1,413 @@
//////////////////////////////////////////////////////////////////////
// agc_impl.cpp: implementation of the CAgc class.
//
// This class implements an automatic gain function.
//
// History:
// 2010-09-15 Initial creation MSW
// 2011-03-27 Initial release
// 2011-09-24 Adapted for gqrx
//////////////////////////////////////////////////////////////////////
//==========================================================================================
// + + + This Software is released under the "Simplified BSD License" + + +
//Copyright 2010 Moe Wheatley. All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are
//permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
//THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED
//WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//The views and conclusions contained in the software and documentation are those of the
//authors and should not be interpreted as representing official policies, either expressed
//or implied, of Moe Wheatley.
//==========================================================================================
#include <dsp/agc_impl.h>
#include <math.h>
//////////////////////////////////////////////////////////////////////
// Local Defines
//////////////////////////////////////////////////////////////////////
//signal delay line time delay in seconds.
//adjust to cover the impulse response time of filter
#define DELAY_TIMECONST .015
//Peak Detector window time delay in seconds.
#define WINDOW_TIMECONST .018
//attack time constant in seconds
//just small enough to let attackave charge up within the DELAY_TIMECONST time
#define ATTACK_RISE_TIMECONST .002
#define ATTACK_FALL_TIMECONST .005
#define DECAY_RISEFALL_RATIO .3 //ratio between rise and fall times of Decay time constants
//adjust for best action with SSB
// hang timer release decay time constant in seconds
#define RELEASE_TIMECONST .05
//limit output to about 3db of max
#define AGC_OUTSCALE 0.7
// keep max in and out the same
#define MAX_AMPLITUDE 1.0 //32767.0
#define MAX_MANUAL_AMPLITUDE 1.0 //32767.0
#define MIN_CONSTANT 3.2767e-4 // const for calc log() so that a value of 0 magnitude == -8
//corresponding to -160dB.
//K = 10^( -8 + log(32767) )
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CAgc::CAgc()
{
m_AgcOn = true;
m_UseHang = false;
m_Threshold = 0;
m_ManualGain = 0;
m_SlopeFactor = 0;
m_Decay = 0;
m_SampleRate = 100.0;
}
CAgc::~CAgc()
{
}
////////////////////////////////////////////////////////////////////////////////
// Sets and calculates various AGC parameters
// "On" switches between AGC on and off.
// "Threshold" specifies AGC Knee in dB if AGC is active.( nominal range -160 to 0dB)
// "ManualGain" specifies AGC manual gain in dB if AGC is not active.(nominal range 0 to 100dB)
// "SlopeFactor" specifies dB reduction in output at knee from maximum output level(nominal range 0 to 10dB)
// "Decay" is AGC decay value in milliseconds ( nominal range 20 to 5000 milliSeconds)
// "SampleRate" is current sample rate of AGC data
////////////////////////////////////////////////////////////////////////////////
void CAgc::SetParameters(bool AgcOn, bool UseHang, int Threshold, int ManualGain,
int SlopeFactor, int Decay, double SampleRate)
{
if( (AgcOn == m_AgcOn) && (UseHang == m_UseHang) &&
(Threshold == m_Threshold) && (ManualGain == m_ManualGain) &&
(SlopeFactor == m_SlopeFactor) && (Decay == m_Decay) &&
(SampleRate == m_SampleRate) )
{
return; //just return if no parameter changed
}
//m_Mutex.lock();
m_AgcOn = AgcOn;
m_UseHang = UseHang;
m_Threshold = Threshold;
m_ManualGain = ManualGain;
m_SlopeFactor = SlopeFactor;
m_Decay = Decay;
if (m_SampleRate != SampleRate)
{ //clear out delay buffer and init some things if sample rate changes
m_SampleRate = SampleRate;
for (int i=0; i<MAX_DELAY_BUF; i++)
{
m_SigDelayBuf[i].re = 0.0;
m_SigDelayBuf[i].im = 0.0;
m_MagBuf[i] = -16.0;
}
m_SigDelayPtr = 0;
m_HangTimer = 0;
m_Peak = -16.0;
m_DecayAve = -5.0;
m_AttackAve = -5.0;
m_MagBufPos = 0;
}
//convert m_ThreshGain to linear manual gain value
m_ManualAgcGain = MAX_MANUAL_AMPLITUDE*pow(10.0, -(100-(double)m_ManualGain)/20.0);
//calculate parameters for AGC gain as a function of input magnitude
m_Knee = (double)m_Threshold/20.0;
m_GainSlope = m_SlopeFactor/(100.0);
m_FixedGain = AGC_OUTSCALE * pow(10.0, m_Knee*(m_GainSlope - 1.0) ); //fixed gain value used below knee threshold
//qDebug()<<"m_Knee = "<<m_Knee<<" m_GainSlope = "<<m_GainSlope<< "m_FixedGain = "<<m_FixedGain;
//calculate fast and slow filter values.
m_AttackRiseAlpha = (1.0-exp(-1.0/(m_SampleRate*ATTACK_RISE_TIMECONST)) );
m_AttackFallAlpha = (1.0-exp(-1.0/(m_SampleRate*ATTACK_FALL_TIMECONST)) );
m_DecayRiseAlpha = (1.0-exp(-1.0/(m_SampleRate * (double)m_Decay*.001*DECAY_RISEFALL_RATIO)) ); //make rise time DECAY_RISEFALL_RATIO of fall
m_HangTime = (int)(m_SampleRate * (double)m_Decay * .001);
if(m_UseHang)
m_DecayFallAlpha = (1.0-exp(-1.0/(m_SampleRate * RELEASE_TIMECONST)) );
else
m_DecayFallAlpha = (1.0-exp(-1.0/(m_SampleRate * (double)m_Decay *.001)) );
m_DelaySamples = (int)(m_SampleRate*DELAY_TIMECONST);
m_WindowSamples = (int)(m_SampleRate*WINDOW_TIMECONST);
//clamp Delay samples within buffer limit
if(m_DelaySamples >= MAX_DELAY_BUF-1)
m_DelaySamples = MAX_DELAY_BUF-1;
//m_Mutex.unlock();
}
//////////////////////////////////////////////////////////////////////
// Automatic Gain Control calculator for COMPLEX data
//////////////////////////////////////////////////////////////////////
void CAgc::ProcessData(int Length, TYPECPX* pInData, TYPECPX* pOutData)
{
double gain;
double mag;
TYPECPX delayedin;
//m_Mutex.lock();
if (m_AgcOn)
{
for (int i=0; i<Length; i++)
{
TYPECPX in = pInData[i]; //get latest input sample
//Get delayed sample of input signal
delayedin = m_SigDelayBuf[m_SigDelayPtr];
//put new input sample into signal delay buffer
m_SigDelayBuf[m_SigDelayPtr++] = in;
if( m_SigDelayPtr >= m_DelaySamples) //deal with delay buffer wrap around
m_SigDelayPtr = 0;
//TYPEREAL dmag = 0.5* log10( (dsig.re*dsig.re+dsig.im*dsig.im)/(MAX_AMPLITUDE*MAX_AMPLITUDE) + 1e-16); //clamped to -160dBfs
//pOutData[i].re = 3000*dmag;
#if 1
mag = fabs(in.re);
double mim = fabs(in.im);
if (mim>mag)
mag = mim;
mag = log10( mag + MIN_CONSTANT ) - log10(MAX_AMPLITUDE); //0==max -8 is min==-160dB
#else
mag = in.re*in.re+in.im*in.im;
//mag is power so 0.5 factor takes square root of power
mag = 0.5* log10( mag/(MAX_AMPLITUDE*MAX_AMPLITUDE) + 1e-16); //clamped to -160dBfs
#endif
//pOutData[i].re = 3000*mag;
//pOutData[i].re = 1500*log10( ((delayedin.re*delayedin.re)+(delayedin.im*delayedin.im))/(MAX_AMPLITUDE*MAX_AMPLITUDE) + 1e-16);;
//create a sliding window of 'm_WindowSamples' magnitudes and output the peak value within the sliding window
double tmp = m_MagBuf[m_MagBufPos]; // get oldest mag sample from buffer into tmp
m_MagBuf[m_MagBufPos++] = mag; // put latest mag sample in buffer;
if (m_MagBufPos >= m_WindowSamples) // deal with magnitude buffer wrap around
m_MagBufPos = 0;
if (mag > m_Peak)
{
m_Peak = mag; //if new sample is larger than current peak then use it, no need to look at buffer values
}
else
{
if (tmp == m_Peak) //tmp is oldest sample pulled out of buffer
{ //if oldest sample pulled out was last peak then need to find next highest peak in buffer
m_Peak = -8.0; //set to lowest value to find next max peak
//search all buffer for maximum value and set as new peak
for (int i=0; i<m_WindowSamples; i++)
{
tmp = m_MagBuf[i];
if (tmp > m_Peak)
m_Peak = tmp;
}
}
}
//pOutData[i].im = 3000*m_Peak;
if (m_UseHang)
{ //using hang timer mode
if (m_Peak>m_AttackAve) //if power is rising (use m_AttackRiseAlpha time constant)
m_AttackAve = (1.0-m_AttackRiseAlpha)*m_AttackAve + m_AttackRiseAlpha*m_Peak;
else //else magnitude is falling (use m_AttackFallAlpha time constant)
m_AttackAve = (1.0-m_AttackFallAlpha)*m_AttackAve + m_AttackFallAlpha*m_Peak;
if (m_Peak>m_DecayAve) //if magnitude is rising (use m_DecayRiseAlpha time constant)
{
m_DecayAve = (1.0-m_DecayRiseAlpha)*m_DecayAve + m_DecayRiseAlpha*m_Peak;
m_HangTimer = 0; //reset hang timer
}
else
{ //here if decreasing signal
if (m_HangTimer<m_HangTime)
m_HangTimer++; //just inc and hold current m_DecayAve
else //else decay with m_DecayFallAlpha which is RELEASE_TIMECONST
m_DecayAve = (1.0-m_DecayFallAlpha)*m_DecayAve + m_DecayFallAlpha*m_Peak;
}
}
else
{ //using exponential decay mode
// perform average of magnitude using 2 averagers each with separate rise and fall time constants
if (m_Peak>m_AttackAve) //if magnitude is rising (use m_AttackRiseAlpha time constant)
m_AttackAve = (1.0-m_AttackRiseAlpha)*m_AttackAve + m_AttackRiseAlpha*m_Peak;
else //else magnitude is falling (use m_AttackFallAlpha time constant)
m_AttackAve = (1.0-m_AttackFallAlpha)*m_AttackAve + m_AttackFallAlpha*m_Peak;
//pOutData[i].im = 3000*m_AttackAve;
if (m_Peak>m_DecayAve) //if magnitude is rising (use m_DecayRiseAlpha time constant)
m_DecayAve = (1.0-m_DecayRiseAlpha)*m_DecayAve + m_DecayRiseAlpha*(m_Peak);
else //else magnitude is falling (use m_DecayFallAlpha time constant)
m_DecayAve = (1.0-m_DecayFallAlpha)*m_DecayAve + m_DecayFallAlpha*(m_Peak);
//pOutData[i].im = 3000*m_DecayAve;
}
//use greater magnitude of attack or Decay Averager
if (m_AttackAve>m_DecayAve)
mag = m_AttackAve;
else
mag = m_DecayAve;
//pOutData[i].im = 3000*mag;
//calc gain depending on which side of knee the magnitude is on
if (mag<=m_Knee) //use fixed gain if below knee
gain = m_FixedGain;
else //use variable gain if above knee
gain = AGC_OUTSCALE * pow(10.0, mag*(m_GainSlope - 1.0) );
//pOutData[i].re = .5*gain;
pOutData[i].re = delayedin.re * gain;
pOutData[i].im = delayedin.im * gain;
}
}
else
{ //manual gain just multiply by m_ManualGain
for (int i=0; i<Length; i++)
{
pOutData[i].re = m_ManualAgcGain * pInData[i].re;
pOutData[i].im = m_ManualAgcGain * pInData[i].im;
}
}
//m_Mutex.unlock();
}
//////////////////////////////////////////////////////////////////////
// Automatic Gain Control calculator for REAL data
//////////////////////////////////////////////////////////////////////
void CAgc::ProcessData(int Length, double* pInData, double* pOutData)
{
double gain;
double mag;
double delayedin;
//m_Mutex.lock();
if (m_AgcOn)
{
for (int i=0; i<Length; i++)
{
double in = pInData[i]; //get latest input sample
//Get delayed sample of input signal
delayedin = m_SigDelayBuf[m_SigDelayPtr].re;
//put new input sample into signal delay buffer
m_SigDelayBuf[m_SigDelayPtr++].re = in;
if (m_SigDelayPtr >= m_DelaySamples) //deal with delay buffer wrap around
m_SigDelayPtr = 0;
//convert |mag| to log |mag|
mag = log10( fabs(in) + MIN_CONSTANT ) - log10(MAX_AMPLITUDE); //0==max -8 is min==-160dB
//create a sliding window of 'm_WindowSamples' magnitudes and output the peak value within the sliding window
double tmp = m_MagBuf[m_MagBufPos]; // get oldest mag sample from buffer into tmp
m_MagBuf[m_MagBufPos++] = mag; // put latest mag sample in buffer;
if (m_MagBufPos >= m_WindowSamples) // deal with magnitude buffer wrap around
m_MagBufPos = 0;
if (mag > m_Peak)
{
m_Peak = mag; // if new sample is larger than current peak then use it, no need to look at buffer values
}
else
{
if (tmp == m_Peak) // tmp is oldest sample pulled out of buffer
{ // if oldest sample pulled out was last peak then need to find next highest peak in buffer
m_Peak = -8.0; // set to lowest value to find next max peak
// search all buffer for maximum value and set as new peak
for (int i=0; i<m_WindowSamples; i++)
{
tmp = m_MagBuf[i];
if (tmp > m_Peak)
m_Peak = tmp;
}
}
}
if (m_UseHang)
{ //using hang timer mode
if (m_Peak>m_AttackAve)
// if magnitude is rising (use m_AttackRiseAlpha time constant)
m_AttackAve = (1.0-m_AttackRiseAlpha)*m_AttackAve + m_AttackRiseAlpha*m_Peak;
else
// else magnitude is falling (use m_AttackFallAlpha time constant)
m_AttackAve = (1.0-m_AttackFallAlpha)*m_AttackAve + m_AttackFallAlpha*m_Peak;
if (m_Peak>m_DecayAve)
{
// if magnitude is rising (use m_DecayRiseAlpha time constant)
m_DecayAve = (1.0-m_DecayRiseAlpha)*m_DecayAve + m_DecayRiseAlpha*m_Peak;
m_HangTimer = 0; // reset hang timer
}
else
{ // here if decreasing signal
if (m_HangTimer<m_HangTime)
m_HangTimer++; // just inc and hold current m_DecayAve
else
// else decay with m_DecayFallAlpha which is RELEASE_TIMECONST
m_DecayAve = (1.0-m_DecayFallAlpha)*m_DecayAve + m_DecayFallAlpha*m_Peak;
}
}
else
{ // using exponential decay mode
// perform average of magnitude using 2 averagers each with separate rise and fall time constants
if (m_Peak>m_AttackAve)
// if magnitude is rising (use m_AttackRiseAlpha time constant)
m_AttackAve = (1.0-m_AttackRiseAlpha)*m_AttackAve + m_AttackRiseAlpha*m_Peak;
else
// else magnitude is falling (use m_AttackFallAlpha time constant)
m_AttackAve = (1.0-m_AttackFallAlpha)*m_AttackAve + m_AttackFallAlpha*m_Peak;
if (m_Peak>m_DecayAve)
// if magnitude is rising (use m_DecayRiseAlpha time constant)
m_DecayAve = (1.0-m_DecayRiseAlpha)*m_DecayAve + m_DecayRiseAlpha*(m_Peak);
else
// else magnitude is falling (use m_DecayFallAlpha time constant)
m_DecayAve = (1.0-m_DecayFallAlpha)*m_DecayAve + m_DecayFallAlpha*(m_Peak);
}
// use greater magnitude of attack or Decay Averager
if (m_AttackAve>m_DecayAve)
mag = m_AttackAve;
else
mag = m_DecayAve;
// calc gain depending on which side of knee the magnitude is on
if (mag <= m_Knee)
// use fixed gain if below knee
gain = m_FixedGain;
else
// use variable gain if above knee
gain = AGC_OUTSCALE * pow(10.0, mag*(m_GainSlope - 1.0) );
pOutData[i] = delayedin * gain;
}
}
else
{ // manual gain just multiply by m_ManualGain
for (int i=0; i<Length; i++)
pOutData[i] = m_ManualAgcGain * pInData[i];
}
//m_Mutex.unlock();
}

75
dsp/agc_impl.h Normal file
View File

@ -0,0 +1,75 @@
//////////////////////////////////////////////////////////////////////
// agc_impl.h: interface for the CAgc class.
//
// This class implements an automatic gain function.
//
// History:
// 2010-09-15 Initial creation MSW
// 2011-03-27 Initial release
// 2011-09-24 Adapted for gqrx
//////////////////////////////////////////////////////////////////////
#ifndef AGC_IMPL_H
#define AGC_IMPL_H
//#include "dsp/datatypes.h"
//#include <QMutex>
#define MAX_DELAY_BUF 2048
typedef struct _dCplx
{
double re;
double im;
} tDComplex;
#define TYPECPX tDComplex
class CAgc
{
public:
CAgc();
virtual ~CAgc();
void SetParameters(bool AgcOn, bool UseHang, int Threshold, int ManualGain, int Slope, int Decay, double SampleRate);
void ProcessData(int Length, TYPECPX* pInData, TYPECPX* pOutData);
void ProcessData(int Length, double* pInData, double* pOutData);
private:
bool m_AgcOn; //internal copy of AGC settings parameters
bool m_UseHang;
int m_Threshold;
int m_ManualGain;
int m_Slope;
int m_Decay;
double m_SampleRate;
double m_SlopeFactor;
double m_ManualAgcGain;
double m_DecayAve;
double m_AttackAve;
double m_AttackRiseAlpha;
double m_AttackFallAlpha;
double m_DecayRiseAlpha;
double m_DecayFallAlpha;
double m_FixedGain;
double m_Knee;
double m_GainSlope;
double m_Peak;
int m_SigDelayPtr;
int m_MagBufPos;
int m_DelaySize;
int m_DelaySamples;
int m_WindowSamples;
int m_HangTime;
int m_HangTimer;
//QMutex m_Mutex; //for keeping threads from stomping on each other
TYPECPX m_SigDelayBuf[MAX_DELAY_BUF];
double m_MagBuf[MAX_DELAY_BUF];
};
#endif // AGC_IMPL_H

199
dsp/rx_agc_xx.cc Normal file
View File

@ -0,0 +1,199 @@
/* -*- 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 <math.h>
#include <gr_io_signature.h>
#include <gr_complex.h>
#include <dsp/rx_agc_xx.h>
rx_agc_cc_sptr make_rx_agc_cc(double sample_rate, bool agc_on, int threshold,
int manual_gain, int slope, int decay, bool use_hang)
{
return gnuradio::get_initial_sptr(new rx_agc_cc(sample_rate, agc_on, threshold,
manual_gain, slope, decay,
use_hang));
}
/*! \brief Create receiver AGC object.
*
* Use make_rx_agc_cc() instead.
*/
rx_agc_cc::rx_agc_cc(double sample_rate, bool agc_on, int threshold,
int manual_gain, int slope, int decay, bool use_hang)
: gr_sync_block ("rx_agc_cc",
gr_make_io_signature(1, 1, sizeof(gr_complex)),
gr_make_io_signature(1, 1, sizeof(gr_complex))),
d_sample_rate(sample_rate),
d_agc_on(agc_on),
d_threshold(threshold),
d_manual_gain(manual_gain),
d_slope(slope),
d_decay(decay),
d_use_hang(use_hang)
{
d_agc = new CAgc();
d_agc->SetParameters(d_agc_on, d_use_hang, d_threshold, d_manual_gain,
d_slope, d_decay, d_sample_rate);
}
rx_agc_cc::~rx_agc_cc()
{
delete d_agc;
}
/*! \brief Receiver AGC work method.
* \param mooutput_items
* \param input_items
* \param output_items
*/
int rx_agc_cc::work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const gr_complex *in = (const gr_complex *) input_items[0];
gr_complex *out = (gr_complex *) output_items[0];
int i;
// lock mutex
boost::mutex::scoped_lock lock(d_mutex);
for (i = 0; i < noutput_items; i++) {
ib[i].im = in[i].imag();
ib[i].re = in[i].real();
}
d_agc->ProcessData(noutput_items, &ib[0], &ob[0]);
for (i = 0; i < noutput_items; i++) {
out[i].real() = ob[i].re;
out[i].imag() = ob[i].im;
}
return noutput_items;
}
/*! \brief Enable or disable AGC.
* \param agc_on Whether AGC should be endabled.
*
* When AGC is disabled a fixed gain is used.
*
* \sa set_manual_gain()
*/
void rx_agc_cc::set_agc_on(bool agc_on)
{
if (agc_on != d_agc_on) {
boost::mutex::scoped_lock lock(d_mutex);
d_agc_on = agc_on;
d_agc->SetParameters(d_agc_on, d_use_hang, d_threshold, d_manual_gain,
d_slope, d_decay, d_sample_rate);
}
}
/*! \brief Set AGC sample rate.
* \param sample_rate The sample rate.
*
* The AGC uses knowledge about the sample rate to calculate various delays and
* time constants.
*/
void rx_agc_cc::set_sample_rate(double sample_rate)
{
if (sample_rate != d_sample_rate) {
boost::mutex::scoped_lock lock(d_mutex);
d_sample_rate = sample_rate;
d_agc->SetParameters(d_agc_on, d_use_hang, d_threshold, d_manual_gain,
d_slope, d_decay, d_sample_rate);
}
}
/*! \brief Set new AGC threshold.
* \param threshold The new threshold between -160 and 0dB.
*
* The threshold specifies AGC "knee" in dB when the AGC is active.
*/
void rx_agc_cc::set_threshold(int threshold)
{
if ((threshold != d_threshold) && (threshold >= -160) && (threshold <= 0)) {
boost::mutex::scoped_lock lock(d_mutex);
d_threshold = threshold;
d_agc->SetParameters(d_agc_on, d_use_hang, d_threshold, d_manual_gain,
d_slope, d_decay, d_sample_rate);
}
}
/*! \brief Set new manual gain.
* \param gain The new manual gain between 0 and 100dB.
*
* The manual gain is used when AGC is switched off.
*
* \sa set_agc_on()
*/
void rx_agc_cc::set_manual_gain(int gain)
{
if ((gain != d_manual_gain) && (gain >= 0) && (gain <= 100)) {
boost::mutex::scoped_lock lock(d_mutex);
d_manual_gain = gain;
d_agc->SetParameters(d_agc_on, d_use_hang, d_threshold, d_manual_gain,
d_slope, d_decay, d_sample_rate);
}
}
/*! \brief Set AGC slope factor.
* \param slope The new slope factor between 0 and 10dB.
*
* The slope factor specifies dB reduction in output at knee from maximum output level
*/
void rx_agc_cc::set_slope(int slope)
{
if (slope != d_slope) {
boost::mutex::scoped_lock lock(d_mutex);
d_slope = slope;
d_agc->SetParameters(d_agc_on, d_use_hang, d_threshold, d_manual_gain,
d_slope, d_decay, d_sample_rate);
}
}
/*! \brief Set AGC decay time.
* \param decay The new AGC decay time between 20 to 5000 ms.
*/
void rx_agc_cc::set_decay(int decay)
{
if (decay != d_decay) {
boost::mutex::scoped_lock lock(d_mutex);
d_decay = decay;
d_agc->SetParameters(d_agc_on, d_use_hang, d_threshold, d_manual_gain,
d_slope, d_decay, d_sample_rate);
}
}
/*! \brief Enable/disable AGC hang.
* \param use_hang Whether to use hang or not.
*/
void rx_agc_cc::set_use_hang(bool use_hang)
{
if (use_hang != d_use_hang) {
boost::mutex::scoped_lock lock(d_mutex);
d_use_hang = use_hang;
d_agc->SetParameters(d_agc_on, d_use_hang, d_threshold, d_manual_gain,
d_slope, d_decay, d_sample_rate);
}
}

109
dsp/rx_agc_xx.h Normal file
View File

@ -0,0 +1,109 @@
/* -*- 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 RX_AGC_XX_H
#define RX_AGC_XX_H
#include <gr_sync_block.h>
#include <gr_complex.h>
#include <boost/thread/mutex.hpp>
#include <dsp/agc_impl.h>
class rx_agc_cc;
typedef boost::shared_ptr<rx_agc_cc> rx_agc_cc_sptr;
/*! \brief Return a shared_ptr to a new instance of rx_agc_cc.
* \param sample_rate The samle rate (default = 96000).
* \param agc_on Whether AGC should be ON (default = true).
* \param threshold AGC Knee in dB if AGC is active. Range -160 to 0dB
* (default = -100dB).
* \param manual_gain Manual gain when AGC is OFF. Range 0 to 100dB
* (default = 0dB).
* \param slope AGC slope factor. Specifies dB reduction in output at
* knee from maximum output level. Range 0 to 10dB
* (default = 5dB TBC).
* \param decay AGC decay time in milliseconds. Range 20 to 5000. This
* parameter determines whether AGC is fast/slow/medium.
* The default value is 500ms (fast AGC).
* \param use_hang Whether AGC should "hang" before starting to decay.
* The default is true.
*
* This is effectively the public constructor for a new AGC block.
* To avoid accidental use of raw pointers, the rx_agc_cc constructor is private.
* make_rx_agc_cc is the public interface for creating new instances.
*/
rx_agc_cc_sptr make_rx_agc_cc(double sample_rate = 96000.0, bool agc_on = true,
int threshold = -100, int manual_gain = 0,
int slope = 2, int decay = 100,
bool use_hang = true);
/*! \brief Experimental AGC block for analog voice modes (AM, SSB, CW).
* \ingroup DSP
*
* This block performs automatic gain control.
* To be written...
*
* \todo rx_agc_ff
*/
class rx_agc_cc : public gr_sync_block
{
friend rx_agc_cc_sptr make_rx_agc_cc(double sample_rate, bool agc_on,
int threshold, int manual_gain,
int slope, int decay, bool use_hang);
protected:
rx_agc_cc(double sample_rate, bool agc_on, int threshold, int manual_gain,
int slope, int decay, bool use_hang);
public:
~rx_agc_cc();
int work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
void set_agc_on(bool agc_on);
void set_sample_rate(double sample_rate);
void set_threshold(int threshold);
void set_manual_gain(int gain);
void set_slope(int slope);
void set_decay(int decay);
void set_use_hang(bool use_hang);
private:
CAgc *d_agc;
boost::mutex d_mutex; /*! Used to lock internal data while processing or setting parameters. */
bool d_agc_on; /*! Current AGC status (true/false). */
double d_sample_rate; /*! Current sample rate. */
int d_threshold; /*! Current AGC threshold (-160...0 dB). */
int d_manual_gain; /*! Current gain when AGC is OFF. */
int d_slope; /*! Current AGC slope (0...10 dB). */
int d_decay; /*! Current AGC decay (20...5000 ms). */
bool d_use_hang; /*! Current AGC hang status (true/false). */
TYPECPX ib[64000];
TYPECPX ob[64000];
};
#endif /* RX_AGC_XX_H */

View File

@ -58,7 +58,9 @@ SOURCES +=\
tlm/arissat/scale_psu.c \
tlm/arissat/scale_ppt.c \
dsp/rx_source_base.cc \
dsp/rx_source_fcd.cc
dsp/rx_source_fcd.cc \
dsp/rx_agc_xx.cc \
dsp/agc_impl.cpp
HEADERS += mainwindow.h \
@ -91,7 +93,9 @@ HEADERS += mainwindow.h \
tlm/arissat/scale_psu.h \
tlm/arissat/scale_ppt.h \
dsp/rx_source_base.h \
dsp/rx_source_fcd.h
dsp/rx_source_fcd.h \
dsp/rx_agc_xx.h \
dsp/agc_impl.h
FORMS += \
qtgui/dockrxopt.ui \

View File

@ -24,17 +24,16 @@
#include <gr_audio_sink.h>
#include <gr_complex_to_xxx.h>
#include <gr_multiply_const_ff.h>
#include <gr_agc2_cc.h>
#include <gr_simple_squelch_cc.h>
#include <receiver.h>
//#include <fcd/fcd_source_c.h>
#include <dsp/rx_source_fcd.h>
#include <dsp/rx_filter.h>
#include <dsp/rx_meter.h>
#include <dsp/rx_demod_fm.h>
#include <dsp/rx_demod_am.h>
#include <dsp/rx_fft.h>
#include <dsp/rx_agc_xx.h>
@ -65,7 +64,7 @@ receiver::receiver(const std::string input_device, const std::string audio_devic
filter = make_rx_filter(d_bandwidth, d_filter_offset, -5000.0, 5000.0, 1000.0);
bb_gain = gr_make_multiply_const_cc(1.0);
agc = gr_make_agc2_cc (0.5, 1.0e-4, 0.7, 1.0, 0.0);
agc = make_rx_agc_cc(d_bandwidth);
sql = gr_make_simple_squelch_cc(-100.0, 0.001);
meter = make_rx_meter_c(false);
demod_ssb = gr_make_complex_to_real(1);

View File

@ -26,17 +26,16 @@
#include <gr_multiply_const_ff.h>
#include <gr_multiply_const_cc.h>
#include <gr_simple_squelch_cc.h>
#include <gr_agc2_cc.h>
#include <gr_file_sink.h>
#include <gr_file_source.h>
#include <gr_throttle.h>
#include <gr_wavfile_sink.h>
#include <gr_wavfile_source.h>
#include <gr_null_sink.h>
//#include <fcd/fcd_source_c.h>
#include <dsp/rx_source_fcd.h>
#include <dsp/rx_filter.h>
#include <dsp/rx_meter.h>
#include <dsp/rx_agc_xx.h>
#include <dsp/rx_demod_fm.h>
#include <dsp/rx_demod_am.h>
#include <dsp/rx_fft.h>
@ -168,7 +167,7 @@ private:
rx_filter_sptr filter;
rx_meter_c_sptr meter; /*! Signal strength. */
gr_multiply_const_cc_sptr bb_gain; /*! Baseband gain. Useful for AM-type modulations. */
gr_agc2_cc_sptr agc; /*! AGC. */
rx_agc_cc_sptr agc; /*! Receiver AGC. */
gr_simple_squelch_cc_sptr sql; /*! Squelch. */
gr_complex_to_real_sptr demod_ssb; /*! SSB demodulator. */
rx_demod_fm_sptr demod_fm; /*! FM demodulator. */