mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-05-23 18:52:28 -04:00
SSB demod: reworked AGC to handle the threshold gate properly
This commit is contained in:
parent
2cefa0ed69
commit
fb0ec4a680
@ -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 ± 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.
|
||||||
|
|
||||||
<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.
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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*);
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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; }
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user