diff --git a/CMakeLists.txt b/CMakeLists.txt index 1df2582d1..513231032 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ set(sdrbase_SOURCES sdrbase/audio/audiofifo.cpp sdrbase/audio/audiooutput.cpp + sdrbase/dsp/agc.cpp sdrbase/dsp/afsquelch.cpp sdrbase/dsp/channelizer.cpp sdrbase/dsp/channelmarker.cpp diff --git a/include-gpl/dsp/agc.h b/include-gpl/dsp/agc.h index 48a463b49..7ed2e9356 100644 --- a/include-gpl/dsp/agc.h +++ b/include-gpl/dsp/agc.h @@ -10,198 +10,130 @@ #include "movingaverage.h" -class SimpleAGC +class AGC { public: + AGC(); + AGC(int historySize, Real R); + virtual ~AGC(); + + void resize(int historySize, Real R); + Real getValue(); + Real getDelayedValue(); + virtual void feed(Complex& ci) = 0; + void openedSquelch(); + void closedSquelch(); + +protected: + Real m_u0; + Real m_R; // objective mag + MovingAverage m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC. + int m_historySize; + int m_count; + static const int m_mult = 4; // squelch delay multiplicator +}; + +class MagSquaredAGC : public AGC +{ +public: + MagSquaredAGC(); + MagSquaredAGC(int historySize, Real R); + virtual ~MagSquaredAGC(); + virtual void feed(Complex& ci); +}; + +class MagAGC : public AGC +{ +public: + MagAGC(); + MagAGC(int historySize, Real R); + virtual ~MagAGC(); + virtual void feed(Complex& ci); +}; + +class AlphaAGC : public AGC +{ +public: + AlphaAGC(); + AlphaAGC(int historySize, Real R); + AlphaAGC(int historySize, Real R, Real alpha); + virtual ~AlphaAGC(); + void resize(int historySize, Real R, Real alpha); + virtual void feed(Complex& ci); + void openedSquelch(); + void closedSquelch(); +private: + Real m_alpha; + bool m_squelchOpen; +}; + +class SimpleAGC +{ +public: SimpleAGC() : - m_squelchOpen(false), - m_fill(0), - m_cutoff(0), - m_clip(0), - m_moving_average() + m_squelchOpen(false), + m_fill(0), + m_cutoff(0), + m_clip(0), + m_moving_average() {} SimpleAGC(int historySize, Real initial, Real cutoff=0, Real clip=0) : - m_squelchOpen(false), - m_fill(initial), - m_cutoff(cutoff), - m_clip(clip), - m_moving_average(historySize, initial) + m_squelchOpen(false), + m_fill(initial), + m_cutoff(cutoff), + m_clip(clip), + m_moving_average(historySize, initial) {} void resize(int historySize, Real initial, Real cutoff=0, Real clip=0) { - m_fill = initial; - m_cutoff = cutoff; - m_clip = clip; - m_moving_average.resize(historySize, initial); + m_fill = initial; + m_cutoff = cutoff; + m_clip = clip; + m_moving_average.resize(historySize, initial); } Real getValue() { - if (m_moving_average.average() > m_clip) - { - return m_moving_average.average(); - } else - { - return m_clip; - } + if (m_moving_average.average() > m_clip) + { + return m_moving_average.average(); + } else + { + return m_clip; + } } - void feed(Real value) - { - if (value > m_cutoff) - { - m_moving_average.feed(value); - } - } + void feed(Real value) + { + if (value > m_cutoff) + { + m_moving_average.feed(value); + } + } - void openedSquelch() - { - m_squelchOpen = true; - } + void openedSquelch() + { + m_squelchOpen = true; + } - void closedSquelch() - { - if (m_squelchOpen) - { - //m_moving_average.fill(m_fill); // Valgrind optim - m_squelchOpen = false; - } - } + void closedSquelch() + { + if (m_squelchOpen) + { + //m_moving_average.fill(m_fill); // Valgrind optim + m_squelchOpen = false; + } + } private: - bool m_squelchOpen; // open for processing - Real m_fill; // refill average at this level - Real m_cutoff; // consider samples only above this level - Real m_clip; // never go below this level - MovingAverage m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC. + bool m_squelchOpen; // open for processing + Real m_fill; // refill average at this level + Real m_cutoff; // consider samples only above this level + Real m_clip; // never go below this level + MovingAverage m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC. }; - -class EvenSimplerAGC -{ -public: - - EvenSimplerAGC() : - m_u0(1.0), - m_R(1.0), - m_moving_average() - {} - - EvenSimplerAGC(int historySize, Real R) : - m_u0(1.0), - m_R(R), - m_moving_average(historySize, m_R) - {} - - void resize(int historySize, Real R) - { - m_R = R; - m_moving_average.resize(historySize, R); - } - - Real getValue() - { - return m_u0; - } - - void feed(Complex& ci) - { - ci *= m_u0; - Real magsq = ci.real()*ci.real() + ci.imag()*ci.imag(); - m_moving_average.feed(magsq); - } - - void openedSquelch() - { - m_u0 = m_R / m_moving_average.average(); - } - - void closedSquelch() - { - //m_moving_average.fill(m_R); // Valgrind optim - m_u0 = 1.0; - } - -private: - Real m_u0; - Real m_R; // objective magsq - MovingAverage m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC. -}; - -class AlphaAGC -{ -public: - - AlphaAGC() : - m_u0(1.0), - m_R(1.0), - m_alpha(0.1), - m_squelchOpen(true), - m_moving_average() - {} - - AlphaAGC(int historySize, Real R, Real alpha) : - m_u0(1.0), - m_R(R), - m_alpha(alpha), - m_squelchOpen(true), - m_moving_average(historySize, m_R) - {} - - void resize(int historySize, Real R, Real alpha) - { - m_R = R; - m_alpha = alpha; - m_squelchOpen = true; - m_moving_average.resize(historySize, R); - } - - Real getValue() - { - return m_u0; - } - - void feed(Complex& ci) - { - ci *= m_u0; - Real magsq = ci.real()*ci.real() + ci.imag()*ci.imag(); - - if (m_squelchOpen && (magsq < m_moving_average.average())) - { - m_moving_average.feed(m_moving_average.average() - m_alpha*(m_moving_average.average() - magsq)); - } - else - { - //m_squelchOpen = true; - m_moving_average.feed(magsq); - } - - } - - void openedSquelch() - { - m_u0 = m_R / m_moving_average.average(); - m_squelchOpen = true; - } - - void closedSquelch() - { - //m_moving_average.fill(m_R); // Valgrind optim - m_u0 = 1.0; - m_squelchOpen = false; - } - -private: - Real m_u0; - Real m_R; // objective magsq - Real m_alpha; - bool m_squelchOpen; - MovingAverage m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC. -}; - - - #endif /* INCLUDE_GPL_DSP_AGC_H_ */ diff --git a/plugins/channel/nfm/nfmdemod.cpp b/plugins/channel/nfm/nfmdemod.cpp index 944596fc5..d6a4f537a 100644 --- a/plugins/channel/nfm/nfmdemod.cpp +++ b/plugins/channel/nfm/nfmdemod.cpp @@ -26,7 +26,7 @@ #include "dsp/pidcontroller.h" #include "dsp/dspengine.h" -static const Real afSqTones[2] = {1200.0, 6000.0}; // {1200.0, 8000.0}; +static const Real afSqTones[2] = {1200.0, 8000.0}; // {1200.0, 8000.0}; MESSAGE_CLASS_DEFINITION(NFMDemod::MsgConfigureNFMDemod, Message) @@ -55,9 +55,9 @@ NFMDemod::NFMDemod() : m_audioBufferFill = 0; m_movingAverage.resize(16, 0); - m_agcLevel = 0.003; // 0.003 + m_agcLevel = 0.0625; // 0.003 //m_AGC.resize(480, m_agcLevel, 0, 0.1*m_agcLevel); - m_AGC.resize(240, m_agcLevel*m_agcLevel, 0.3); + m_AGC.resize(600, m_agcLevel*m_agcLevel); //, 0.3); m_ctcssDetector.setCoefficients(3000, 6000.0); // 0.5s / 2 Hz resolution m_afSquelch.setCoefficients(24, 48000.0, 5, 1); // 4000 Hz span, 250us @@ -165,7 +165,7 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto Real qp = ci.imag() - m_m2Sample.imag(); Real h1 = m_m1Sample.real() * qp; Real h2 = m_m1Sample.imag() * ip; - Real demod = (h1 - h2) * 10000; + Real demod = (h1 - h2) * 16; // 10000 (multiply by 2^16 after demod) m_m2Sample = m_m1Sample; m_m1Sample = ci; @@ -218,7 +218,7 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto { demod = m_bandpass.filter(demod); demod *= m_running.m_volume; - sample = demod * ((1<<15)/301); // denominator = bandpass filter number of taps + sample = demod * ((1<<16)/301) * m_AGC.getDelayedValue(); // denominator = bandpass filter number of taps } m_AGC.openedSquelch(); diff --git a/plugins/channel/nfm/nfmdemod.h b/plugins/channel/nfm/nfmdemod.h index 6bd64cebf..b7eff886b 100644 --- a/plugins/channel/nfm/nfmdemod.h +++ b/plugins/channel/nfm/nfmdemod.h @@ -163,7 +163,7 @@ private: Complex m_m1Sample; Complex m_m2Sample; MovingAverage m_movingAverage; - AlphaAGC m_AGC; + MagSquaredAGC m_AGC; Real m_agcLevel; // AGC will aim to this level Real m_agcFloor; // AGC will not go below this level diff --git a/sdrbase/dsp/agc.cpp b/sdrbase/dsp/agc.cpp new file mode 100644 index 000000000..1cd0c0180 --- /dev/null +++ b/sdrbase/dsp/agc.cpp @@ -0,0 +1,167 @@ +/* + * agc.cpp + * + * Created on: Sep 7, 2015 + * Author: f4exb + */ + +#include "dsp/agc.h" + + +AGC::AGC() : + m_u0(1.0), + m_R(1.0), + m_moving_average(), + m_historySize(0), + m_count(0) +{} + +AGC::AGC(int historySize, Real R) : + m_u0(1.0), + m_R(R), + m_moving_average(historySize, m_R), + m_historySize(historySize), + m_count(0) +{} + +AGC::~AGC() +{} + +void AGC::resize(int historySize, Real R) +{ + m_R = R; + m_moving_average.resize(historySize, R); + m_historySize = historySize; + m_count = 0; +} + +Real AGC::getValue() +{ + return m_u0; +} + +Real AGC::getDelayedValue() +{ + if (m_count < m_historySize*m_mult) + { + return 0; + } + else + { + return 1; + } +} + +void AGC::openedSquelch() +{ + if (m_count < m_historySize*m_mult) + { + m_count++; + } + + m_u0 = m_R / m_moving_average.average(); +} + +void AGC::closedSquelch() +{ + //m_moving_average.fill(m_R); // Valgrind optim + m_count = 0; + m_u0 = m_R / m_moving_average.average(); +} + + +MagSquaredAGC::MagSquaredAGC() : + AGC() +{} + +MagSquaredAGC::MagSquaredAGC(int historySize, Real R) : + AGC(historySize, R) +{} + +MagSquaredAGC::~MagSquaredAGC() +{} + +void MagSquaredAGC::feed(Complex& ci) +{ + ci *= m_u0; + Real magsq = ci.real()*ci.real() + ci.imag()*ci.imag(); + m_moving_average.feed(magsq); +} + + +MagAGC::MagAGC() : + AGC() +{} + +MagAGC::MagAGC(int historySize, Real R) : + AGC(historySize, R) +{} + +MagAGC::~MagAGC() +{} + +void MagAGC::feed(Complex& ci) +{ + ci *= m_u0; + Real mag = sqrt(ci.real()*ci.real() + ci.imag()*ci.imag()); + m_moving_average.feed(mag); +} + + +AlphaAGC::AlphaAGC() : + AGC(), + m_alpha(0.5), + m_squelchOpen(true) +{} + +AlphaAGC::AlphaAGC(int historySize, Real R) : + AGC(historySize, R), + m_alpha(0.5), + m_squelchOpen(true) +{} + + +AlphaAGC::AlphaAGC(int historySize, Real R, Real alpha) : + AGC(historySize, R), + m_alpha(alpha), + m_squelchOpen(true) +{} + +AlphaAGC::~AlphaAGC() +{} + +void AlphaAGC::resize(int historySize, Real R, Real alpha) +{ + m_R = R; + m_alpha = alpha; + m_squelchOpen = true; + m_moving_average.resize(historySize, R); +} + +void AlphaAGC::feed(Complex& ci) +{ + ci *= m_u0; + Real magsq = ci.real()*ci.real() + ci.imag()*ci.imag(); + + if (m_squelchOpen && (magsq)) + { + m_moving_average.feed(m_moving_average.average() - m_alpha*(m_moving_average.average() - magsq)); + } + else + { + //m_squelchOpen = true; + m_moving_average.feed(magsq); + } +} + +void AlphaAGC::openedSquelch() +{ + AGC::openedSquelch(); + m_squelchOpen = true; +} + +void AlphaAGC::closedSquelch() +{ + AGC::closedSquelch(); + m_squelchOpen = false; +} diff --git a/sdrbase/dsp/channelizer.cpp b/sdrbase/dsp/channelizer.cpp index 1481cd3fc..a94bad8dd 100644 --- a/sdrbase/dsp/channelizer.cpp +++ b/sdrbase/dsp/channelizer.cpp @@ -86,6 +86,8 @@ bool Channelizer::handleMessage(const Message& cmd) { qDebug() << "Channelizer::handleMessage: " << cmd.getIdentifier(); + // TODO: apply changes only if input sample rate or requested output sample rate change. Change of center frequency has no impact. + if (DSPSignalNotification::match(cmd)) { DSPSignalNotification& notif = (DSPSignalNotification&) cmd;