1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-16 05:11:49 -05:00

SSM modulator: added audio compressor

This commit is contained in:
f4exb 2017-08-05 19:08:33 +02:00
parent 0f9c8d17a5
commit d8f7dcebdc
8 changed files with 479 additions and 27 deletions

View File

@ -325,6 +325,7 @@ bool SSBDemod::handleMessage(const Message& cmd)
if (m_agcNbSamples != agcNbSamples) if (m_agcNbSamples != agcNbSamples)
{ {
m_agc.resize(agcNbSamples, agcTarget); m_agc.resize(agcNbSamples, agcTarget);
m_agc.setStepDownDelay(agcNbSamples);
m_agcNbSamples = agcNbSamples; m_agcNbSamples = agcNbSamples;
} }

View File

@ -24,6 +24,7 @@
#include <dsp/upchannelizer.h> #include <dsp/upchannelizer.h>
#include "dsp/dspengine.h" #include "dsp/dspengine.h"
#include "dsp/pidcontroller.h" #include "dsp/pidcontroller.h"
#include "util/db.h"
MESSAGE_CLASS_DEFINITION(SSBMod::MsgConfigureSSBMod, Message) MESSAGE_CLASS_DEFINITION(SSBMod::MsgConfigureSSBMod, Message)
MESSAGE_CLASS_DEFINITION(SSBMod::MsgConfigureFileSourceName, Message) MESSAGE_CLASS_DEFINITION(SSBMod::MsgConfigureFileSourceName, Message)
@ -45,7 +46,6 @@ SSBMod::SSBMod(BasebandSampleSink* sampleSink) :
m_DSBFilterBufferIndex(0), m_DSBFilterBufferIndex(0),
m_sampleSink(sampleSink), m_sampleSink(sampleSink),
m_movingAverage(40, 0), m_movingAverage(40, 0),
m_volumeAGC(40, 0),
m_audioFifo(4, 48000), m_audioFifo(4, 48000),
m_settingsMutex(QMutex::Recursive), m_settingsMutex(QMutex::Recursive),
m_fileSize(0), m_fileSize(0),
@ -54,7 +54,8 @@ SSBMod::SSBMod(BasebandSampleSink* sampleSink) :
m_afInput(SSBModInputNone), m_afInput(SSBModInputNone),
m_levelCalcCount(0), m_levelCalcCount(0),
m_peakLevel(0.0f), m_peakLevel(0.0f),
m_levelSum(0.0f) m_levelSum(0.0f),
m_inAGC(9600, 0.2, 1e-4)
{ {
setObjectName("SSBMod"); setObjectName("SSBMod");
@ -84,7 +85,6 @@ SSBMod::SSBMod(BasebandSampleSink* sampleSink) :
m_sumCount = 0; m_sumCount = 0;
m_movingAverage.resize(16, 0); m_movingAverage.resize(16, 0);
m_volumeAGC.resize(4096, 0.003, 0);
m_magsq = 0.0; m_magsq = 0.0;
m_toneNco.setFreq(1000.0, m_config.m_audioSampleRate); m_toneNco.setFreq(1000.0, m_config.m_audioSampleRate);
@ -96,6 +96,9 @@ SSBMod::SSBMod(BasebandSampleSink* sampleSink) :
m_cwKeyer.setMode(CWKeyer::CWNone); m_cwKeyer.setMode(CWKeyer::CWNone);
m_cwSmoother.setNbFadeSamples(192); // 4 ms at 48 kHz m_cwSmoother.setNbFadeSamples(192); // 4 ms at 48 kHz
m_inAGC.setGate(m_config.m_agcThresholdGate);
m_inAGC.setStepDownDelay(m_config.m_agcThresholdDelay);
m_inAGC.setClamping(true);
apply(); apply();
} }
@ -130,7 +133,12 @@ void SSBMod::configure(MessageQueue* messageQueue,
bool audioFlipChannels, bool audioFlipChannels,
bool dsb, bool dsb,
bool audioMute, bool audioMute,
bool playLoop) bool playLoop,
bool agc,
int agcTime,
int agcThreshold,
int agcThresholdGate,
int agcThresholdDelay)
{ {
Message* cmd = MsgConfigureSSBMod::create(bandwidth, Message* cmd = MsgConfigureSSBMod::create(bandwidth,
lowCutoff, lowCutoff,
@ -141,7 +149,12 @@ void SSBMod::configure(MessageQueue* messageQueue,
audioFlipChannels, audioFlipChannels,
dsb, dsb,
audioMute, audioMute,
playLoop); playLoop,
agc,
agcTime,
agcThreshold,
agcThresholdGate,
agcThresholdDelay);
messageQueue->push(cmd); messageQueue->push(cmd);
} }
@ -282,11 +295,22 @@ void SSBMod::pullAF(Complex& sample)
{ {
Real real; Real real;
m_ifstream.read(reinterpret_cast<char*>(&real), sizeof(Real)); m_ifstream.read(reinterpret_cast<char*>(&real), sizeof(Real));
if (m_running.m_agc)
{
ci.real(real);
ci.imag(0.0f);
m_inAGC.feed(ci);
ci *= m_running.m_volumeFactor;
}
else
{
ci.real(real * m_running.m_volumeFactor); ci.real(real * m_running.m_volumeFactor);
ci.imag(0.0f); ci.imag(0.0f);
} }
} }
} }
}
else else
{ {
ci.real(0.0f); ci.real(0.0f);
@ -308,10 +332,20 @@ void SSBMod::pullAF(Complex& sample)
} }
} }
else else
{
if (m_running.m_agc)
{
ci.real(((m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) / 65536.0f));
ci.imag(0.0f);
m_inAGC.feed(ci);
ci *= m_running.m_volumeFactor;
}
else
{ {
ci.real(((m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) / 65536.0f) * m_running.m_volumeFactor); ci.real(((m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) / 65536.0f) * m_running.m_volumeFactor);
ci.imag(0.0f); ci.imag(0.0f);
} }
}
break; break;
case SSBModInputCWTone: case SSBModInputCWTone:
@ -580,6 +614,13 @@ bool SSBMod::handleMessage(const Message& cmd)
m_config.m_dsb = cfg.getDSB(); m_config.m_dsb = cfg.getDSB();
m_config.m_audioMute = cfg.getAudioMute(); m_config.m_audioMute = cfg.getAudioMute();
m_config.m_playLoop = cfg.getPlayLoop(); m_config.m_playLoop = cfg.getPlayLoop();
m_config.m_agc = cfg.getAGC();
m_config.m_agcTime = 48 * cfg.getAGCTime(); // ms
m_config.m_agcThresholdEnable = cfg.getAGCThreshold() != -99;
m_config.m_agcThreshold = CalcDb::powerFromdB(cfg.getAGCThreshold()); // power dB
m_config.m_agcThresholdGate = 48 * cfg.getAGCThresholdGate(); // ms
m_config.m_agcThresholdDelay = 48 * cfg.getAGCThresholdDelay(); // ms
apply(); apply();
@ -595,7 +636,13 @@ bool SSBMod::handleMessage(const Message& cmd)
<< " m_audioFlipChannels: " << m_config.m_audioFlipChannels << " m_audioFlipChannels: " << m_config.m_audioFlipChannels
<< " m_dsb: " << m_config.m_dsb << " m_dsb: " << m_config.m_dsb
<< " m_audioMute: " << m_config.m_audioMute << " m_audioMute: " << m_config.m_audioMute
<< " m_playLoop: " << m_config.m_playLoop; << " m_playLoop: " << m_config.m_playLoop
<< " m_agc: " << m_config.m_agc
<< " m_agcTime: " << m_config.m_agcTime
<< " m_agcThresholdEnable: " << m_config.m_agcThresholdEnable
<< " m_agcThreshold: " << m_config.m_agcThreshold
<< " m_agcThresholdGate: " << m_config.m_agcThresholdGate
<< " m_agcThresholdDelay: " << m_config.m_agcThresholdDelay;
return true; return true;
} }
@ -705,6 +752,31 @@ void SSBMod::apply()
} }
} }
if (m_config.m_agcTime != m_running.m_agcTime)
{
m_inAGC.resize(m_config.m_agcTime, 0.2);
}
if (m_config.m_agcThresholdEnable != m_running.m_agcThresholdEnable)
{
m_inAGC.setThresholdEnable(m_config.m_agcThresholdEnable);
}
if (m_config.m_agcThreshold != m_running.m_agcThreshold)
{
m_inAGC.setThreshold(m_config.m_agcThreshold);
}
if (m_config.m_agcThresholdGate != m_running.m_agcThresholdGate)
{
m_inAGC.setGate(m_config.m_agcThresholdGate);
}
if (m_config.m_agcThresholdDelay != m_running.m_agcThresholdDelay)
{
m_inAGC.setStepDownDelay(m_config.m_agcThresholdDelay);
}
m_running.m_outputSampleRate = m_config.m_outputSampleRate; m_running.m_outputSampleRate = m_config.m_outputSampleRate;
m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset;
m_running.m_bandwidth = m_config.m_bandwidth; m_running.m_bandwidth = m_config.m_bandwidth;
@ -719,6 +791,12 @@ void SSBMod::apply()
m_running.m_dsb = m_config.m_dsb; m_running.m_dsb = m_config.m_dsb;
m_running.m_audioMute = m_config.m_audioMute; m_running.m_audioMute = m_config.m_audioMute;
m_running.m_playLoop = m_config.m_playLoop; m_running.m_playLoop = m_config.m_playLoop;
m_running.m_agc = m_config.m_agc;
m_running.m_agcTime = m_config.m_agcTime;
m_running.m_agcThresholdEnable = m_config.m_agcThresholdEnable;
m_running.m_agcThreshold = m_config.m_agcThreshold;
m_running.m_agcThresholdGate = m_config.m_agcThresholdGate;
m_running.m_agcThresholdDelay = m_config.m_agcThresholdDelay;
} }
void SSBMod::openFileStream() void SSBMod::openFileStream()

View File

@ -187,7 +187,12 @@ public:
bool audioFlipChannels, bool audioFlipChannels,
bool dsb, bool dsb,
bool audioMute, bool audioMute,
bool playLoop); bool playLoop,
bool agc,
int agcTime,
int agcThreshold,
int agcThresholdGate,
int agcThresholdDelay);
virtual void pull(Sample& sample); virtual void pull(Sample& sample);
virtual void pullAudio(int nbSamples); virtual void pullAudio(int nbSamples);
@ -225,6 +230,11 @@ private:
bool getDSB() const { return m_dsb; } bool getDSB() const { return m_dsb; }
bool getAudioMute() const { return m_audioMute; } bool getAudioMute() const { return m_audioMute; }
bool getPlayLoop() const { return m_playLoop; } bool getPlayLoop() const { return m_playLoop; }
bool getAGC() const { return m_agc; }
int getAGCTime() const { return m_agcTime; }
int getAGCThreshold() const { return m_agcThreshold; }
int getAGCThresholdGate() const { return m_agcThresholdGate; }
int getAGCThresholdDelay() const { return m_agcThresholdDelay; }
static MsgConfigureSSBMod* create(Real bandwidth, static MsgConfigureSSBMod* create(Real bandwidth,
Real lowCutoff, Real lowCutoff,
@ -235,7 +245,12 @@ private:
bool audioFlipChannels, bool audioFlipChannels,
bool dsb, bool dsb,
bool audioMute, bool audioMute,
bool playLoop) bool playLoop,
bool agc,
int agcTime,
int agcThreshold,
int agcThresholdGate,
int agcThresholdDelay)
{ {
return new MsgConfigureSSBMod(bandwidth, return new MsgConfigureSSBMod(bandwidth,
lowCutoff, lowCutoff,
@ -246,7 +261,12 @@ private:
audioFlipChannels, audioFlipChannels,
dsb, dsb,
audioMute, audioMute,
playLoop); playLoop,
agc,
agcTime,
agcThreshold,
agcThresholdGate,
agcThresholdDelay);
} }
private: private:
@ -260,6 +280,11 @@ private:
bool m_dsb; bool m_dsb;
bool m_audioMute; bool m_audioMute;
bool m_playLoop; bool m_playLoop;
bool m_agc;
int m_agcTime;
int m_agcThreshold;
int m_agcThresholdGate;
int m_agcThresholdDelay;
MsgConfigureSSBMod(Real bandwidth, MsgConfigureSSBMod(Real bandwidth,
Real lowCutoff, Real lowCutoff,
@ -270,7 +295,12 @@ private:
bool audioFlipChannels, bool audioFlipChannels,
bool dsb, bool dsb,
bool audioMute, bool audioMute,
bool playLoop) : bool playLoop,
bool agc,
int agcTime,
int agcThreshold,
int agcThresholdGate,
int agcThresholdDelay) :
Message(), Message(),
m_bandwidth(bandwidth), m_bandwidth(bandwidth),
m_lowCutoff(lowCutoff), m_lowCutoff(lowCutoff),
@ -281,7 +311,12 @@ private:
m_audioFlipChannels(audioFlipChannels), m_audioFlipChannels(audioFlipChannels),
m_dsb(dsb), m_dsb(dsb),
m_audioMute(audioMute), m_audioMute(audioMute),
m_playLoop(playLoop) m_playLoop(playLoop),
m_agc(agc),
m_agcTime(agcTime),
m_agcThreshold(agcThreshold),
m_agcThresholdGate(agcThresholdGate),
m_agcThresholdDelay(agcThresholdDelay)
{ } { }
}; };
@ -313,6 +348,12 @@ private:
bool m_dsb; bool m_dsb;
bool m_audioMute; bool m_audioMute;
bool m_playLoop; bool m_playLoop;
bool m_agc;
int m_agcTime;
bool m_agcThresholdEnable;
double m_agcThreshold;
int m_agcThresholdGate;
int m_agcThresholdDelay;
Config() : Config() :
m_outputSampleRate(0), m_outputSampleRate(0),
@ -328,7 +369,13 @@ private:
m_audioFlipChannels(false), m_audioFlipChannels(false),
m_dsb(false), m_dsb(false),
m_audioMute(false), m_audioMute(false),
m_playLoop(false) m_playLoop(false),
m_agc(false),
m_agcTime(9600),
m_agcThresholdEnable(true),
m_agcThreshold(1e-4),
m_agcThresholdGate(192),
m_agcThresholdDelay(2400)
{ } { }
}; };
@ -365,7 +412,6 @@ private:
double m_magsq; double m_magsq;
MovingAverage<double> m_movingAverage; MovingAverage<double> m_movingAverage;
SimpleAGC m_volumeAGC;
AudioVector m_audioBuffer; AudioVector m_audioBuffer;
uint m_audioBufferFill; uint m_audioBufferFill;
@ -386,6 +432,8 @@ private:
CWKeyer m_cwKeyer; CWKeyer m_cwKeyer;
CWSmoother m_cwSmoother; CWSmoother m_cwSmoother;
MagAGC m_inAGC;
static const int m_levelNbSamples; static const int m_levelNbSamples;
void apply(); void apply();

View File

@ -36,6 +36,18 @@
const QString SSBModGUI::m_channelID = "sdrangel.channeltx.modssb"; const QString SSBModGUI::m_channelID = "sdrangel.channeltx.modssb";
const int SSBModGUI::m_agcTimeConstant[] = {
1,
2,
5,
10,
20,
50,
100,
200,
500,
990};
SSBModGUI* SSBModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI) SSBModGUI* SSBModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI)
{ {
SSBModGUI* gui = new SSBModGUI(pluginAPI, deviceAPI); SSBModGUI* gui = new SSBModGUI(pluginAPI, deviceAPI);
@ -105,6 +117,11 @@ QByteArray SSBModGUI::serialize() const
s.writeBool(9, ui->audioBinaural->isChecked()); s.writeBool(9, ui->audioBinaural->isChecked());
s.writeBool(10, ui->audioFlipChannels->isChecked()); s.writeBool(10, ui->audioFlipChannels->isChecked());
s.writeBool(11, ui->dsb->isChecked()); s.writeBool(11, ui->dsb->isChecked());
s.writeBool(12, ui->agc->isChecked());
s.writeS32(13, ui->agcTime->value());
s.writeS32(14, ui->agcThreshold->value());
s.writeS32(15, ui->agcThresholdGate->value());
s.writeS32(16, ui->agcThresholdDelay->value());
return s.final(); return s.final();
} }
@ -158,6 +175,18 @@ bool SSBModGUI::deserialize(const QByteArray& data)
ui->audioFlipChannels->setChecked(booltmp); ui->audioFlipChannels->setChecked(booltmp);
d.readBool(11, &booltmp); d.readBool(11, &booltmp);
ui->dsb->setChecked(booltmp); ui->dsb->setChecked(booltmp);
d.readBool(12, &booltmp, false);
ui->agc->setChecked(booltmp);
d.readS32(13, &tmp, 7);
ui->agcTime->setValue(tmp > 9 ? 9 : tmp);
d.readS32(14, &tmp, -40);
ui->agcThreshold->setValue(tmp);
d.readS32(15, &tmp, 4);
ui->agcThresholdGate->setValue(tmp);
d.readS32(16, &tmp, 5);
ui->agcThresholdDelay->setValue(tmp);
displaySettings();
blockApplySettings(false); blockApplySettings(false);
m_channelMarker.blockSignals(false); m_channelMarker.blockSignals(false);
@ -399,6 +428,37 @@ void SSBModGUI::on_mic_toggled(bool checked)
m_ssbMod->getInputMessageQueue()->push(message); m_ssbMod->getInputMessageQueue()->push(message);
} }
void SSBModGUI::on_agc_stateChanged(int state __attribute((__unused__)))
{
applySettings();
}
void SSBModGUI::on_agcTime_valueChanged(int value){
QString s = QString::number(m_agcTimeConstant[value], 'f', 0);
ui->agcTimeText->setText(s);
applySettings();
}
void SSBModGUI::on_agcThreshold_valueChanged(int value)
{
displayAGCPowerThreshold(value);
applySettings();
}
void SSBModGUI::on_agcThresholdGate_valueChanged(int value)
{
QString s = QString::number(value, 'f', 0);
ui->agcThresholdGateText->setText(s);
applySettings();
}
void SSBModGUI::on_agcThresholdDelay_valueChanged(int value)
{
QString s = QString::number(value * 10, 'f', 0);
ui->agcThresholdDelayText->setText(s);
applySettings();
}
void SSBModGUI::on_navTimeSlider_valueChanged(int value) void SSBModGUI::on_navTimeSlider_valueChanged(int value)
{ {
if (m_enableNavTime && ((value >= 0) && (value <= 100))) if (m_enableNavTime && ((value >= 0) && (value <= 100)))
@ -507,6 +567,7 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa
ui->cwKeyerGUI->setBuddies(m_ssbMod->getInputMessageQueue(), m_ssbMod->getCWKeyer()); ui->cwKeyerGUI->setBuddies(m_ssbMod->getInputMessageQueue(), m_ssbMod->getCWKeyer());
ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum);
displaySettings();
applySettings(); applySettings();
setNewRate(m_spanLog2); setNewRate(m_spanLog2);
@ -629,7 +690,36 @@ void SSBModGUI::applySettings()
ui->audioFlipChannels->isChecked(), ui->audioFlipChannels->isChecked(),
ui->dsb->isChecked(), ui->dsb->isChecked(),
ui->audioMute->isChecked(), ui->audioMute->isChecked(),
ui->playLoop->isChecked()); ui->playLoop->isChecked(),
ui->agc->isChecked(),
m_agcTimeConstant[ui->agcTime->value()],
ui->agcThreshold->value(),
ui->agcThresholdGate->value(),
ui->agcThresholdDelay->value() * 10);
}
}
void SSBModGUI::displaySettings()
{
QString s = QString::number(m_agcTimeConstant[ui->agcTime->value()], 'f', 0);
ui->agcTimeText->setText(s);
displayAGCPowerThreshold(ui->agcThreshold->value());
s = QString::number(ui->agcThresholdGate->value(), 'f', 0);
ui->agcThresholdGateText->setText(s);
s = QString::number(ui->agcThresholdDelay->value() * 10, 'f', 0);
ui->agcThresholdDelayText->setText(s);
}
void SSBModGUI::displayAGCPowerThreshold(int value)
{
if (value == -99)
{
ui->agcThresholdText->setText("---");
}
else
{
QString s = QString::number(value, 'f', 0);
ui->agcThresholdText->setText(s);
} }
} }

View File

@ -71,6 +71,11 @@ private slots:
void on_tone_toggled(bool checked); void on_tone_toggled(bool checked);
void on_toneFrequency_valueChanged(int value); void on_toneFrequency_valueChanged(int value);
void on_mic_toggled(bool checked); void on_mic_toggled(bool checked);
void on_agc_stateChanged(int state);
void on_agcTime_valueChanged(int value);
void on_agcThreshold_valueChanged(int value);
void on_agcThresholdGate_valueChanged(int value);
void on_agcThresholdDelay_valueChanged(int value);
void on_play_toggled(bool checked); void on_play_toggled(bool checked);
void on_playLoop_toggled(bool checked); void on_playLoop_toggled(bool checked);
void on_morseKeyer_toggled(bool checked); void on_morseKeyer_toggled(bool checked);
@ -107,6 +112,7 @@ private:
std::size_t m_tickCount; std::size_t m_tickCount;
bool m_enableNavTime; bool m_enableNavTime;
SSBMod::SSBModInputAF m_modAFInput; SSBMod::SSBModInputAF m_modAFInput;
static const int m_agcTimeConstant[]; //!< time constant index to value in ms
explicit SSBModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); explicit SSBModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL);
virtual ~SSBModGUI(); virtual ~SSBModGUI();
@ -116,6 +122,8 @@ private:
void blockApplySettings(bool block); void blockApplySettings(bool block);
void applySettings(); void applySettings();
void displaySettings();
void displayAGCPowerThreshold(int value);
void updateWithStreamData(); void updateWithStreamData();
void updateWithStreamTime(); void updateWithStreamTime();

View File

@ -463,6 +463,204 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_agc">
<item>
<widget class="QCheckBox" name="agc">
<property name="toolTip">
<string>Toggle audio compressor</string>
</property>
<property name="text">
<string>Cmp</string>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="agcTime">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Compressor time constant (attack)</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>9</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="agcTimeText">
<property name="minimumSize">
<size>
<width>25</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Compressor time constant in ms</string>
</property>
<property name="text">
<string>000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="agcThreshold">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Audio squelch threshold</string>
</property>
<property name="minimum">
<number>-99</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>-40</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="agcThresholdText">
<property name="minimumSize">
<size>
<width>14</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Audio squelch threshold (dB power)</string>
</property>
<property name="text">
<string>-00</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="agcThresholdGate">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Audio squelch gate</string>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="agcThresholdGateText">
<property name="minimumSize">
<size>
<width>18</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Audio squelch gate in ms</string>
</property>
<property name="text">
<string>00</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="agcThresholdDelay">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Audio squelch delay (release)</string>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="agcThresholdDelayText">
<property name="minimumSize">
<size>
<width>25</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Audio squelch delay in ms</string>
</property>
<property name="text">
<string>000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="inputSelectLayout"> <layout class="QHBoxLayout" name="inputSelectLayout">
<item> <item>

View File

@ -53,7 +53,11 @@ MagAGC::MagAGC(int historySize, double R, double threshold) :
m_stepDelta(1.0/m_stepLength), m_stepDelta(1.0/m_stepLength),
m_stepUpCounter(0), m_stepUpCounter(0),
m_stepDownCounter(m_stepLength), m_stepDownCounter(m_stepLength),
m_gateCounter(0) m_gateCounter(0),
m_stepDownDelay(historySize),
m_clamping(false),
m_R2(R*R),
m_clampMax(1.0)
{} {}
MagAGC::~MagAGC() MagAGC::~MagAGC()
@ -61,6 +65,7 @@ MagAGC::~MagAGC()
void MagAGC::resize(int historySize, Real R) void MagAGC::resize(int historySize, Real R)
{ {
m_R2 = R*R;
m_stepLength = std::min(StepLengthMax, historySize/2); m_stepLength = std::min(StepLengthMax, historySize/2);
m_stepDelta = 1.0 / m_stepLength; m_stepDelta = 1.0 / m_stepLength;
m_stepUpCounter = 0; m_stepUpCounter = 0;
@ -88,7 +93,24 @@ 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);
if (m_clamping)
{
if (m_squared)
{
double u0 = m_R / m_moving_average.average();
m_u0 = (u0 * m_magsq > m_clampMax) ? m_clampMax / m_magsq : u0;
}
else
{
double u02 = m_R2 / m_moving_average.average();
m_u0 = (u02 * m_magsq > m_clampMax) ? m_clampMax / sqrt(m_magsq) : sqrt(u02);
}
}
else
{
m_u0 = m_R / (m_squared ? m_moving_average.average() : sqrt(m_moving_average.average())); m_u0 = m_R / (m_squared ? m_moving_average.average() : sqrt(m_moving_average.average()));
}
if (m_thresholdEnable) if (m_thresholdEnable)
{ {
@ -105,14 +127,14 @@ double MagAGC::feedAndGetValue(const Complex& ci)
} }
else else
{ {
if (m_count < m_moving_average.historySize()) { if (m_count < m_stepDownDelay) {
m_count++; m_count++;
} }
m_gateCounter = 0; m_gateCounter = 0;
} }
if (m_count < m_moving_average.historySize()) if (m_count < m_stepDownDelay)
{ {
m_stepDownCounter = m_stepUpCounter; m_stepDownCounter = m_stepUpCounter;

View File

@ -22,11 +22,11 @@ public:
virtual void feed(Complex& ci) = 0; virtual void feed(Complex& ci) = 0;
protected: protected:
double m_u0; double m_u0; //!< AGC factor
double m_R; // objective mag double m_R; //!< objective mag
MovingAverage<double> m_moving_average; // Averaging engine. The stack length conditions the smoothness of AGC. MovingAverage<double> m_moving_average; //!< Averaging engine. The stack length conditions the smoothness of AGC.
int m_historySize; int m_historySize; //!< Averaging length (attack)
int m_count; int m_count; //!< Samples counter
}; };
@ -43,6 +43,9 @@ public:
void setThreshold(double threshold) { m_threshold = threshold; } void setThreshold(double threshold) { m_threshold = threshold; }
void setThresholdEnable(bool enable); void setThresholdEnable(bool enable);
void setGate(int gate) { m_gate = gate; } void setGate(int gate) { m_gate = gate; }
void setStepDownDelay(int stepDownDelay) { m_stepDownDelay = stepDownDelay; }
void setClamping(bool clamping) { m_clamping = clamping; }
void setClampMax(double clampMax) { m_clampMax = clampMax; }
private: private:
bool m_squared; //!< use squared magnitude (power) to compute AGC value bool m_squared; //!< use squared magnitude (power) to compute AGC value
double m_magsq; //!< current squared magnitude (power) double m_magsq; //!< current squared magnitude (power)
@ -54,6 +57,10 @@ private:
int m_stepUpCounter; //!< step up transition samples counter int m_stepUpCounter; //!< step up transition samples counter
int m_stepDownCounter; //!< step down transition samples counter int m_stepDownCounter; //!< step down transition samples counter
int m_gateCounter; //!< threshold gate samples counter int m_gateCounter; //!< threshold gate samples counter
int m_stepDownDelay; //!< delay in samples before cutoff (release)
bool m_clamping; //!< clamping active
double m_R2; //!< square of objective magnitude
double m_clampMax; //!< maximum to clamp to
}; };