1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-04-10 13:40:37 -04:00

WDSP Receiver: implemented spectrum handling and AGC

This commit is contained in:
f4exb 2024-06-25 03:51:25 +02:00
parent bc34a759c7
commit dc626ea9bb
3 changed files with 114 additions and 61 deletions

View File

@ -211,7 +211,7 @@ void WDSPRxGUI::on_dnr_toggled(bool)
void WDSPRxGUI::on_agcGain_valueChanged(int value)
{
QString s = QString::number((1<<value), 'f', 0);
QString s = QString::number(value, 'f', 0);
ui->agcGainText->setText(s);
m_settings.m_agcGain = value;
applySettings();
@ -769,11 +769,9 @@ void WDSPRxGUI::dnrSetup(int32_t iValueChanged)
void WDSPRxGUI::tick()
{
double magsqAvg, magsqPeak;
double powDbAvg, powDbPeak;
int nbMagsqSamples;
m_wdspRx->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
double powDbAvg = CalcDb::dbPower(magsqAvg);
double powDbPeak = CalcDb::dbPower(magsqPeak);
m_wdspRx->getMagSqLevels(powDbAvg, powDbPeak, nbMagsqSamples); // powers directly in dB
ui->channelPowerMeter->levelChanged(
(WDSPRxSettings::m_mminPowerThresholdDBf + powDbAvg) / WDSPRxSettings::m_mminPowerThresholdDBf,

View File

@ -34,31 +34,60 @@
#include "wdsprxsink.h"
const int WDSPRxSink::m_ssbFftLen = 2048;
const int WDSPRxSink::m_agcTarget = 3276; // 32768/10 -10 dB amplitude => -20 dB power: center of normal signal
const int WDSPRxSink::m_wdspSampleRate = 48000;
const int WDSPRxSink::m_wdspBufSize = 512;
WDSPRxSink::SpectrumProbe::SpectrumProbe(SampleVector& sampleVector) :
m_sampleVector(sampleVector)
m_sampleVector(sampleVector),
m_spanLog2(0),
m_dsb(false),
m_usb(true),
m_sum(0)
{}
void WDSPRxSink::SpectrumProbe::proceed(const double *in, int nb_samples)
void WDSPRxSink::SpectrumProbe::setSpanLog2(int spanLog2)
{
for (int i = 0; i < nb_samples; i++) {
m_sampleVector.push_back(Sample{static_cast<FixReal>(in[2*i]*SDR_RX_SCALED), static_cast<FixReal>(in[2*i+1]*SDR_RX_SCALED)});
m_spanLog2 = spanLog2;
}
void WDSPRxSink::SpectrumProbe::proceed(const float *in, int nb_samples)
{
int decim = 1<<(m_spanLog2 - 1);
unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
for (int i = 0; i < nb_samples; i++)
{
float cr = in[2*i+1];
float ci = in[2*i];
m_sum += std::complex<float>{cr, ci};
if (decim == 1)
{
m_sampleVector.push_back(Sample(cr*SDR_RX_SCALEF, ci*SDR_RX_SCALEF));
}
else
{
if (!(m_undersampleCount++ & decim_mask))
{
float avgr = m_sum.real() / decim;
float avgi = m_sum.imag() / decim;
if (!m_dsb & !m_usb)
{ // invert spectrum for LSB
m_sampleVector.push_back(Sample(avgi*SDR_RX_SCALEF, avgr*SDR_RX_SCALEF));
}
else
{
m_sampleVector.push_back(Sample(avgr*SDR_RX_SCALEF, avgi*SDR_RX_SCALEF));
}
m_sum = 0;
}
}
}
}
WDSPRxSink::WDSPRxSink() :
m_audioBinaual(false),
m_dsb(false),
m_audioMute(false),
m_agc(12000, m_agcTarget, 1e-2),
m_agcActive(false),
m_agcClamping(false),
m_agcNbSamples(12000),
m_agcPowerThreshold(1e-2),
m_agcThresholdGate(0),
m_squelchDelayLine(2*48000),
m_audioActive(false),
m_spectrumSink(nullptr),
@ -75,12 +104,10 @@ WDSPRxSink::WDSPRxSink() :
m_audioBuffer.resize(m_audioSampleRate / 10);
m_audioBufferFill = 0;
m_undersampleCount = 0;
m_sum = 0;
m_demodBuffer.resize(1<<12);
m_demodBufferFill = 0;
m_usb = true;
m_sAvg = 0.0;
m_sPeak = 0.0;
m_sCount = 1;
@ -144,37 +171,34 @@ void WDSPRxSink::getMagSqLevels(double& avg, double& peak, int& nbSamples)
void WDSPRxSink::processOneSample(Complex &ci)
{
m_rxa->get_inbuff()[2*m_inCount] = ci.real() / SDR_RX_SCALED;
m_rxa->get_inbuff()[2*m_inCount+1] = ci.imag() / SDR_RX_SCALED;
m_rxa->get_inbuff()[2*m_inCount] = ci.imag() / SDR_RX_SCALEF;
m_rxa->get_inbuff()[2*m_inCount+1] = ci.real() / SDR_RX_SCALEF;
if (++m_inCount == m_rxa->get_insize())
{
WDSP::RXA::xrxa(m_rxa);
m_sCount = m_wdspBufSize;
m_sAvg = WDSP::METER::GetMeter(*m_rxa, WDSP::RXA::RXA_S_AV);
m_sPeak = WDSP::METER::GetMeter(*m_rxa, WDSP::RXA::RXA_S_PK);
for (int i = 0; i < m_rxa->get_outsize(); i++)
{
if (i == 0)
{
m_sCount = m_wdspBufSize;
m_sAvg = WDSP::METER::GetMeter(*m_rxa, WDSP::RXA::RXA_S_AV);
m_sPeak = WDSP::METER::GetMeter(*m_rxa, WDSP::RXA::RXA_S_PK);
}
if (m_audioMute)
if (m_settings.m_audioMute)
{
m_audioBuffer[m_audioBufferFill].r = 0;
m_audioBuffer[m_audioBufferFill].l = 0;
}
else
{
const double& cr = m_rxa->get_outbuff()[2*i];
const double& ci = m_rxa->get_outbuff()[2*i+1];
const double& cr = m_rxa->get_outbuff()[2*i+1];
const double& ci = m_rxa->get_outbuff()[2*i];
qint16 zr = cr * 32768.0;
qint16 zi = ci * 32768.0;
m_audioBuffer[m_audioBufferFill].r = zr * m_volume;
m_audioBuffer[m_audioBufferFill].l = zi * m_volume;
if (m_audioBinaual)
if (m_settings.m_audioBinaural)
{
m_demodBuffer[m_demodBufferFill++] = zr * m_volume;
m_demodBuffer[m_demodBufferFill++] = zi * m_volume;
@ -204,7 +228,7 @@ void WDSPRxSink::processOneSample(Complex &ci)
fifo->write(
(quint8*) &m_demodBuffer[0],
m_demodBuffer.size() * sizeof(qint16),
m_audioBinaual ? DataFifo::DataTypeCI16 : DataFifo::DataTypeI16
m_settings.m_audioBinaural ? DataFifo::DataTypeCI16 : DataFifo::DataTypeI16
);
}
}
@ -228,7 +252,7 @@ void WDSPRxSink::processOneSample(Complex &ci)
if (m_spectrumSink && (m_sampleBuffer.size() != 0))
{
m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false);
m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), !m_settings.m_dsb);
m_sampleBuffer.clear();
}
@ -304,7 +328,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
<< " m_lowCutoff: " << settings.m_filterBank[settings.m_filterIndex].m_lowCutoff
<< " m_fftWindow: " << settings.m_filterBank[settings.m_filterIndex].m_fftWindow << "]"
<< " m_volume: " << settings.m_volume
<< " m_audioBinaual: " << settings.m_audioBinaural
<< " m_audioBinaural: " << settings.m_audioBinaural
<< " m_audioFlipChannels: " << settings.m_audioFlipChannels
<< " m_dsb: " << settings.m_dsb
<< " m_audioMute: " << settings.m_audioMute
@ -336,11 +360,11 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
if (band < 0)
{
band = -band;
m_usb = false;
m_spectrumProbe.setUSB(false);
}
else
{
m_usb = true;
m_spectrumProbe.setUSB(true);
}
m_Bandwidth = band;
@ -351,11 +375,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
{
fLow = high;
fHigh = -high;
m_spectrumProbe.setDSB(true);
}
else
{
fLow = high;
fHigh = low;
m_spectrumProbe.setDSB(false);
}
}
else
@ -364,11 +390,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
{
fLow = -high;
fHigh = high;
m_spectrumProbe.setDSB(true);
}
else
{
fLow = low;
fHigh = high;
m_spectrumProbe.setDSB(false);
}
}
@ -381,19 +409,55 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
WDSP::NBP::NBPSetWindow(*m_rxa, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow);
}
if ((m_settings.m_filterBank[settings.m_filterIndex].m_spanLog2 != settings.m_filterBank[settings.m_filterIndex].m_spanLog2) || force) {
m_spectrumProbe.setSpanLog2(settings.m_filterBank[settings.m_filterIndex].m_spanLog2);
}
if ((m_settings.m_agc != settings.m_agc)
|| (m_settings.m_agcMode != settings.m_agcMode)
|| (m_settings.m_agcSlope != settings.m_agcSlope)
|| (m_settings.m_agcHangThreshold != settings.m_agcHangThreshold)
|| (m_settings.m_agcGain != settings.m_agcGain) || force)
{
WDSP::WCPAGC::SetAGCMode(*m_rxa, settings.m_agc ? (int) settings.m_agcMode + 1 : 0); // SetRXAAGCMode(id, agc);
WDSP::WCPAGC::SetAGCSlope(*m_rxa, settings.m_agcSlope); // SetRXAAGCSlope(id, rx->agc_slope);
WDSP::WCPAGC::SetAGCTop(*m_rxa, (float) settings.m_agcGain); // SetRXAAGCTop(id, rx->agc_gain);
if (settings.m_agc)
{
WDSP::WCPAGC::SetAGCMode(*m_rxa, (int) settings.m_agcMode + 1);
WDSP::WCPAGC::SetAGCFixed(*m_rxa, (double) settings.m_agcGain);
switch (settings.m_agcMode)
{
case WDSPRxSettings::AGCMode::AGCLong:
WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2);
WDSP::WCPAGC::SetAGCHang(*m_rxa, 2000); // SetRXAAGCHang(id, 2000);
WDSP::WCPAGC::SetAGCDecay(*m_rxa, 2000); // SetRXAAGCDecay(id, 2000);
WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold);
break;
case WDSPRxSettings::AGCMode::AGCSlow:
WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2);
WDSP::WCPAGC::SetAGCHang(*m_rxa, 1000); // SetRXAAGCHang(id, 1000);
WDSP::WCPAGC::SetAGCDecay(*m_rxa, 500); // SetRXAAGCDecay(id, 500);
WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold);
break;
case WDSPRxSettings::AGCMode::AGCMedium:
WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2);
WDSP::WCPAGC::SetAGCHang(*m_rxa, 0); // SetRXAAGCHang(id, 0);
WDSP::WCPAGC::SetAGCDecay(*m_rxa, 250); // SetRXAAGCDecay(id, 250);
WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100);
break;
case WDSPRxSettings::AGCMode::AGCFast:
WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2);
WDSP::WCPAGC::SetAGCHang(*m_rxa, 0); // SetRXAAGCHang(id, 0);
WDSP::WCPAGC::SetAGCDecay(*m_rxa, 50); // SetRXAAGCDecay(id, 50);
WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100);
break;
}
WDSP::WCPAGC::SetAGCFixed(*m_rxa, 60.0f); // default
}
else
{
WDSP::WCPAGC::SetAGCMode(*m_rxa, 0);
WDSP::WCPAGC::SetAGCTop(*m_rxa, (double) settings.m_agcGain);
WDSP::WCPAGC::SetAGCFixed(*m_rxa, (float) settings.m_agcGain);
}
}
@ -411,9 +475,5 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
WDSP::PANEL::SetPanelCopy(*m_rxa, settings.m_audioFlipChannels ? 3 : 0);
}
m_audioBinaual = settings.m_audioBinaural;
m_dsb = settings.m_dsb;
m_audioMute = settings.m_audioMute;
m_agcActive = settings.m_agc;
m_settings = settings;
}

View File

@ -24,7 +24,6 @@
#include "dsp/ncof.h"
#include "dsp/interpolator.h"
#include "dsp/fftfilt.h"
#include "dsp/agc.h"
#include "dsp/firfilter.h"
#include "audio/audiofifo.h"
#include "util/doublebufferfifo.h"
@ -63,9 +62,17 @@ private:
{
public:
SpectrumProbe(SampleVector& sampleVector);
virtual void proceed(const double *in, int nbSamples);
virtual void proceed(const float *in, int nbSamples);
void setSpanLog2(int spanLog2);
void setDSB(bool dsb) { m_dsb = dsb; }
void setUSB(bool usb) { m_usb = usb; }
private:
SampleVector& m_sampleVector;
int m_spanLog2;
bool m_dsb;
bool m_usb;
uint32_t m_undersampleCount;
std::complex<float> m_sum;
};
struct MagSqLevelsStore
@ -83,23 +90,12 @@ private:
Real m_Bandwidth;
Real m_volume;
fftfilt::cmplx m_sum;
int m_undersampleCount;
int m_channelSampleRate;
int m_channelFrequencyOffset;
bool m_audioBinaual;
bool m_usb;
bool m_dsb;
bool m_audioMute;
double m_sAvg;
double m_sPeak;
int m_sCount;
MagAGC m_agc;
bool m_agcActive;
bool m_agcClamping;
int m_agcNbSamples; //!< number of audio (48 kHz) samples for AGC averaging
double m_agcPowerThreshold; //!< AGC power threshold (linear)
int m_agcThresholdGate; //!< Gate length in number of samples befor threshold triggers
DoubleBufferFIFO<fftfilt::cmplx> m_squelchDelayLine;
bool m_audioActive; //!< True if an audio signal is produced (no AGC or AGC and above threshold)
@ -125,7 +121,6 @@ private:
WDSP::RXA *m_rxa;
static const int m_ssbFftLen;
static const int m_agcTarget;
static const int m_wdspSampleRate;
static const int m_wdspBufSize;