kopia lustrzana https://github.com/f4exb/sdrangel
NFM demod: back to the basics
rodzic
c6b2730456
commit
34942340a3
|
@ -168,5 +168,5 @@ Assuming Debian Jessie is used:
|
||||||
- Tx support with the BladeRF
|
- Tx support with the BladeRF
|
||||||
- Enhance WFM (stereo, RDS?)
|
- Enhance WFM (stereo, RDS?)
|
||||||
- Even more demods ...
|
- Even more demods ...
|
||||||
- Support for Airspy
|
- Support for Hack-RF
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,10 @@ public:
|
||||||
// Constructors and Destructor
|
// Constructors and Destructor
|
||||||
AFSquelch();
|
AFSquelch();
|
||||||
// allows user defined tone pair
|
// allows user defined tone pair
|
||||||
AFSquelch(unsigned int nbTones, const Real *tones);
|
AFSquelch(unsigned int nbTones,
|
||||||
|
const Real *tones,
|
||||||
|
int samplesAttack = 0,
|
||||||
|
int samplesDecay = 0);
|
||||||
virtual ~AFSquelch();
|
virtual ~AFSquelch();
|
||||||
|
|
||||||
// setup the basic parameters and coefficients
|
// setup the basic parameters and coefficients
|
||||||
|
@ -39,7 +42,7 @@ public:
|
||||||
|
|
||||||
// set the detection threshold
|
// set the detection threshold
|
||||||
void setThreshold(double _threshold) {
|
void setThreshold(double _threshold) {
|
||||||
threshold = _threshold;
|
m_threshold = _threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
// analyze a sample set and optionally filter
|
// analyze a sample set and optionally filter
|
||||||
|
@ -49,11 +52,11 @@ public:
|
||||||
// get the tone set
|
// get the tone set
|
||||||
const Real *getToneSet() const
|
const Real *getToneSet() const
|
||||||
{
|
{
|
||||||
return toneSet;
|
return m_toneSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open() const {
|
bool open() const {
|
||||||
return isOpen;
|
return m_isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset(); // reset the analysis algorithm
|
void reset(); // reset the analysis algorithm
|
||||||
|
@ -64,23 +67,23 @@ protected:
|
||||||
void evaluate();
|
void evaluate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int N;
|
int m_N;
|
||||||
int sampleRate;
|
int m_sampleRate;
|
||||||
int samplesProcessed;
|
int m_samplesProcessed;
|
||||||
int maxPowerIndex;
|
int m_maxPowerIndex;
|
||||||
int nTones;
|
int m_nTones;
|
||||||
int samplesAttack;
|
int m_samplesAttack;
|
||||||
int attackCount;
|
int m_attackCount;
|
||||||
int samplesDecay;
|
int m_samplesDecay;
|
||||||
int decayCount;
|
int m_decayCount;
|
||||||
bool isOpen;
|
bool m_isOpen;
|
||||||
double threshold;
|
double m_threshold;
|
||||||
double *k;
|
double *m_k;
|
||||||
double *coef;
|
double *m_coef;
|
||||||
Real *toneSet;
|
Real *m_toneSet;
|
||||||
double *u0;
|
double *m_u0;
|
||||||
double *u1;
|
double *m_u1;
|
||||||
double *power;
|
double *m_power;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,8 @@ public:
|
||||||
|
|
||||||
void resize(int historySize, Real R);
|
void resize(int historySize, Real R);
|
||||||
Real getValue();
|
Real getValue();
|
||||||
Real getDelayedValue();
|
Real getAverage();
|
||||||
virtual void feed(Complex& ci) = 0;
|
virtual void feed(Complex& ci) = 0;
|
||||||
virtual Real returnedDelayedValue() const = 0;
|
|
||||||
void openedSquelch();
|
|
||||||
void closedSquelch();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Real m_u0;
|
Real m_u0;
|
||||||
|
@ -32,7 +29,6 @@ protected:
|
||||||
MovingAverage<Real> m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC.
|
MovingAverage<Real> m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC.
|
||||||
int m_historySize;
|
int m_historySize;
|
||||||
int m_count;
|
int m_count;
|
||||||
static const int m_mult = 4; // squelch delay multiplicator
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MagSquaredAGC : public AGC
|
class MagSquaredAGC : public AGC
|
||||||
|
@ -42,7 +38,6 @@ public:
|
||||||
MagSquaredAGC(int historySize, Real R);
|
MagSquaredAGC(int historySize, Real R);
|
||||||
virtual ~MagSquaredAGC();
|
virtual ~MagSquaredAGC();
|
||||||
virtual void feed(Complex& ci);
|
virtual void feed(Complex& ci);
|
||||||
virtual Real returnedDelayedValue() const { return m_u0; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MagAGC : public AGC
|
class MagAGC : public AGC
|
||||||
|
@ -52,7 +47,6 @@ public:
|
||||||
MagAGC(int historySize, Real R);
|
MagAGC(int historySize, Real R);
|
||||||
virtual ~MagAGC();
|
virtual ~MagAGC();
|
||||||
virtual void feed(Complex& ci);
|
virtual void feed(Complex& ci);
|
||||||
virtual Real returnedDelayedValue() const { return m_u0; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AlphaAGC : public AGC
|
class AlphaAGC : public AGC
|
||||||
|
@ -64,9 +58,6 @@ public:
|
||||||
virtual ~AlphaAGC();
|
virtual ~AlphaAGC();
|
||||||
void resize(int historySize, Real R, Real alpha);
|
void resize(int historySize, Real R, Real alpha);
|
||||||
virtual void feed(Complex& ci);
|
virtual void feed(Complex& ci);
|
||||||
virtual Real returnedDelayedValue() const { return 1; }
|
|
||||||
void openedSquelch();
|
|
||||||
void closedSquelch();
|
|
||||||
private:
|
private:
|
||||||
Real m_alpha;
|
Real m_alpha;
|
||||||
bool m_squelchOpen;
|
bool m_squelchOpen;
|
||||||
|
@ -118,20 +109,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void openedSquelch()
|
|
||||||
{
|
|
||||||
m_squelchOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void closedSquelch()
|
|
||||||
{
|
|
||||||
if (m_squelchOpen)
|
|
||||||
{
|
|
||||||
//m_moving_average.fill(m_fill); // Valgrind optim
|
|
||||||
m_squelchOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_squelchOpen; // open for processing
|
bool m_squelchOpen; // open for processing
|
||||||
Real m_fill; // refill average at this level
|
Real m_fill; // refill average at this level
|
||||||
|
|
|
@ -113,7 +113,6 @@ void AMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_volumeAGC.closedSquelch();
|
|
||||||
sample = 0;
|
sample = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,14 +26,13 @@
|
||||||
#include "dsp/pidcontroller.h"
|
#include "dsp/pidcontroller.h"
|
||||||
#include "dsp/dspengine.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)
|
MESSAGE_CLASS_DEFINITION(NFMDemod::MsgConfigureNFMDemod, Message)
|
||||||
|
|
||||||
NFMDemod::NFMDemod() :
|
NFMDemod::NFMDemod() :
|
||||||
m_ctcssIndex(0),
|
m_ctcssIndex(0),
|
||||||
m_sampleCount(0),
|
m_sampleCount(0),
|
||||||
m_afSquelch(2, afSqTones),
|
|
||||||
m_squelchOpen(false),
|
m_squelchOpen(false),
|
||||||
m_audioFifo(4, 48000),
|
m_audioFifo(4, 48000),
|
||||||
m_settingsMutex(QMutex::Recursive)
|
m_settingsMutex(QMutex::Recursive)
|
||||||
|
@ -54,14 +53,11 @@ NFMDemod::NFMDemod() :
|
||||||
m_audioBuffer.resize(1<<14);
|
m_audioBuffer.resize(1<<14);
|
||||||
m_audioBufferFill = 0;
|
m_audioBufferFill = 0;
|
||||||
|
|
||||||
m_movingAverage.resize(16, 0);
|
m_movingAverage.resize(240, 0);
|
||||||
m_agcLevel = 0.0625; // 0.003
|
m_agcLevel = 1.0;
|
||||||
//m_AGC.resize(480, m_agcLevel, 0, 0.1*m_agcLevel);
|
m_AGC.resize(240, m_agcLevel);
|
||||||
m_AGC.resize(600, m_agcLevel*m_agcLevel); //, 0.3);
|
|
||||||
|
|
||||||
m_ctcssDetector.setCoefficients(3000, 6000.0); // 0.5s / 2 Hz resolution
|
m_ctcssDetector.setCoefficients(3000, 6000.0); // 0.5s / 2 Hz resolution
|
||||||
m_afSquelch.setCoefficients(24, 48000.0, 5, 1); // 4000 Hz span, 250us
|
|
||||||
m_afSquelch.setThreshold(0.001);
|
|
||||||
|
|
||||||
DSPEngine::instance()->addAudioSink(&m_audioFifo);
|
DSPEngine::instance()->addAudioSink(&m_audioFifo);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +161,7 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
|
||||||
Real qp = ci.imag() - m_m2Sample.imag();
|
Real qp = ci.imag() - m_m2Sample.imag();
|
||||||
Real h1 = m_m1Sample.real() * qp;
|
Real h1 = m_m1Sample.real() * qp;
|
||||||
Real h2 = m_m1Sample.imag() * ip;
|
Real h2 = m_m1Sample.imag() * ip;
|
||||||
Real demod = (h1 - h2) * 2; // 10000 (multiply by 2^16 after demod)
|
Real demod = (h1 - h2) * 1; // 10000 (multiply by 2^16 after demod)
|
||||||
|
|
||||||
m_m2Sample = m_m1Sample;
|
m_m2Sample = m_m1Sample;
|
||||||
m_m1Sample = ci;
|
m_m1Sample = ci;
|
||||||
|
@ -173,12 +169,7 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
|
||||||
|
|
||||||
// AF processing
|
// AF processing
|
||||||
|
|
||||||
if(m_afSquelch.analyze(&demod))
|
if (m_AGC.getAverage() > m_squelchLevel)
|
||||||
{
|
|
||||||
m_squelchOpen = m_afSquelch.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_squelchOpen)
|
|
||||||
{
|
{
|
||||||
if (m_running.m_ctcssOn)
|
if (m_running.m_ctcssOn)
|
||||||
{
|
{
|
||||||
|
@ -218,10 +209,8 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
|
||||||
{
|
{
|
||||||
demod = m_bandpass.filter(demod);
|
demod = m_bandpass.filter(demod);
|
||||||
demod *= m_running.m_volume;
|
demod *= m_running.m_volume;
|
||||||
sample = demod * ((1<<18)/301) * m_AGC.getDelayedValue(); // denominator = bandpass filter number of taps
|
sample = demod * 4; // denominator = bandpass filter number of taps
|
||||||
}
|
}
|
||||||
|
|
||||||
m_AGC.openedSquelch();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -231,7 +220,6 @@ void NFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
|
||||||
m_ctcssIndex = 0;
|
m_ctcssIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_AGC.closedSquelch();
|
|
||||||
sample = 0;
|
sample = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,10 +342,11 @@ void NFMDemod::apply()
|
||||||
|
|
||||||
if (m_config.m_squelch != m_running.m_squelch)
|
if (m_config.m_squelch != m_running.m_squelch)
|
||||||
{
|
{
|
||||||
m_squelchLevel = pow(10.0, m_config.m_squelch / 10.0);
|
// input is a power level in dB
|
||||||
m_squelchLevel *= m_squelchLevel;
|
// m_squelchLevel = pow(10.0, m_config.m_squelch / 10.0);
|
||||||
m_afSquelch.setThreshold(m_squelchLevel);
|
m_squelchLevel = pow(10.0, m_config.m_squelch / 20.0); // to magnitude
|
||||||
m_afSquelch.reset();
|
|
||||||
|
//m_squelchLevel *= m_squelchLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_running.m_inputSampleRate = m_config.m_inputSampleRate;
|
m_running.m_inputSampleRate = m_config.m_inputSampleRate;
|
||||||
|
|
|
@ -156,14 +156,13 @@ private:
|
||||||
|
|
||||||
double m_squelchLevel;
|
double m_squelchLevel;
|
||||||
//int m_squelchState;
|
//int m_squelchState;
|
||||||
AFSquelch m_afSquelch;
|
|
||||||
bool m_squelchOpen;
|
bool m_squelchOpen;
|
||||||
|
|
||||||
Real m_lastArgument;
|
Real m_lastArgument;
|
||||||
Complex m_m1Sample;
|
Complex m_m1Sample;
|
||||||
Complex m_m2Sample;
|
Complex m_m2Sample;
|
||||||
MovingAverage<Real> m_movingAverage;
|
MovingAverage<Real> m_movingAverage;
|
||||||
MagSquaredAGC m_AGC;
|
MagAGC m_AGC;
|
||||||
Real m_agcLevel; // AGC will aim to this level
|
Real m_agcLevel; // AGC will aim to this level
|
||||||
Real m_agcFloor; // AGC will not go below this level
|
Real m_agcFloor; // AGC will not go below this level
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
<item row="4" column="4">
|
<item row="4" column="4">
|
||||||
<widget class="QSlider" name="squelch">
|
<widget class="QSlider" name="squelch">
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>-60</number>
|
<number>-100</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
|
|
|
@ -18,73 +18,73 @@
|
||||||
#include "dsp/afsquelch.h"
|
#include "dsp/afsquelch.h"
|
||||||
|
|
||||||
AFSquelch::AFSquelch() :
|
AFSquelch::AFSquelch() :
|
||||||
N(0),
|
m_N(0),
|
||||||
sampleRate(0),
|
m_sampleRate(0),
|
||||||
samplesProcessed(0),
|
m_samplesProcessed(0),
|
||||||
maxPowerIndex(0),
|
m_maxPowerIndex(0),
|
||||||
nTones(2),
|
m_nTones(2),
|
||||||
samplesAttack(0),
|
m_samplesAttack(0),
|
||||||
attackCount(0),
|
m_attackCount(0),
|
||||||
samplesDecay(0),
|
m_samplesDecay(0),
|
||||||
decayCount(0),
|
m_decayCount(0),
|
||||||
isOpen(false),
|
m_isOpen(false),
|
||||||
threshold(0.0)
|
m_threshold(0.0)
|
||||||
{
|
{
|
||||||
k = new double[nTones];
|
m_k = new double[m_nTones];
|
||||||
coef = new double[nTones];
|
m_coef = new double[m_nTones];
|
||||||
toneSet = new Real[nTones];
|
m_toneSet = new Real[m_nTones];
|
||||||
u0 = new double[nTones];
|
m_u0 = new double[m_nTones];
|
||||||
u1 = new double[nTones];
|
m_u1 = new double[m_nTones];
|
||||||
power = new double[nTones];
|
m_power = new double[m_nTones];
|
||||||
|
|
||||||
toneSet[0] = 2000.0;
|
m_toneSet[0] = 2000.0;
|
||||||
toneSet[1] = 10000.0;
|
m_toneSet[1] = 10000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
AFSquelch::AFSquelch(unsigned int nbTones, const Real *tones) :
|
AFSquelch::AFSquelch(unsigned int nbTones, const Real *tones, int samplesAttack, int samplesDecay) :
|
||||||
N(0),
|
m_N(0),
|
||||||
sampleRate(0),
|
m_sampleRate(0),
|
||||||
samplesProcessed(0),
|
m_samplesProcessed(0),
|
||||||
maxPowerIndex(0),
|
m_maxPowerIndex(0),
|
||||||
nTones(nbTones),
|
m_nTones(nbTones),
|
||||||
samplesAttack(0),
|
m_samplesAttack(samplesAttack),
|
||||||
attackCount(0),
|
m_attackCount(0),
|
||||||
samplesDecay(0),
|
m_samplesDecay(samplesDecay),
|
||||||
decayCount(0),
|
m_decayCount(0),
|
||||||
isOpen(false),
|
m_isOpen(false),
|
||||||
threshold(0.0)
|
m_threshold(0.0)
|
||||||
{
|
{
|
||||||
k = new double[nTones];
|
m_k = new double[m_nTones];
|
||||||
coef = new double[nTones];
|
m_coef = new double[m_nTones];
|
||||||
toneSet = new Real[nTones];
|
m_toneSet = new Real[m_nTones];
|
||||||
u0 = new double[nTones];
|
m_u0 = new double[m_nTones];
|
||||||
u1 = new double[nTones];
|
m_u1 = new double[m_nTones];
|
||||||
power = new double[nTones];
|
m_power = new double[m_nTones];
|
||||||
|
|
||||||
for (int j = 0; j < nTones; ++j)
|
for (int j = 0; j < m_nTones; ++j)
|
||||||
{
|
{
|
||||||
toneSet[j] = tones[j];
|
m_toneSet[j] = tones[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AFSquelch::~AFSquelch()
|
AFSquelch::~AFSquelch()
|
||||||
{
|
{
|
||||||
delete[] k;
|
delete[] m_k;
|
||||||
delete[] coef;
|
delete[] m_coef;
|
||||||
delete[] toneSet;
|
delete[] m_toneSet;
|
||||||
delete[] u0;
|
delete[] m_u0;
|
||||||
delete[] u1;
|
delete[] m_u1;
|
||||||
delete[] power;
|
delete[] m_power;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AFSquelch::setCoefficients(int _N, int _samplerate, int _samplesAttack, int _samplesDecay )
|
void AFSquelch::setCoefficients(int _N, int _samplerate, int _samplesAttack, int _samplesDecay )
|
||||||
{
|
{
|
||||||
N = _N; // save the basic parameters for use during analysis
|
m_N = _N; // save the basic parameters for use during analysis
|
||||||
sampleRate = _samplerate;
|
m_sampleRate = _samplerate;
|
||||||
samplesAttack = _samplesAttack;
|
m_samplesAttack = _samplesAttack;
|
||||||
samplesDecay = _samplesDecay;
|
m_samplesDecay = _samplesDecay;
|
||||||
|
|
||||||
// for each of the frequencies (tones) of interest calculate
|
// for each of the frequencies (tones) of interest calculate
|
||||||
// k and the associated filter coefficient as per the Goertzel
|
// k and the associated filter coefficient as per the Goertzel
|
||||||
|
@ -93,10 +93,10 @@ void AFSquelch::setCoefficients(int _N, int _samplerate, int _samplesAttack, int
|
||||||
// for later display. The tone set is specified in the
|
// for later display. The tone set is specified in the
|
||||||
// constructor. Notice that the resulting coefficients are
|
// constructor. Notice that the resulting coefficients are
|
||||||
// independent of N.
|
// independent of N.
|
||||||
for (int j = 0; j < nTones; ++j)
|
for (int j = 0; j < m_nTones; ++j)
|
||||||
{
|
{
|
||||||
k[j] = ((double)N * toneSet[j]) / (double)sampleRate;
|
m_k[j] = ((double)m_N * m_toneSet[j]) / (double)m_sampleRate;
|
||||||
coef[j] = 2.0 * cos((2.0 * M_PI * toneSet[j])/(double)sampleRate);
|
m_coef[j] = 2.0 * cos((2.0 * M_PI * m_toneSet[j])/(double)m_sampleRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,12 +106,12 @@ bool AFSquelch::analyze(Real *sample)
|
||||||
{
|
{
|
||||||
|
|
||||||
feedback(*sample); // Goertzel feedback
|
feedback(*sample); // Goertzel feedback
|
||||||
samplesProcessed += 1;
|
m_samplesProcessed += 1;
|
||||||
|
|
||||||
if (samplesProcessed == N) // completed a block of N
|
if (m_samplesProcessed == m_N) // completed a block of N
|
||||||
{
|
{
|
||||||
feedForward(); // calculate the power at each tone
|
feedForward(); // calculate the power at each tone
|
||||||
samplesProcessed = 0;
|
m_samplesProcessed = 0;
|
||||||
return true; // have a result
|
return true; // have a result
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -126,21 +126,21 @@ void AFSquelch::feedback(Real in)
|
||||||
double t;
|
double t;
|
||||||
|
|
||||||
// feedback for each tone
|
// feedback for each tone
|
||||||
for (int j = 0; j < nTones; ++j)
|
for (int j = 0; j < m_nTones; ++j)
|
||||||
{
|
{
|
||||||
t = u0[j];
|
t = m_u0[j];
|
||||||
u0[j] = in + (coef[j] * u0[j]) - u1[j];
|
m_u0[j] = in + (m_coef[j] * m_u0[j]) - m_u1[j];
|
||||||
u1[j] = t;
|
m_u1[j] = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AFSquelch::feedForward()
|
void AFSquelch::feedForward()
|
||||||
{
|
{
|
||||||
for (int j = 0; j < nTones; ++j)
|
for (int j = 0; j < m_nTones; ++j)
|
||||||
{
|
{
|
||||||
power[j] = (u0[j] * u0[j]) + (u1[j] * u1[j]) - (coef[j] * u0[j] * u1[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]);
|
||||||
u0[j] = u1[j] = 0.0; // reset for next block.
|
m_u0[j] = m_u1[j] = 0.0; // reset for next block.
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate();
|
evaluate();
|
||||||
|
@ -149,14 +149,14 @@ void AFSquelch::feedForward()
|
||||||
|
|
||||||
void AFSquelch::reset()
|
void AFSquelch::reset()
|
||||||
{
|
{
|
||||||
for (int j = 0; j < nTones; ++j)
|
for (int j = 0; j < m_nTones; ++j)
|
||||||
{
|
{
|
||||||
power[j] = u0[j] = u1[j] = 0.0; // reset
|
m_power[j] = m_u0[j] = m_u1[j] = 0.0; // reset
|
||||||
}
|
}
|
||||||
|
|
||||||
samplesProcessed = 0;
|
m_samplesProcessed = 0;
|
||||||
maxPowerIndex = 0;
|
m_maxPowerIndex = 0;
|
||||||
isOpen = false;
|
m_isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -166,49 +166,51 @@ void AFSquelch::evaluate()
|
||||||
double minPower;
|
double minPower;
|
||||||
int minIndex = 0, maxIndex = 0;
|
int minIndex = 0, maxIndex = 0;
|
||||||
|
|
||||||
for (int j = 0; j < nTones; ++j)
|
for (int j = 0; j < m_nTones; ++j)
|
||||||
{
|
{
|
||||||
if (power[j] > maxPower) {
|
if (m_power[j] > maxPower) {
|
||||||
maxPower = power[j];
|
maxPower = m_power[j];
|
||||||
maxIndex = j;
|
maxIndex = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
minPower = maxPower;
|
minPower = maxPower;
|
||||||
|
|
||||||
for (int j = 0; j < nTones; ++j)
|
for (int j = 0; j < m_nTones; ++j)
|
||||||
{
|
{
|
||||||
if (power[j] < minPower) {
|
if (m_power[j] < minPower) {
|
||||||
minPower = power[j];
|
minPower = m_power[j];
|
||||||
minIndex = j;
|
minIndex = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// principle is to open if power is uneven because noise gives even power
|
// principle is to open if power is uneven because noise gives even power
|
||||||
bool open = ((maxPower - minPower) > threshold) && (minIndex > maxIndex);
|
bool open = ((maxPower - minPower) > m_threshold); // && (minIndex > maxIndex);
|
||||||
|
|
||||||
if (open)
|
if (open)
|
||||||
{
|
{
|
||||||
if (samplesAttack && (attackCount < samplesAttack))
|
if (m_samplesAttack && (m_attackCount < m_samplesAttack))
|
||||||
{
|
{
|
||||||
attackCount++;
|
m_isOpen = false;
|
||||||
|
m_attackCount++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
isOpen = true;
|
m_isOpen = true;
|
||||||
decayCount = 0;
|
m_decayCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (samplesDecay && (decayCount < samplesDecay))
|
if (m_samplesDecay && (m_decayCount < m_samplesDecay))
|
||||||
{
|
{
|
||||||
decayCount++;
|
m_isOpen = true;
|
||||||
|
m_decayCount++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
isOpen = false;
|
m_isOpen = false;
|
||||||
attackCount = 0;
|
m_attackCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,36 +40,11 @@ Real AGC::getValue()
|
||||||
return m_u0;
|
return m_u0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Real AGC::getDelayedValue()
|
Real AGC::getAverage()
|
||||||
{
|
{
|
||||||
if (m_count < m_historySize*m_mult)
|
return m_moving_average.average();
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return returnedDelayedValue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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() :
|
MagSquaredAGC::MagSquaredAGC() :
|
||||||
AGC()
|
AGC()
|
||||||
{}
|
{}
|
||||||
|
@ -83,9 +58,10 @@ MagSquaredAGC::~MagSquaredAGC()
|
||||||
|
|
||||||
void MagSquaredAGC::feed(Complex& ci)
|
void MagSquaredAGC::feed(Complex& ci)
|
||||||
{
|
{
|
||||||
ci *= m_u0;
|
|
||||||
Real magsq = ci.real()*ci.real() + ci.imag()*ci.imag();
|
Real magsq = ci.real()*ci.real() + ci.imag()*ci.imag();
|
||||||
m_moving_average.feed(magsq);
|
m_moving_average.feed(magsq);
|
||||||
|
m_u0 = m_R / m_moving_average.average();
|
||||||
|
ci *= m_u0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,9 +78,10 @@ MagAGC::~MagAGC()
|
||||||
|
|
||||||
void MagAGC::feed(Complex& ci)
|
void MagAGC::feed(Complex& ci)
|
||||||
{
|
{
|
||||||
ci *= m_u0;
|
|
||||||
Real mag = sqrt(ci.real()*ci.real() + ci.imag()*ci.imag());
|
Real mag = sqrt(ci.real()*ci.real() + ci.imag()*ci.imag());
|
||||||
m_moving_average.feed(mag);
|
m_moving_average.feed(mag);
|
||||||
|
m_u0 = m_R / m_moving_average.average();
|
||||||
|
ci *= m_u0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +117,6 @@ void AlphaAGC::resize(int historySize, Real R, Real alpha)
|
||||||
|
|
||||||
void AlphaAGC::feed(Complex& ci)
|
void AlphaAGC::feed(Complex& ci)
|
||||||
{
|
{
|
||||||
ci *= m_u0;
|
|
||||||
Real magsq = ci.real()*ci.real() + ci.imag()*ci.imag();
|
Real magsq = ci.real()*ci.real() + ci.imag()*ci.imag();
|
||||||
|
|
||||||
if (m_squelchOpen && (magsq))
|
if (m_squelchOpen && (magsq))
|
||||||
|
@ -152,16 +128,5 @@ void AlphaAGC::feed(Complex& ci)
|
||||||
//m_squelchOpen = true;
|
//m_squelchOpen = true;
|
||||||
m_moving_average.feed(magsq);
|
m_moving_average.feed(magsq);
|
||||||
}
|
}
|
||||||
}
|
ci *= m_u0;
|
||||||
|
|
||||||
void AlphaAGC::openedSquelch()
|
|
||||||
{
|
|
||||||
AGC::openedSquelch();
|
|
||||||
m_squelchOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlphaAGC::closedSquelch()
|
|
||||||
{
|
|
||||||
AGC::closedSquelch();
|
|
||||||
m_squelchOpen = false;
|
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue