mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-22 17:45:48 -05:00
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
This commit is contained in:
parent
51396e01ac
commit
8948928ca0
@ -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
|
||||
|
@ -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.
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user