mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-09-04 14:17:50 -04:00
SSB demod: updated AGC
This commit is contained in:
parent
d15b484a4f
commit
2597883015
@ -24,6 +24,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "audio/audiooutput.h"
|
#include "audio/audiooutput.h"
|
||||||
#include "dsp/dspengine.h"
|
#include "dsp/dspengine.h"
|
||||||
|
#include "util/db.h"
|
||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureSSBDemod, Message)
|
MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureSSBDemod, Message)
|
||||||
|
|
||||||
@ -32,7 +33,10 @@ SSBDemod::SSBDemod(BasebandSampleSink* sampleSink) :
|
|||||||
m_audioFlipChannels(false),
|
m_audioFlipChannels(false),
|
||||||
m_dsb(false),
|
m_dsb(false),
|
||||||
m_audioMute(false),
|
m_audioMute(false),
|
||||||
m_agc(12000, 40.0, 1e-2),
|
m_agc(12000, agcTarget, 1e-2),
|
||||||
|
m_agcActive(false),
|
||||||
|
m_agcNbSamples(12000),
|
||||||
|
m_agcPowerThreshold(1e-2),
|
||||||
m_sampleSink(sampleSink),
|
m_sampleSink(sampleSink),
|
||||||
m_audioFifo(4, 24000),
|
m_audioFifo(4, 24000),
|
||||||
m_settingsMutex(QMutex::Recursive)
|
m_settingsMutex(QMutex::Recursive)
|
||||||
@ -181,25 +185,25 @@ void SSBDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double agcVal = m_agc.feedAndGetValue(sideband[i]);
|
double agcVal = m_agcActive ? m_agc.feedAndGetValue(sideband[i]) : 1.0;
|
||||||
|
|
||||||
if (m_audioBinaual)
|
if (m_audioBinaual)
|
||||||
{
|
{
|
||||||
if (m_audioFlipChannels)
|
if (m_audioFlipChannels)
|
||||||
{
|
{
|
||||||
m_audioBuffer[m_audioBufferFill].r = (qint16)(sideband[i].imag() * m_volume * agcVal * 10);
|
m_audioBuffer[m_audioBufferFill].r = (qint16)(sideband[i].imag() * m_volume * agcVal);
|
||||||
m_audioBuffer[m_audioBufferFill].l = (qint16)(sideband[i].real() * m_volume * agcVal * 10);
|
m_audioBuffer[m_audioBufferFill].l = (qint16)(sideband[i].real() * m_volume * agcVal);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_audioBuffer[m_audioBufferFill].r = (qint16)(sideband[i].real() * m_volume * agcVal * 10);
|
m_audioBuffer[m_audioBufferFill].r = (qint16)(sideband[i].real() * m_volume * agcVal);
|
||||||
m_audioBuffer[m_audioBufferFill].l = (qint16)(sideband[i].imag() * m_volume * agcVal * 10);
|
m_audioBuffer[m_audioBufferFill].l = (qint16)(sideband[i].imag() * m_volume * agcVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Real demod = (sideband[i].real() + sideband[i].imag()) * 0.7;
|
Real demod = (sideband[i].real() + sideband[i].imag()) * 0.7;
|
||||||
qint16 sample = (qint16)(demod * m_volume * agcVal * 10);
|
qint16 sample = (qint16)(demod * m_volume * agcVal);
|
||||||
m_audioBuffer[m_audioBufferFill].l = sample;
|
m_audioBuffer[m_audioBufferFill].l = sample;
|
||||||
m_audioBuffer[m_audioBufferFill].r = sample;
|
m_audioBuffer[m_audioBufferFill].r = sample;
|
||||||
}
|
}
|
||||||
@ -305,6 +309,22 @@ bool SSBDemod::handleMessage(const Message& cmd)
|
|||||||
m_audioFlipChannels = cfg.getAudioFlipChannels();
|
m_audioFlipChannels = cfg.getAudioFlipChannels();
|
||||||
m_dsb = cfg.getDSB();
|
m_dsb = cfg.getDSB();
|
||||||
m_audioMute = cfg.getAudioMute();
|
m_audioMute = cfg.getAudioMute();
|
||||||
|
m_agcActive = cfg.getAGC();
|
||||||
|
|
||||||
|
int agcNbSamples = 48 * (1<<cfg.getAGCTimeLog2());
|
||||||
|
double agcPowerThreshold = CalcDb::powerFromdB(cfg.getAGCPowerThershold()) * (1<<30);
|
||||||
|
|
||||||
|
if (m_agcNbSamples != agcNbSamples)
|
||||||
|
{
|
||||||
|
m_agc.resize(agcNbSamples, agcTarget);
|
||||||
|
m_agcNbSamples = agcNbSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_agcPowerThreshold != agcPowerThreshold)
|
||||||
|
{
|
||||||
|
m_agc.setThreshold(agcPowerThreshold);
|
||||||
|
m_agcPowerThreshold = agcPowerThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
m_settingsMutex.unlock();
|
m_settingsMutex.unlock();
|
||||||
|
|
||||||
@ -315,7 +335,10 @@ bool SSBDemod::handleMessage(const Message& cmd)
|
|||||||
<< " m_audioBinaual: " << m_audioBinaual
|
<< " m_audioBinaual: " << m_audioBinaual
|
||||||
<< " m_audioFlipChannels: " << m_audioFlipChannels
|
<< " m_audioFlipChannels: " << m_audioFlipChannels
|
||||||
<< " m_dsb: " << m_dsb
|
<< " m_dsb: " << m_dsb
|
||||||
<< "m_audioMute: " << m_audioMute;
|
<< " m_audioMute: " << m_audioMute
|
||||||
|
<< " m_agcActive: " << m_agcActive
|
||||||
|
<< " agcNbSamples: " << agcNbSamples
|
||||||
|
<< " agcPowerThreshold: " << agcPowerThreshold;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "util/message.h"
|
#include "util/message.h"
|
||||||
|
|
||||||
#define ssbFftLen 1024
|
#define ssbFftLen 1024
|
||||||
|
#define agcTarget 327.68 // -20 dB amplitude => -40 dB power: center of normal signal
|
||||||
|
|
||||||
class SSBDemod : public BasebandSampleSink {
|
class SSBDemod : public BasebandSampleSink {
|
||||||
public:
|
public:
|
||||||
@ -173,6 +174,9 @@ private:
|
|||||||
double m_magsqPeak;
|
double m_magsqPeak;
|
||||||
int m_magsqCount;
|
int m_magsqCount;
|
||||||
MagAGC m_agc;
|
MagAGC m_agc;
|
||||||
|
bool m_agcActive;
|
||||||
|
int m_agcNbSamples; //!< number of audio (48 kHz) samples for AGC averaging
|
||||||
|
double m_agcPowerThreshold; //!< AGC power threshold (linear)
|
||||||
|
|
||||||
NCOF m_nco;
|
NCOF m_nco;
|
||||||
Interpolator m_interpolator;
|
Interpolator m_interpolator;
|
||||||
|
@ -79,6 +79,9 @@ QByteArray SSBDemodGUI::serialize() const
|
|||||||
s.writeBool(8, m_audioBinaural);
|
s.writeBool(8, m_audioBinaural);
|
||||||
s.writeBool(9, m_audioFlipChannels);
|
s.writeBool(9, m_audioFlipChannels);
|
||||||
s.writeBool(10, m_dsb);
|
s.writeBool(10, m_dsb);
|
||||||
|
s.writeBool(11, ui->agc->isChecked());
|
||||||
|
s.writeS32(12, ui->agcTimeLog2->value());
|
||||||
|
s.writeS32(13, ui->agcPowerThreshold->value());
|
||||||
return s.final();
|
return s.final();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +100,7 @@ bool SSBDemodGUI::deserialize(const QByteArray& data)
|
|||||||
QByteArray bytetmp;
|
QByteArray bytetmp;
|
||||||
quint32 u32tmp;
|
quint32 u32tmp;
|
||||||
qint32 tmp;
|
qint32 tmp;
|
||||||
|
bool booltmp;
|
||||||
|
|
||||||
blockApplySettings(true);
|
blockApplySettings(true);
|
||||||
m_channelMarker.blockSignals(true);
|
m_channelMarker.blockSignals(true);
|
||||||
@ -122,6 +126,12 @@ bool SSBDemodGUI::deserialize(const QByteArray& data)
|
|||||||
ui->audioFlipChannels->setChecked(m_audioFlipChannels);
|
ui->audioFlipChannels->setChecked(m_audioFlipChannels);
|
||||||
d.readBool(10, &m_dsb);
|
d.readBool(10, &m_dsb);
|
||||||
ui->dsb->setChecked(m_dsb);
|
ui->dsb->setChecked(m_dsb);
|
||||||
|
d.readBool(11, &booltmp, false);
|
||||||
|
ui->agc->setChecked(booltmp);
|
||||||
|
d.readS32(12, &tmp, 7);
|
||||||
|
ui->agcTimeText->setText(QString("%1").arg((1<<tmp), 0, 'f', 0));
|
||||||
|
d.readS32(13, &tmp, -20);
|
||||||
|
ui->agcPowerThresholdText->setText(QString("%1").arg(tmp, 0, 'f', 0));
|
||||||
|
|
||||||
blockApplySettings(false);
|
blockApplySettings(false);
|
||||||
m_channelMarker.blockSignals(false);
|
m_channelMarker.blockSignals(false);
|
||||||
@ -265,6 +275,23 @@ void SSBDemodGUI::on_volume_valueChanged(int value)
|
|||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SSBDemodGUI::on_agc_stateChanged(int state)
|
||||||
|
{
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSBDemodGUI::on_agcTimeLog2_valueChanged(int value)
|
||||||
|
{
|
||||||
|
ui->agcTimeText->setText(QString("%1").arg((1<<value), 0, 'f', 0));
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSBDemodGUI::on_agcPowerThreshold_valueChanged(int value)
|
||||||
|
{
|
||||||
|
ui->agcPowerThresholdText->setText(QString("%1").arg(value, 0, 'f', 0));
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
void SSBDemodGUI::on_audioMute_toggled(bool checked)
|
void SSBDemodGUI::on_audioMute_toggled(bool checked)
|
||||||
{
|
{
|
||||||
m_audioMute = checked;
|
m_audioMute = checked;
|
||||||
|
@ -433,6 +433,9 @@
|
|||||||
<height>24</height>
|
<height>24</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -489,6 +492,12 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="agcTimeText">
|
<widget class="QLabel" name="agcTimeText">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>30</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>AGC time constant (ms)</string>
|
<string>AGC time constant (ms)</string>
|
||||||
</property>
|
</property>
|
||||||
@ -521,12 +530,18 @@
|
|||||||
<number>1</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>-40</number>
|
<number>-50</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="agcPowerThersholdText">
|
<widget class="QLabel" name="agcPowerThresholdText">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>AGC power threshold (dB)</string>
|
<string>AGC power threshold (dB)</string>
|
||||||
</property>
|
</property>
|
||||||
@ -677,6 +692,14 @@
|
|||||||
<include location="../../../sdrbase/resources/res.qrc"/>
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
<include location="../../../sdrbase/resources/res.qrc"/>
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
<include location="../../../sdrbase/resources/res.qrc"/>
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -45,7 +45,8 @@ Real AGC::getAverage()
|
|||||||
MagSquaredAGC::MagSquaredAGC(int historySize, double R, double threshold) :
|
MagSquaredAGC::MagSquaredAGC(int historySize, double R, double threshold) :
|
||||||
AGC(historySize, R),
|
AGC(historySize, R),
|
||||||
m_magsq(0.0),
|
m_magsq(0.0),
|
||||||
m_threshold(threshold)
|
m_threshold(threshold),
|
||||||
|
m_thresholdCount(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
MagSquaredAGC::~MagSquaredAGC()
|
MagSquaredAGC::~MagSquaredAGC()
|
||||||
@ -64,7 +65,19 @@ double MagSquaredAGC::feedAndGetValue(const Complex& ci)
|
|||||||
m_magsq = ci.real()*ci.real() + ci.imag()*ci.imag();
|
m_magsq = ci.real()*ci.real() + ci.imag()*ci.imag();
|
||||||
m_moving_average.feed(m_magsq);
|
m_moving_average.feed(m_magsq);
|
||||||
m_u0 = m_R / m_moving_average.average();
|
m_u0 = m_R / m_moving_average.average();
|
||||||
return m_magsq > m_threshold ? m_u0 : 1.0;
|
|
||||||
|
if (m_magsq > m_threshold)
|
||||||
|
{
|
||||||
|
m_thresholdCount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_thresholdCount < m_moving_average.historySize()) {
|
||||||
|
m_thresholdCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (m_thresholdCount < m_moving_average.historySize()) ? m_u0 : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//MagAGC::MagAGC() :
|
//MagAGC::MagAGC() :
|
||||||
@ -75,7 +88,8 @@ double MagSquaredAGC::feedAndGetValue(const Complex& ci)
|
|||||||
MagAGC::MagAGC(int historySize, double R, double threshold) :
|
MagAGC::MagAGC(int historySize, double R, double threshold) :
|
||||||
AGC(historySize, R),
|
AGC(historySize, R),
|
||||||
m_magsq(0.0),
|
m_magsq(0.0),
|
||||||
m_threshold(threshold)
|
m_threshold(threshold),
|
||||||
|
m_thresholdCount(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
MagAGC::~MagAGC()
|
MagAGC::~MagAGC()
|
||||||
@ -94,7 +108,19 @@ double MagAGC::feedAndGetValue(const Complex& ci)
|
|||||||
m_magsq = ci.real()*ci.real() + ci.imag()*ci.imag();
|
m_magsq = ci.real()*ci.real() + ci.imag()*ci.imag();
|
||||||
m_moving_average.feed(m_magsq);
|
m_moving_average.feed(m_magsq);
|
||||||
m_u0 = m_R / sqrt(m_moving_average.average());
|
m_u0 = m_R / sqrt(m_moving_average.average());
|
||||||
return m_magsq > m_threshold ? m_u0 : 1.0;
|
|
||||||
|
if (m_magsq > m_threshold)
|
||||||
|
{
|
||||||
|
m_thresholdCount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_thresholdCount < m_moving_average.historySize()) {
|
||||||
|
m_thresholdCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (m_thresholdCount < m_moving_average.historySize()) ? m_u0 : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//AlphaAGC::AlphaAGC() :
|
//AlphaAGC::AlphaAGC() :
|
||||||
|
@ -37,9 +37,11 @@ public:
|
|||||||
virtual void feed(Complex& ci);
|
virtual void feed(Complex& ci);
|
||||||
double feedAndGetValue(const Complex& ci);
|
double feedAndGetValue(const Complex& ci);
|
||||||
double getMagSq() const { return m_magsq; }
|
double getMagSq() const { return m_magsq; }
|
||||||
|
void setThreshold(double threshold) { m_threshold = threshold; }
|
||||||
private:
|
private:
|
||||||
double m_magsq;
|
double m_magsq;
|
||||||
double m_threshold;
|
double m_threshold; //!< squelch on magsq average with transition from +3dB
|
||||||
|
int m_thresholdCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MagAGC : public AGC
|
class MagAGC : public AGC
|
||||||
@ -50,9 +52,11 @@ public:
|
|||||||
virtual void feed(Complex& ci);
|
virtual void feed(Complex& ci);
|
||||||
double feedAndGetValue(const Complex& ci);
|
double feedAndGetValue(const Complex& ci);
|
||||||
Real getMagSq() const { return m_magsq; }
|
Real getMagSq() const { return m_magsq; }
|
||||||
|
void setThreshold(double threshold) { m_threshold = threshold; }
|
||||||
private:
|
private:
|
||||||
double m_magsq;
|
double m_magsq;
|
||||||
double m_threshold;
|
double m_threshold; //!< squelch on magsq average
|
||||||
|
int m_thresholdCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AlphaAGC : public AGC
|
class AlphaAGC : public AGC
|
||||||
|
@ -50,6 +50,11 @@ public:
|
|||||||
return m_sum;
|
return m_sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int historySize() const
|
||||||
|
{
|
||||||
|
return m_history.size();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<Type> m_history;
|
std::vector<Type> m_history;
|
||||||
Type m_sum;
|
Type m_sum;
|
||||||
|
@ -28,3 +28,8 @@ double CalcDb::dbPower(double magsq, double floordB)
|
|||||||
return floordB;
|
return floordB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double CalcDb::powerFromdB(double powerdB)
|
||||||
|
{
|
||||||
|
return pow(10.0, powerdB / 10.0);
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ class CalcDb
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static double dbPower(double magsq, double floordB = -100.0);
|
static double dbPower(double magsq, double floordB = -100.0);
|
||||||
|
static double powerFromdB(double powerdB);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* INCLUDE_UTIL_DB_H_ */
|
#endif /* INCLUDE_UTIL_DB_H_ */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user