diff --git a/dsp/agc_impl.cpp b/dsp/agc_impl.cpp new file mode 100644 index 00000000..95c03f8b --- /dev/null +++ b/dsp/agc_impl.cpp @@ -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 +#include + +////////////////////////////////////////////////////////////////////// +// 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 + +#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 diff --git a/dsp/rx_agc_xx.cc b/dsp/rx_agc_xx.cc new file mode 100644 index 00000000..51a53fbe --- /dev/null +++ b/dsp/rx_agc_xx.cc @@ -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 +#include +#include +#include + + +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); + } +} diff --git a/dsp/rx_agc_xx.h b/dsp/rx_agc_xx.h new file mode 100644 index 00000000..99e45357 --- /dev/null +++ b/dsp/rx_agc_xx.h @@ -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 +#include +#include +#include + +class rx_agc_cc; + +typedef boost::shared_ptr 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 */ diff --git a/gqrx.pro b/gqrx.pro index 7926e0b3..e9abd605 100644 --- a/gqrx.pro +++ b/gqrx.pro @@ -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 \ diff --git a/receiver.cc b/receiver.cc index 935ddc93..8f46fd88 100644 --- a/receiver.cc +++ b/receiver.cc @@ -24,17 +24,16 @@ #include #include #include -#include #include #include -//#include #include #include #include #include #include #include +#include @@ -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); diff --git a/receiver.h b/receiver.h index 15a1c9f6..b54dd196 100644 --- a/receiver.h +++ b/receiver.h @@ -26,17 +26,16 @@ #include #include #include -#include #include #include #include #include #include #include -//#include #include #include #include +#include #include #include #include @@ -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. */