diff --git a/CMakeLists.txt b/CMakeLists.txt index 152cf1066..8e192c903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,8 +55,8 @@ set(sdrbase_SOURCES sdrbase/audio/audiofifo.cpp sdrbase/audio/audiooutput.cpp - sdrbase/dsp/agc.cpp sdrbase/dsp/afsquelch.cpp + sdrbase/dsp/agc.cpp sdrbase/dsp/channelizer.cpp sdrbase/dsp/channelmarker.cpp sdrbase/dsp/ctcssdetector.cpp @@ -127,6 +127,7 @@ set(sdrbase_HEADERS include-gpl/audio/audiofifo.h include-gpl/audio/audiooutput.h + include-gpl/dsp/afsquelch.h include-gpl/dsp/channelizer.h include/dsp/channelmarker.h include-gpl/dsp/complex.h diff --git a/include-gpl/dsp/afsquelch.h b/include-gpl/dsp/afsquelch.h index 3989f9688..eb587995d 100644 --- a/include-gpl/dsp/afsquelch.h +++ b/include-gpl/dsp/afsquelch.h @@ -18,6 +18,7 @@ #define INCLUDE_GPL_DSP_AFSQUELCH_H_ #include "dsp/dsptypes.h" +#include "dsp/movingaverage.h" /** AFSquelch: AF squelch class based on the Modified Goertzel * algorithm. @@ -28,26 +29,24 @@ public: AFSquelch(); // allows user defined tone pair AFSquelch(unsigned int nbTones, - const Real *tones, - int samplesAttack = 0, - int samplesDecay = 0); + const Real *tones); virtual ~AFSquelch(); // setup the basic parameters and coefficients void setCoefficients( - int N, // the algorithm "block" size - int SampleRate, // input signal sample rate - int _samplesAttack, // number of results before squelch opens - int _samplesDecay); // number of results keeping squelch open + int N, //!< the algorithm "block" size + unsigned int nbAvg, //!< averaging size + int SampleRate, //!< input signal sample rate + int _samplesAttack, //!< number of results before squelch opens + int _samplesDecay); //!< number of results keeping squelch open // set the detection threshold - void setThreshold(double _threshold) { - m_threshold = _threshold; - } + void setThreshold(double _threshold); // analyze a sample set and optionally filter // the tone frequencies. - bool analyze(Real *sample); // input signal sample + bool analyze(Real sample); // input signal sample + bool evaluate(); // evaluate result // get the tone set const Real *getToneSet() const @@ -64,9 +63,9 @@ public: protected: void feedback(Real sample); void feedForward(); - void evaluate(); private: + unsigned int m_nbAvg; //!< number of power samples taken for moving average int m_N; int m_sampleRate; int m_samplesProcessed; @@ -84,6 +83,7 @@ private: double *m_u0; double *m_u1; double *m_power; + std::vector > m_movingAverages; }; diff --git a/include-gpl/dsp/movingaverage.h b/include-gpl/dsp/movingaverage.h index 545d46608..dfe284856 100644 --- a/include-gpl/dsp/movingaverage.h +++ b/include-gpl/dsp/movingaverage.h @@ -51,6 +51,11 @@ public: return m_sum / (float) m_history.size(); } + Type sum() const + { + return m_sum; + } + protected: std::vector m_history; Type m_sum; diff --git a/plugins/channel/nfm/nfmdemod.cpp b/plugins/channel/nfm/nfmdemod.cpp index 2fcc838e5..26fc113ae 100644 --- a/plugins/channel/nfm/nfmdemod.cpp +++ b/plugins/channel/nfm/nfmdemod.cpp @@ -33,7 +33,7 @@ MESSAGE_CLASS_DEFINITION(NFMDemod::MsgConfigureNFMDemod, Message) NFMDemod::NFMDemod() : m_ctcssIndex(0), m_sampleCount(0), - m_squelchOpen(false), + m_afSquelch(2, afSqTones), m_audioFifo(4, 48000), m_settingsMutex(QMutex::Recursive) { @@ -53,11 +53,11 @@ NFMDemod::NFMDemod() : m_audioBuffer.resize(1<<14); m_audioBufferFill = 0; - m_movingAverage.resize(240, 0); m_agcLevel = 1.0; m_AGC.resize(240, m_agcLevel); m_ctcssDetector.setCoefficients(3000, 6000.0); // 0.5s / 2 Hz resolution + m_afSquelch.setCoefficients(24, 1200, 48000.0, 4, 0); // 4000 Hz span, 250us DSPEngine::instance()->addAudioSink(&m_audioFifo); } @@ -114,6 +114,7 @@ Real angleDist(Real a, Real b) void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) { + bool squelchOpen; Complex ci; m_settingsMutex.lock(); @@ -169,7 +170,13 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto // AF processing - if (m_AGC.getAverage() > m_squelchLevel) + if (m_afSquelch.analyze(demod)) + { + squelchOpen = m_afSquelch.evaluate(); + } + + if (squelchOpen) + //if (m_AGC.getAverage() > m_squelchLevel) { if (m_running.m_ctcssOn) { @@ -343,10 +350,9 @@ void NFMDemod::apply() if (m_config.m_squelch != m_running.m_squelch) { // input is a power level in dB - // m_squelchLevel = pow(10.0, m_config.m_squelch / 10.0); - m_squelchLevel = pow(10.0, m_config.m_squelch / 20.0); // to magnitude - + m_squelchLevel = pow(10.0, m_config.m_squelch / 10.0); //m_squelchLevel *= m_squelchLevel; + m_afSquelch.setThreshold(m_squelchLevel); } m_running.m_inputSampleRate = m_config.m_inputSampleRate; diff --git a/plugins/channel/nfm/nfmdemod.h b/plugins/channel/nfm/nfmdemod.h index 4fa26f632..bf3dae5fe 100644 --- a/plugins/channel/nfm/nfmdemod.h +++ b/plugins/channel/nfm/nfmdemod.h @@ -25,7 +25,7 @@ #include "dsp/interpolator.h" #include "dsp/lowpass.h" #include "dsp/bandpass.h" -#include "dsp/movingaverage.h" +#include "dsp/afsquelch.h" #include "dsp/agc.h" #include "dsp/ctcssdetector.h" #include "dsp/afsquelch.h" @@ -155,14 +155,12 @@ private: int m_sampleCount; double m_squelchLevel; - //int m_squelchState; - bool m_squelchOpen; Real m_lastArgument; Complex m_m1Sample; Complex m_m2Sample; - MovingAverage m_movingAverage; MagAGC m_AGC; + AFSquelch m_afSquelch; Real m_agcLevel; // AGC will aim to this level Real m_agcFloor; // AGC will not go below this level diff --git a/plugins/channel/nfm/nfmdemodgui.ui b/plugins/channel/nfm/nfmdemodgui.ui index 739c06707..f9974a386 100644 --- a/plugins/channel/nfm/nfmdemodgui.ui +++ b/plugins/channel/nfm/nfmdemodgui.ui @@ -131,7 +131,7 @@ - -100 + -30 0 @@ -140,7 +140,7 @@ 1 - -40 + -20 Qt::Horizontal diff --git a/plugins/samplesource/airspy/airspyinput.cpp b/plugins/samplesource/airspy/airspyinput.cpp index b0092e8fd..d935d7dfa 100644 --- a/plugins/samplesource/airspy/airspyinput.cpp +++ b/plugins/samplesource/airspy/airspyinput.cpp @@ -131,7 +131,7 @@ bool AirspyInput::start(int device) stop(); } - if (!m_sampleFifo.setSize(96000 * 4)) + if (!m_sampleFifo.setSize(1<<19)) { qCritical("AirspyInput::start: could not allocate SampleFifo"); return false; diff --git a/sdrbase/dsp/afsquelch.cpp b/sdrbase/dsp/afsquelch.cpp index 2087d435d..b38caabbc 100644 --- a/sdrbase/dsp/afsquelch.cpp +++ b/sdrbase/dsp/afsquelch.cpp @@ -18,6 +18,7 @@ #include "dsp/afsquelch.h" AFSquelch::AFSquelch() : + m_nbAvg(128), m_N(0), m_sampleRate(0), m_samplesProcessed(0), @@ -36,20 +37,22 @@ AFSquelch::AFSquelch() : m_u0 = new double[m_nTones]; m_u1 = new double[m_nTones]; m_power = new double[m_nTones]; + m_movingAverages.resize(m_nTones, MovingAverage(m_nbAvg, 0.0)); m_toneSet[0] = 2000.0; m_toneSet[1] = 10000.0; } -AFSquelch::AFSquelch(unsigned int nbTones, const Real *tones, int samplesAttack, int samplesDecay) : +AFSquelch::AFSquelch(unsigned int nbTones, const Real *tones) : m_N(0), + m_nbAvg(0), m_sampleRate(0), m_samplesProcessed(0), m_maxPowerIndex(0), m_nTones(nbTones), - m_samplesAttack(samplesAttack), + m_samplesAttack(0), m_attackCount(0), - m_samplesDecay(samplesDecay), + m_samplesDecay(0), m_decayCount(0), m_isOpen(false), m_threshold(0.0) @@ -79,12 +82,14 @@ AFSquelch::~AFSquelch() } -void AFSquelch::setCoefficients(int _N, int _samplerate, int _samplesAttack, int _samplesDecay ) +void AFSquelch::setCoefficients(int _N, unsigned int nbAvg, int _samplerate, int _samplesAttack, int _samplesDecay ) { m_N = _N; // save the basic parameters for use during analysis + m_nbAvg = nbAvg; m_sampleRate = _samplerate; m_samplesAttack = _samplesAttack; m_samplesDecay = _samplesDecay; + m_movingAverages.resize(m_nTones, MovingAverage(m_nbAvg, 0.0)); // for each of the frequencies (tones) of interest calculate // k and the associated filter coefficient as per the Goertzel @@ -102,10 +107,10 @@ void AFSquelch::setCoefficients(int _N, int _samplerate, int _samplesAttack, int // Analyze an input signal -bool AFSquelch::analyze(Real *sample) +bool AFSquelch::analyze(Real sample) { - feedback(*sample); // Goertzel feedback + feedback(sample); // Goertzel feedback m_samplesProcessed += 1; if (m_samplesProcessed == m_N) // completed a block of N @@ -140,6 +145,7 @@ void AFSquelch::feedForward() for (int j = 0; j < m_nTones; ++j) { m_power[j] = (m_u0[j] * m_u0[j]) + (m_u1[j] * m_u1[j]) - (m_coef[j] * m_u0[j] * m_u1[j]); + m_movingAverages[j].feed(m_power[j]); m_u0[j] = m_u1[j] = 0.0; // reset for next block. } @@ -152,6 +158,7 @@ void AFSquelch::reset() for (int j = 0; j < m_nTones; ++j) { m_power[j] = m_u0[j] = m_u1[j] = 0.0; // reset + m_movingAverages[j].fill(0.0); } m_samplesProcessed = 0; @@ -160,7 +167,7 @@ void AFSquelch::reset() } -void AFSquelch::evaluate() +bool AFSquelch::evaluate() { double maxPower = 0.0; double minPower; @@ -168,8 +175,9 @@ void AFSquelch::evaluate() for (int j = 0; j < m_nTones; ++j) { - if (m_power[j] > maxPower) { - maxPower = m_power[j]; + if (m_movingAverages[j].sum() > maxPower) + { + maxPower = m_movingAverages[j].sum(); maxIndex = j; } } @@ -178,14 +186,14 @@ void AFSquelch::evaluate() for (int j = 0; j < m_nTones; ++j) { - if (m_power[j] < minPower) { - minPower = m_power[j]; + if (m_movingAverages[j].sum() < minPower) { + minPower = m_movingAverages[j].sum(); minIndex = j; } } // principle is to open if power is uneven because noise gives even power - bool open = ((maxPower - minPower) > m_threshold); // && (minIndex > maxIndex); + bool open = (minPower/maxPower < m_threshold) && (minIndex > maxIndex); if (open) { @@ -213,4 +221,12 @@ void AFSquelch::evaluate() m_attackCount = 0; } } + + return m_isOpen; +} + +void AFSquelch::setThreshold(double threshold) +{ + qDebug("AFSquelch::setThreshold: threshold: %f", threshold); + m_threshold = threshold; }