From fb0ec4a68019f11801a3fdc19bdd0940d3dc1531 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 9 Jun 2019 09:25:18 +0200 Subject: [PATCH] SSB demod: reworked AGC to handle the threshold gate properly --- plugins/channelrx/demodssb/readme.md | 8 +-- plugins/channelrx/demodssb/ssbdemodgui.cpp | 23 +++++--- plugins/channelrx/demodssb/ssbdemodgui.h | 2 +- plugins/channelrx/demodssb/ssbdemodgui.ui | 14 ++--- plugins/channelrx/demodssb/ssbplugin.cpp | 2 +- sdrbase/dsp/agc.cpp | 63 ++++++++++------------ sdrbase/dsp/agc.h | 5 +- 7 files changed, 60 insertions(+), 57 deletions(-) diff --git a/plugins/channelrx/demodssb/readme.md b/plugins/channelrx/demodssb/readme.md index c7ffc2e02..e7ddc5b20 100644 --- a/plugins/channelrx/demodssb/readme.md +++ b/plugins/channelrx/demodssb/readme.md @@ -2,7 +2,7 @@

Introduction

-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.

Interface

@@ -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 - +

4: Invert left and right channels

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 ± 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 ± sign.

10: "Low cut": In channel bandpass filter cutoff frequency closest to zero

@@ -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. diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index 2bd3532e4..58c02f76b 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -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); diff --git a/plugins/channelrx/demodssb/ssbdemodgui.h b/plugins/channelrx/demodssb/ssbdemodgui.h index fceead47a..673e63dad 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.h +++ b/plugins/channelrx/demodssb/ssbdemodgui.h @@ -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*); diff --git a/plugins/channelrx/demodssb/ssbdemodgui.ui b/plugins/channelrx/demodssb/ssbdemodgui.ui index a481293b3..6b29df55c 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.ui +++ b/plugins/channelrx/demodssb/ssbdemodgui.ui @@ -6,7 +6,7 @@ 0 0 - 412 + 414 190 @@ -18,7 +18,7 @@ - 412 + 414 0 @@ -36,13 +36,13 @@ 0 0 - 410 + 415 171 - 410 + 415 0 @@ -836,7 +836,7 @@ Power threshold gate (ms) - 20 + 68 1 @@ -850,7 +850,7 @@ - 16 + 22 0 @@ -858,7 +858,7 @@ Power threshold gate (ms) - 00 + 000 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/plugins/channelrx/demodssb/ssbplugin.cpp b/plugins/channelrx/demodssb/ssbplugin.cpp index 932de22d7..b882a0253 100644 --- a/plugins/channelrx/demodssb/ssbplugin.cpp +++ b/plugins/channelrx/demodssb/ssbplugin.cpp @@ -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, diff --git a/sdrbase/dsp/agc.cpp b/sdrbase/dsp/agc.cpp index c105b3145..d899b111e 100644 --- a/sdrbase/dsp/agc.cpp +++ b/sdrbase/dsp/agc.cpp @@ -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 } diff --git a/sdrbase/dsp/agc.h b/sdrbase/dsp/agc.h index f1f1745b2..7b531a0ae 100644 --- a/sdrbase/dsp/agc.h +++ b/sdrbase/dsp/agc.h @@ -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; }