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:
parent
0f9c8d17a5
commit
d8f7dcebdc
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user