SSB demod: reworked AGC to handle the threshold gate properly

pull/375/head
f4exb 2019-06-09 09:25:18 +02:00
rodzic 2cefa0ed69
commit fb0ec4a680
7 zmienionych plików z 60 dodań i 57 usunięć

Wyświetl plik

@ -2,7 +2,7 @@
<h2>Introduction</h2> <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> <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 - 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 - 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> <h3>4: Invert left and right channels</h3>
Inverts left and right audio channels. Useful in binaural mode only. 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. 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 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> <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. 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. When the power threshold is close to the noise floor a few milliseconds help in preventing noise power wiggle to open the squelch.

Wyświetl plik

@ -202,9 +202,10 @@ void SSBDemodGUI::on_agcPowerThreshold_valueChanged(int value)
void SSBDemodGUI::on_agcThresholdGate_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); ui->agcThresholdGateText->setText(s);
m_settings.m_agcThresholdGate = value; m_settings.m_agcThresholdGate = agcThresholdGate;
applySettings(); applySettings();
} }
@ -565,10 +566,7 @@ void SSBDemodGUI::displaySettings()
ui->agcPowerThreshold->setValue(m_settings.m_agcPowerThreshold); ui->agcPowerThreshold->setValue(m_settings.m_agcPowerThreshold);
displayAGCPowerThreshold(ui->agcPowerThreshold->value()); displayAGCPowerThreshold(ui->agcPowerThreshold->value());
displayAGCThresholdGate(m_settings.m_agcThresholdGate);
ui->agcThresholdGate->setValue(m_settings.m_agcThresholdGate);
s = QString::number(ui->agcThresholdGate->value(), 'f', 0);
ui->agcThresholdGateText->setText(s);
blockApplySettings(false); 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*) void SSBDemodGUI::leaveEvent(QEvent*)
{ {
m_channelMarker.setHighlighted(false); m_channelMarker.setHighlighted(false);

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -46,12 +46,11 @@ public:
double getMagSq() const { return m_magsq; } double getMagSq() const { return m_magsq; }
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; m_gateCounter = 0; m_count = 0; }
void setStepDownDelay(int stepDownDelay) { m_stepDownDelay = stepDownDelay; } void setStepDownDelay(int stepDownDelay) { m_stepDownDelay = stepDownDelay; m_gateCounter = 0; m_count = 0; }
void setClamping(bool clamping) { m_clamping = clamping; } void setClamping(bool clamping) { m_clamping = clamping; }
void setClampMax(double clampMax) { m_clampMax = clampMax; } void setClampMax(double clampMax) { m_clampMax = clampMax; }
int getStepDownDelay() const { return m_stepDownDelay; } int getStepDownDelay() const { return m_stepDownDelay; }
float getStepDownValue() const;
float getStepValue() const; float getStepValue() const;
void setHardLimiting(bool hardLimiting) { m_hardLimiting = hardLimiting; } void setHardLimiting(bool hardLimiting) { m_hardLimiting = hardLimiting; }