SSB demod: reworked AGC to handle the threshold gate properly

This commit is contained in:
f4exb 2019-06-09 09:25:18 +02:00
parent 2cefa0ed69
commit fb0ec4a680
7 changed files with 60 additions and 57 deletions

View File

@ -2,7 +2,7 @@
<h2>Introduction</h2>
This plugin can be used to listen to a single sideband or double sidebands modulated signal.
This plugin can be used to listen to a single sideband or double sidebands modulated signal.
<h2>Interface</h2>
@ -24,7 +24,7 @@ Average total power in dB relative to a +/- 1.0 amplitude signal received in the
- Monaural: the scalar signal is routed to both left and right audio channels
- Binaural: the complex signal is fed with the real part on the left audio channel and the imaginary part to the right audio channel
<h3>4: Invert left and right channels</h3>
Inverts left and right audio channels. Useful in binaural mode only.
@ -91,7 +91,7 @@ This is how the Span (8) and bandpass (9, 10) filter controls look like in the 3
Values are expressed in kHz and step is 100 Hz.
- In SSB mode this is the upper (USB: positive frequencies) or lower (LSB: negative frequencies) cutoff of the in channel single side band bandpass filter. The value triggers LSB mode when negative and USB when positive
- In DSB mode this is half the bandwidth of the double side band in channel bandpass filter therefore the value is prefixed with the &#177; sign.
- In DSB mode this is half the bandwidth of the double side band in channel bandpass filter therefore the value is prefixed with the &#177; sign.
<h3>10: "Low cut": In channel bandpass filter cutoff frequency closest to zero</h3>
@ -140,7 +140,7 @@ The signal power is calculated as the moving average over the AGC time constant
Active only in AGC mode with squelch enabled.
To avoid unwanted squelch opening on short transient bursts only signals with power above threshold during this period in milliseconds will open the squelch.It can be varied from 0 to 20 ms in 1 ms steps.
To avoid unwanted squelch opening on short transient bursts only signals with power above threshold during this period in milliseconds will open the squelch.It can be varied from 0 to 20 ms in 1 ms steps then from 30 to 500 ms in 10 ms steps.
When the power threshold is close to the noise floor a few milliseconds help in preventing noise power wiggle to open the squelch.

View File

@ -202,9 +202,10 @@ void SSBDemodGUI::on_agcPowerThreshold_valueChanged(int value)
void SSBDemodGUI::on_agcThresholdGate_valueChanged(int value)
{
QString s = QString::number(value, 'f', 0);
int agcThresholdGate = value < 20 ? value : ((value - 20) * 10) + 20;
QString s = QString::number(agcThresholdGate, 'f', 0);
ui->agcThresholdGateText->setText(s);
m_settings.m_agcThresholdGate = value;
m_settings.m_agcThresholdGate = agcThresholdGate;
applySettings();
}
@ -565,10 +566,7 @@ void SSBDemodGUI::displaySettings()
ui->agcPowerThreshold->setValue(m_settings.m_agcPowerThreshold);
displayAGCPowerThreshold(ui->agcPowerThreshold->value());
ui->agcThresholdGate->setValue(m_settings.m_agcThresholdGate);
s = QString::number(ui->agcThresholdGate->value(), 'f', 0);
ui->agcThresholdGateText->setText(s);
displayAGCThresholdGate(m_settings.m_agcThresholdGate);
blockApplySettings(false);
}
@ -586,6 +584,19 @@ void SSBDemodGUI::displayAGCPowerThreshold(int value)
}
}
void SSBDemodGUI::displayAGCThresholdGate(int value)
{
QString s = QString::number(value, 'f', 0);
ui->agcThresholdGateText->setText(s);
int dialValue = value;
if (value > 20) {
dialValue = ((value - 20) / 10) + 20;
}
ui->agcThresholdGate->setValue(dialValue);
}
void SSBDemodGUI::leaveEvent(QEvent*)
{
m_channelMarker.setHighlighted(false);

View File

@ -73,8 +73,8 @@ private:
void applyBandwidths(int spanLog2, bool force = false);
int spanLog2Limit(int spanLog2);
void displaySettings();
void displayAGCPowerThreshold(int value);
void displayAGCThresholdGate(int value);
void leaveEvent(QEvent*);
void enterEvent(QEvent*);

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>412</width>
<width>414</width>
<height>190</height>
</rect>
</property>
@ -18,7 +18,7 @@
</property>
<property name="minimumSize">
<size>
<width>412</width>
<width>414</width>
<height>0</height>
</size>
</property>
@ -36,13 +36,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>410</width>
<width>415</width>
<height>171</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>410</width>
<width>415</width>
<height>0</height>
</size>
</property>
@ -836,7 +836,7 @@
<string>Power threshold gate (ms)</string>
</property>
<property name="maximum">
<number>20</number>
<number>68</number>
</property>
<property name="pageStep">
<number>1</number>
@ -850,7 +850,7 @@
<widget class="QLabel" name="agcThresholdGateText">
<property name="minimumSize">
<size>
<width>16</width>
<width>22</width>
<height>0</height>
</size>
</property>
@ -858,7 +858,7 @@
<string>Power threshold gate (ms)</string>
</property>
<property name="text">
<string>00</string>
<string>000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>

View File

@ -9,7 +9,7 @@
const PluginDescriptor SSBPlugin::m_pluginDescriptor = {
QString("SSB Demodulator"),
QString("4.8.2"),
QString("4.10.0"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,

View File

@ -50,7 +50,7 @@ MagAGC::MagAGC(int historySize, double R, double threshold) :
m_stepLength(std::min(2400, historySize/2)), // max 50 ms (at 48 kHz)
m_stepDelta(1.0/m_stepLength),
m_stepUpCounter(0),
m_stepDownCounter(m_stepLength),
m_stepDownCounter(0),
m_gateCounter(0),
m_stepDownDelay(historySize),
m_clamping(false),
@ -68,7 +68,7 @@ void MagAGC::resize(int historySize, int stepLength, Real R)
m_stepLength = stepLength;
m_stepDelta = 1.0 / m_stepLength;
m_stepUpCounter = 0;
m_stepDownCounter = m_stepLength;
m_stepDownCounter = 0;
AGC::resize(historySize, R);
m_moving_average.fill(0);
}
@ -85,7 +85,7 @@ void MagAGC::setThresholdEnable(bool enable)
if (m_thresholdEnable != enable)
{
m_stepUpCounter = 0;
m_stepDownCounter = m_stepLength;
m_stepDownCounter = 0;
}
m_thresholdEnable = enable;
@ -136,50 +136,55 @@ double MagAGC::feedAndGetValue(const Complex& ci)
if (m_thresholdEnable)
{
bool open = false;
if (m_magsq > m_threshold)
{
if (m_gateCounter < m_gate)
{
if (m_gateCounter < m_gate) {
m_gateCounter++;
}
else
{
m_count = 0;
} else {
open = true;
}
}
else
{
if (m_count < m_stepDownDelay) {
m_count++;
}
m_gateCounter = 0;
}
if (m_count < m_stepDownDelay)
if (open)
{
m_stepDownCounter = m_stepUpCounter;
m_count = m_stepDownDelay; // delay before step down (grace delay)
}
else
{
m_count--;
m_gateCounter = m_gate; // keep gate open during grace
}
if (m_stepUpCounter < m_stepLength)
if (m_count > 0) // up phase
{
m_stepDownCounter = m_stepUpCounter; // prepare for step down
if (m_stepUpCounter < m_stepLength) // step up
{
m_stepUpCounter++;
return hardLimiter(m_u0 * StepFunctions::smootherstep(m_stepUpCounter * m_stepDelta), m_magsq);
}
else
else // steady open
{
return hardLimiter(m_u0, m_magsq);
}
}
else
else // down phase
{
m_stepUpCounter = m_stepDownCounter;
m_stepUpCounter = m_stepDownCounter; // prepare for step up
if (m_stepDownCounter > 0)
if (m_stepDownCounter > 0) // step down
{
m_stepDownCounter--;
return hardLimiter(m_u0 * StepFunctions::smootherstep(m_stepDownCounter * m_stepDelta), m_magsq);
}
else
else // steady closed
{
return 0.0;
}
@ -191,25 +196,13 @@ double MagAGC::feedAndGetValue(const Complex& ci)
}
}
float MagAGC::getStepDownValue() const
{
if (m_count < m_stepDownDelay)
{
return 1.0f;
}
else
{
return StepFunctions::smootherstep(m_stepDownCounter * m_stepDelta);
}
}
float MagAGC::getStepValue() const
{
if (m_count < m_stepDownDelay)
if (m_count > 0) // up phase
{
return StepFunctions::smootherstep(m_stepUpCounter * m_stepDelta); // step up
}
else
else // down phase
{
return StepFunctions::smootherstep(m_stepDownCounter * m_stepDelta); // step down
}

View File

@ -46,12 +46,11 @@ public:
double getMagSq() const { return m_magsq; }
void setThreshold(double threshold) { m_threshold = threshold; }
void setThresholdEnable(bool enable);
void setGate(int gate) { m_gate = gate; }
void setStepDownDelay(int stepDownDelay) { m_stepDownDelay = stepDownDelay; }
void setGate(int gate) { m_gate = gate; m_gateCounter = 0; m_count = 0; }
void setStepDownDelay(int stepDownDelay) { m_stepDownDelay = stepDownDelay; m_gateCounter = 0; m_count = 0; }
void setClamping(bool clamping) { m_clamping = clamping; }
void setClampMax(double clampMax) { m_clampMax = clampMax; }
int getStepDownDelay() const { return m_stepDownDelay; }
float getStepDownValue() const;
float getStepValue() const;
void setHardLimiting(bool hardLimiting) { m_hardLimiting = hardLimiting; }