mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-29 11:18:56 -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 AM demod so now you can listen to air traffic!
|
||||||
- Added the possibility to change the brightness and/or color of the grid.
|
- 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.
|
- 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
|
To Do
|
||||||
|
@ -18,26 +18,33 @@ public:
|
|||||||
m_squelch(false),
|
m_squelch(false),
|
||||||
m_fill(0),
|
m_fill(0),
|
||||||
m_cutoff(0),
|
m_cutoff(0),
|
||||||
|
m_clip(0),
|
||||||
m_moving_average()
|
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_squelch(false),
|
||||||
m_fill(initial),
|
m_fill(initial),
|
||||||
m_cutoff(cutoff),
|
m_cutoff(cutoff),
|
||||||
|
m_clip(clip),
|
||||||
m_moving_average(historySize, initial)
|
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_fill = initial;
|
||||||
m_cutoff = cutoff;
|
m_cutoff = cutoff;
|
||||||
|
m_clip = clip;
|
||||||
m_moving_average.resize(historySize, initial);
|
m_moving_average.resize(historySize, initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
Real getValue()
|
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)
|
void feed(Real value)
|
||||||
@ -60,10 +67,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_squelch;
|
bool m_squelch; // open for processing
|
||||||
Real m_fill;
|
Real m_fill; // refill average at this level
|
||||||
Real m_cutoff;
|
Real m_cutoff; // consider samples only above this level
|
||||||
MovingAverage m_moving_average;
|
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_audioBufferFill = 0;
|
||||||
|
|
||||||
m_movingAverage.resize(16, 0);
|
m_movingAverage.resize(16, 0);
|
||||||
|
m_agcLevel = 0.003;
|
||||||
|
m_AGC.resize(4096, m_agcLevel, 0, 0.1*m_agcLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
NFMDemod::~NFMDemod()
|
NFMDemod::~NFMDemod()
|
||||||
@ -108,6 +110,9 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
|
|||||||
if(m_squelchState > 0) {
|
if(m_squelchState > 0) {
|
||||||
m_squelchState--;
|
m_squelchState--;
|
||||||
|
|
||||||
|
m_AGC.feed(abs(ci));
|
||||||
|
ci *= (m_agcLevel / m_AGC.getValue());
|
||||||
|
|
||||||
// demod
|
// demod
|
||||||
/*
|
/*
|
||||||
Real argument = arg(ci);
|
Real argument = arg(ci);
|
||||||
@ -115,9 +120,12 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
|
|||||||
m_lastArgument = argument;
|
m_lastArgument = argument;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Original NFM
|
||||||
Complex d = conj(m_m1Sample) * ci;
|
Complex d = conj(m_m1Sample) * ci;
|
||||||
Real demod = atan2(d.imag(), d.real());
|
Real demod = atan2(d.imag(), d.real());
|
||||||
demod /= M_PI;
|
demod /= M_PI;
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Real argument1 = arg(ci);//atan2(ci.imag(), ci.real());
|
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);
|
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_m2Sample = m_m1Sample;
|
||||||
m_m1Sample = ci;
|
m_m1Sample = ci;
|
||||||
|
|
||||||
@ -133,15 +149,18 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter
|
|||||||
|
|
||||||
demod = m_lowpass.filter(demod);
|
demod = m_lowpass.filter(demod);
|
||||||
|
|
||||||
|
/*
|
||||||
if(demod < -1)
|
if(demod < -1)
|
||||||
demod = -1;
|
demod = -1;
|
||||||
else if(demod > 1)
|
else if(demod > 1)
|
||||||
demod = 1;
|
demod = 1;
|
||||||
|
*/
|
||||||
|
|
||||||
demod *= m_running.m_volume;
|
demod *= m_running.m_volume;
|
||||||
sample = demod * 32700;
|
sample = demod * 32700;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
m_AGC.close();
|
||||||
sample = 0;
|
sample = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "dsp/interpolator.h"
|
#include "dsp/interpolator.h"
|
||||||
#include "dsp/lowpass.h"
|
#include "dsp/lowpass.h"
|
||||||
#include "dsp/movingaverage.h"
|
#include "dsp/movingaverage.h"
|
||||||
|
#include "dsp/agc.h"
|
||||||
#include "audio/audiofifo.h"
|
#include "audio/audiofifo.h"
|
||||||
#include "util/message.h"
|
#include "util/message.h"
|
||||||
|
|
||||||
@ -119,6 +120,9 @@ private:
|
|||||||
Complex m_m1Sample;
|
Complex m_m1Sample;
|
||||||
Complex m_m2Sample;
|
Complex m_m2Sample;
|
||||||
MovingAverage m_movingAverage;
|
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;
|
AudioVector m_audioBuffer;
|
||||||
uint m_audioBufferFill;
|
uint m_audioBufferFill;
|
||||||
|
Loading…
Reference in New Issue
Block a user