From 8948928ca006cad8641b46c08cdeea1e0821254c Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 May 2015 17:19:06 +0200 Subject: [PATCH] NFM demodulation without using atan and smooth squelch with AGC suppressing most clicks on low level signals and hiss on carrier tails. Only useful modulation comes through --- Readme.md | 1 + include-gpl/dsp/agc.h | 22 +++++++++++++++------- plugins/channel/nfm/nfmdemod.cpp | 19 +++++++++++++++++++ plugins/channel/nfm/nfmdemod.h | 4 ++++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/Readme.md b/Readme.md index 4f7dd8b3d..069d452fa 100644 --- a/Readme.md +++ b/Readme.md @@ -80,6 +80,7 @@ Done since the fork - Added AM demod so now you can listen to air traffic! - Added the possibility to change the brightness and/or color of the grid. - Make the low cutoff frequency of the SSB filter variable so it can be used for CW also. + - NFM demodulation without using atan and smooth squelch with AGC suppressing most clicks on low level signals and hiss on carrier tails. Only useful modulation comes through. ===== To Do diff --git a/include-gpl/dsp/agc.h b/include-gpl/dsp/agc.h index fadebf4eb..698683e42 100644 --- a/include-gpl/dsp/agc.h +++ b/include-gpl/dsp/agc.h @@ -18,26 +18,33 @@ public: m_squelch(false), m_fill(0), m_cutoff(0), + m_clip(0), m_moving_average() {} - SimpleAGC(int historySize, Real initial, Real cutoff) : + SimpleAGC(int historySize, Real initial, Real cutoff=0, Real clip=0) : m_squelch(false), m_fill(initial), m_cutoff(cutoff), + m_clip(clip), m_moving_average(historySize, initial) {} - void resize(int historySize, Real initial, Real cutoff) + 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); } Real getValue() { - return m_moving_average.average(); + if (m_moving_average.average() > m_clip) { + return m_moving_average.average(); + } else { + return m_clip; + } } void feed(Real value) @@ -60,10 +67,11 @@ public: } private: - bool m_squelch; - Real m_fill; - Real m_cutoff; - MovingAverage m_moving_average; + bool m_squelch; // 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. }; diff --git a/plugins/channel/nfm/nfmdemod.cpp b/plugins/channel/nfm/nfmdemod.cpp index 96722304f..780e2e981 100644 --- a/plugins/channel/nfm/nfmdemod.cpp +++ b/plugins/channel/nfm/nfmdemod.cpp @@ -43,6 +43,8 @@ NFMDemod::NFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink) : m_audioBufferFill = 0; m_movingAverage.resize(16, 0); + m_agcLevel = 0.003; + m_AGC.resize(4096, m_agcLevel, 0, 0.1*m_agcLevel); } NFMDemod::~NFMDemod() @@ -108,6 +110,9 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter if(m_squelchState > 0) { m_squelchState--; + m_AGC.feed(abs(ci)); + ci *= (m_agcLevel / m_AGC.getValue()); + // demod /* Real argument = arg(ci); @@ -115,9 +120,12 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter m_lastArgument = argument; */ + /* + // Original NFM Complex d = conj(m_m1Sample) * ci; Real demod = atan2(d.imag(), d.real()); demod /= M_PI; + */ /* Real argument1 = arg(ci);//atan2(ci.imag(), ci.real()); @@ -126,6 +134,14 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter m_lastSample = Complex(argument1, 0); */ + // Alternative without atan - needs AGC + // http://www.embedded.com/design/configurable-systems/4212086/DSP-Tricks--Frequency-demodulation-algorithms- + Real ip = ci.real() - m_m2Sample.real(); + Real qp = ci.imag() - m_m2Sample.imag(); + Real h1 = m_m1Sample.real() * qp; + Real h2 = m_m1Sample.imag() * ip; + Real demod = (h1 - h2) * 10000; + m_m2Sample = m_m1Sample; m_m1Sample = ci; @@ -133,15 +149,18 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter demod = m_lowpass.filter(demod); + /* if(demod < -1) demod = -1; else if(demod > 1) demod = 1; + */ demod *= m_running.m_volume; sample = demod * 32700; } else { + m_AGC.close(); sample = 0; } diff --git a/plugins/channel/nfm/nfmdemod.h b/plugins/channel/nfm/nfmdemod.h index dc8b76365..9996f8bcd 100644 --- a/plugins/channel/nfm/nfmdemod.h +++ b/plugins/channel/nfm/nfmdemod.h @@ -24,6 +24,7 @@ #include "dsp/interpolator.h" #include "dsp/lowpass.h" #include "dsp/movingaverage.h" +#include "dsp/agc.h" #include "audio/audiofifo.h" #include "util/message.h" @@ -119,6 +120,9 @@ private: Complex m_m1Sample; Complex m_m2Sample; MovingAverage m_movingAverage; + SimpleAGC m_AGC; + Real m_agcLevel; // AGC will aim to this level + Real m_agcFloor; // AGC will not go below this level AudioVector m_audioBuffer; uint m_audioBufferFill;