From b2303b028c51fb4a18d934f17582b3c2255e286e Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 24 Jun 2024 10:20:14 +0200 Subject: [PATCH] WDSP Rx: first basic implementation --- plugins/channelrx/wdsprx/wdsprx.cpp | 122 +--- plugins/channelrx/wdsprx/wdsprxgui.cpp | 104 +--- plugins/channelrx/wdsprx/wdsprxgui.h | 9 +- plugins/channelrx/wdsprx/wdsprxgui.ui | 192 +----- plugins/channelrx/wdsprx/wdsprxsettings.cpp | 53 +- plugins/channelrx/wdsprx/wdsprxsettings.h | 26 +- plugins/channelrx/wdsprx/wdsprxsink.cpp | 407 +++++------- plugins/channelrx/wdsprx/wdsprxsink.h | 52 +- wdsp/CMakeLists.txt | 2 +- wdsp/RXA.cpp | 657 ++++++++++---------- wdsp/RXA.hpp | 4 +- wdsp/bufferprobe.hpp | 39 ++ wdsp/meter.cpp | 38 +- wdsp/meter.hpp | 14 +- wdsp/sender.cpp | 40 +- wdsp/sender.hpp | 16 +- 16 files changed, 672 insertions(+), 1103 deletions(-) create mode 100644 wdsp/bufferprobe.hpp diff --git a/plugins/channelrx/wdsprx/wdsprx.cpp b/plugins/channelrx/wdsprx/wdsprx.cpp index 7c54e7569..0f1149e40 100644 --- a/plugins/channelrx/wdsprx/wdsprx.cpp +++ b/plugins/channelrx/wdsprx/wdsprx.cpp @@ -251,7 +251,7 @@ void WDSPRx::applySettings(const WDSPRxSettings& settings, bool force) << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset << " m_filterIndex: " << settings.m_filterIndex << " [m_spanLog2: " << settings.m_filterBank[settings.m_filterIndex].m_spanLog2 - << " m_rfBandwidth: " << settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth + << " m_highCutoff: " << settings.m_filterBank[settings.m_filterIndex].m_highCutoff << " 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 @@ -260,16 +260,10 @@ void WDSPRx::applySettings(const WDSPRxSettings& settings, bool force) << " m_dsb: " << settings.m_dsb << " m_audioMute: " << settings.m_audioMute << " m_agcActive: " << settings.m_agc - << " m_agcClamping: " << settings.m_agcClamping - << " m_agcTimeLog2: " << settings.m_agcTimeLog2 - << " agcPowerThreshold: " << settings.m_agcPowerThreshold - << " agcThresholdGate: " << settings.m_agcThresholdGate - << " m_dnr: " << settings.m_dnr - << " m_dnrScheme: " << settings.m_dnrScheme - << " m_dnrAboveAvgFactor: " << settings.m_dnrAboveAvgFactor - << " m_dnrSigmaFactor: " << settings.m_dnrSigmaFactor - << " m_dnrNbPeaks: " << settings.m_dnrNbPeaks - << " m_dnrAlpha: " << settings.m_dnrAlpha + << " m_agcMode: " << settings.m_agcMode + << " m_agcGain: " << settings.m_agcGain + << " m_agcSlope: " << settings.m_agcSlope + << " m_agcHangThreshold: " << settings.m_agcHangThreshold << " m_audioDeviceName: " << settings.m_audioDeviceName << " m_streamIndex: " << settings.m_streamIndex << " m_useReverseAPI: " << settings.m_useReverseAPI @@ -290,7 +284,7 @@ void WDSPRx::applySettings(const WDSPRxSettings& settings, bool force) if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2 != settings.m_filterBank[settings.m_filterIndex].m_spanLog2) || force) { reverseAPIKeys.append("spanLog2"); } - if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth != settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth) || force) { + if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_highCutoff != settings.m_filterBank[settings.m_filterIndex].m_highCutoff) || force) { reverseAPIKeys.append("rfBandwidth"); } if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff != settings.m_filterBank[settings.m_filterIndex].m_lowCutoff) || force) { @@ -302,18 +296,6 @@ void WDSPRx::applySettings(const WDSPRxSettings& settings, bool force) if ((m_settings.m_volume != settings.m_volume) || force) { reverseAPIKeys.append("volume"); } - if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) || force) { - reverseAPIKeys.append("agcTimeLog2"); - } - if ((m_settings.m_agcPowerThreshold != settings.m_agcPowerThreshold) || force) { - reverseAPIKeys.append("agcPowerThreshold"); - } - if ((m_settings.m_agcThresholdGate != settings.m_agcThresholdGate) || force) { - reverseAPIKeys.append("agcThresholdGate"); - } - if ((m_settings.m_agcClamping != settings.m_agcClamping) || force) { - reverseAPIKeys.append("agcClamping"); - } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { reverseAPIKeys.append("audioDeviceName"); } @@ -332,24 +314,6 @@ void WDSPRx::applySettings(const WDSPRxSettings& settings, bool force) if ((m_settings.m_agc != settings.m_agc) || force) { reverseAPIKeys.append("agc"); } - if ((m_settings.m_dnr != settings.m_dnr) || force) { - reverseAPIKeys.append("dnr"); - } - if ((m_settings.m_dnrScheme != settings.m_dnrScheme) || force) { - reverseAPIKeys.append("dnrScheme"); - } - if ((m_settings.m_dnrAboveAvgFactor != settings.m_dnrAboveAvgFactor) || force) { - reverseAPIKeys.append("dnrAboveAvgFactor"); - } - if ((m_settings.m_dnrSigmaFactor != settings.m_dnrSigmaFactor) || force) { - reverseAPIKeys.append("dnrSigmaFactor"); - } - if ((m_settings.m_dnrNbPeaks != settings.m_dnrNbPeaks) || force) { - reverseAPIKeys.append("dnrNbPeaks"); - } - if ((m_settings.m_dnrAlpha != settings.m_dnrAlpha) || force) { - reverseAPIKeys.append("dnrAlpha"); - } if (m_settings.m_streamIndex != settings.m_streamIndex) { @@ -367,12 +331,12 @@ void WDSPRx::applySettings(const WDSPRxSettings& settings, bool force) } if ((settings.m_dsb != m_settings.m_dsb) - || (settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth != m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth) + || (settings.m_filterBank[settings.m_filterIndex].m_highCutoff != m_settings.m_filterBank[m_settings.m_filterIndex].m_highCutoff) || (settings.m_filterBank[settings.m_filterIndex].m_lowCutoff != m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff) || force) { SpectrumSettings spectrumSettings = m_spectrumVis.getSettings(); spectrumSettings.m_ssb = !settings.m_dsb; - spectrumSettings.m_usb = (settings.m_filterBank[settings.m_filterIndex].m_lowCutoff < settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth); + spectrumSettings.m_usb = (settings.m_filterBank[settings.m_filterIndex].m_lowCutoff < settings.m_filterBank[settings.m_filterIndex].m_highCutoff); SpectrumVis::MsgConfigureSpectrumVis *msg = SpectrumVis::MsgConfigureSpectrumVis::create(spectrumSettings, false); m_spectrumVis.getInputMessageQueue()->push(msg); } @@ -508,7 +472,7 @@ void WDSPRx::webapiUpdateChannelSettings( settings.m_filterBank[settings.m_filterIndex].m_spanLog2 = response.getSsbDemodSettings()->getSpanLog2(); } if (channelSettingsKeys.contains("rfBandwidth")) { - settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth = response.getSsbDemodSettings()->getRfBandwidth(); + settings.m_filterBank[settings.m_filterIndex].m_highCutoff = response.getSsbDemodSettings()->getRfBandwidth(); } if (channelSettingsKeys.contains("lowCutoff")) { settings.m_filterBank[settings.m_filterIndex].m_lowCutoff = response.getSsbDemodSettings()->getLowCutoff(); @@ -534,33 +498,6 @@ void WDSPRx::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("agc")) { settings.m_agc = response.getSsbDemodSettings()->getAgc() != 0; } - if (channelSettingsKeys.contains("agcClamping")) { - settings.m_agcClamping = response.getSsbDemodSettings()->getAgcClamping() != 0; - } - if (channelSettingsKeys.contains("agcTimeLog2")) { - settings.m_agcTimeLog2 = response.getSsbDemodSettings()->getAgcTimeLog2(); - } - if (channelSettingsKeys.contains("agcPowerThreshold")) { - settings.m_agcPowerThreshold = response.getSsbDemodSettings()->getAgcPowerThreshold(); - } - if (channelSettingsKeys.contains("agcThresholdGate")) { - settings.m_agcThresholdGate = response.getSsbDemodSettings()->getAgcThresholdGate(); - } - if (channelSettingsKeys.contains("dnr")) { - settings.m_dnr = response.getSsbDemodSettings()->getDnr() != 0; - } - if (channelSettingsKeys.contains("dnrAboveAvgFactor")) { - settings.m_dnrAboveAvgFactor = response.getSsbDemodSettings()->getDnrAboveAvgFactor(); - } - if (channelSettingsKeys.contains("dnrSigmaFactor")) { - settings.m_dnrSigmaFactor = response.getSsbDemodSettings()->getDnrSigmaFactor(); - } - if (channelSettingsKeys.contains("dnrNbPeaks")) { - settings.m_dnrNbPeaks = response.getSsbDemodSettings()->getDnrNbPeaks(); - } - if (channelSettingsKeys.contains("dnrAlpha")) { - settings.m_dnrAlpha = response.getSsbDemodSettings()->getDnrAlpha(); - } if (channelSettingsKeys.contains("rgbColor")) { settings.m_rgbColor = response.getSsbDemodSettings()->getRgbColor(); } @@ -616,7 +553,7 @@ void WDSPRx::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respon response.getSsbDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); response.getSsbDemodSettings()->setFilterIndex(settings.m_filterIndex); response.getSsbDemodSettings()->setSpanLog2(settings.m_filterBank[settings.m_filterIndex].m_spanLog2); - response.getSsbDemodSettings()->setRfBandwidth(settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth); + response.getSsbDemodSettings()->setRfBandwidth(settings.m_filterBank[settings.m_filterIndex].m_highCutoff); response.getSsbDemodSettings()->setLowCutoff(settings.m_filterBank[settings.m_filterIndex].m_lowCutoff); response.getSsbDemodSettings()->setFftWindow((int) settings.m_filterBank[settings.m_filterIndex].m_fftWindow); response.getSsbDemodSettings()->setVolume(settings.m_volume); @@ -625,16 +562,6 @@ void WDSPRx::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respon response.getSsbDemodSettings()->setDsb(settings.m_dsb ? 1 : 0); response.getSsbDemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0); response.getSsbDemodSettings()->setAgc(settings.m_agc ? 1 : 0); - response.getSsbDemodSettings()->setAgcClamping(settings.m_agcClamping ? 1 : 0); - response.getSsbDemodSettings()->setAgcTimeLog2(settings.m_agcTimeLog2); - response.getSsbDemodSettings()->setAgcPowerThreshold(settings.m_agcPowerThreshold); - response.getSsbDemodSettings()->setAgcThresholdGate(settings.m_agcThresholdGate); - response.getSsbDemodSettings()->setDnr(settings.m_dnr ? 1 : 0); - response.getSsbDemodSettings()->setDnrScheme(settings.m_dnrScheme); - response.getSsbDemodSettings()->setDnrAboveAvgFactor(settings.m_dnrAboveAvgFactor); - response.getSsbDemodSettings()->setDnrSigmaFactor(settings.m_dnrSigmaFactor); - response.getSsbDemodSettings()->setDnrNbPeaks(settings.m_dnrNbPeaks); - response.getSsbDemodSettings()->setDnrAlpha(settings.m_dnrAlpha); response.getSsbDemodSettings()->setRgbColor(settings.m_rgbColor); if (response.getSsbDemodSettings()->getTitle()) { @@ -799,7 +726,7 @@ void WDSPRx::webapiFormatChannelSettings( swgSSBDemodSettings->setSpanLog2(settings.m_filterBank[settings.m_filterIndex].m_spanLog2); } if (channelSettingsKeys.contains("rfBandwidth") || force) { - swgSSBDemodSettings->setRfBandwidth(settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth); + swgSSBDemodSettings->setRfBandwidth(settings.m_filterBank[settings.m_filterIndex].m_highCutoff); } if (channelSettingsKeys.contains("lowCutoff") || force) { swgSSBDemodSettings->setLowCutoff(settings.m_filterBank[settings.m_filterIndex].m_lowCutoff); @@ -825,33 +752,6 @@ void WDSPRx::webapiFormatChannelSettings( if (channelSettingsKeys.contains("agc") || force) { swgSSBDemodSettings->setAgc(settings.m_agc ? 1 : 0); } - if (channelSettingsKeys.contains("agcClamping") || force) { - swgSSBDemodSettings->setAgcClamping(settings.m_agcClamping ? 1 : 0); - } - if (channelSettingsKeys.contains("agcTimeLog2") || force) { - swgSSBDemodSettings->setAgcTimeLog2(settings.m_agcTimeLog2); - } - if (channelSettingsKeys.contains("agcPowerThreshold") || force) { - swgSSBDemodSettings->setAgcPowerThreshold(settings.m_agcPowerThreshold); - } - if (channelSettingsKeys.contains("agcThresholdGate") || force) { - swgSSBDemodSettings->setAgcThresholdGate(settings.m_agcThresholdGate); - } - if (channelSettingsKeys.contains("dnr")) { - swgSSBDemodSettings->setDnr(settings.m_dnr ? 1 : 0); - } - if (channelSettingsKeys.contains("dnrAboveAvgFactor")) { - swgSSBDemodSettings->setDnrAboveAvgFactor(settings.m_dnrAboveAvgFactor); - } - if (channelSettingsKeys.contains("dnrSigmaFactor")) { - swgSSBDemodSettings->setDnrSigmaFactor(settings.m_dnrSigmaFactor); - } - if (channelSettingsKeys.contains("dnrNbPeaks")) { - swgSSBDemodSettings->setDnrNbPeaks(settings.m_dnrNbPeaks); - } - if (channelSettingsKeys.contains("dnrAlpha")) { - swgSSBDemodSettings->setDnrAlpha(settings.m_dnrAlpha); - } if (channelSettingsKeys.contains("rgbColor") || force) { swgSSBDemodSettings->setRgbColor(settings.m_rgbColor); } diff --git a/plugins/channelrx/wdsprx/wdsprxgui.cpp b/plugins/channelrx/wdsprx/wdsprxgui.cpp index a133882b9..44bdd750f 100644 --- a/plugins/channelrx/wdsprx/wdsprxgui.cpp +++ b/plugins/channelrx/wdsprx/wdsprxgui.cpp @@ -202,43 +202,18 @@ void WDSPRxGUI::on_agc_toggled(bool checked) { m_settings.m_agc = checked; applySettings(); - displayAGC(); } -void WDSPRxGUI::on_agcClamping_toggled(bool checked) +void WDSPRxGUI::on_dnr_toggled(bool) { - m_settings.m_agcClamping = checked; - applySettings(); + // TBD } -void WDSPRxGUI::on_dnr_toggled(bool checked) -{ - m_settings.m_dnr = checked; - m_settings.m_filterBank[m_settings.m_filterIndex].m_dnr = m_settings.m_dnr; - applySettings(); -} - -void WDSPRxGUI::on_agcTimeLog2_valueChanged(int value) +void WDSPRxGUI::on_agcGain_valueChanged(int value) { QString s = QString::number((1<agcTimeText->setText(s); - m_settings.m_agcTimeLog2 = value; - applySettings(); -} - -void WDSPRxGUI::on_agcPowerThreshold_valueChanged(int value) -{ - displayAGCPowerThreshold(value); - m_settings.m_agcPowerThreshold = value; - applySettings(); -} - -void WDSPRxGUI::on_agcThresholdGate_valueChanged(int value) -{ - int agcThresholdGate = value < 20 ? value : ((value - 20) * 10) + 20; - QString s = QString::number(agcThresholdGate, 'f', 0); - ui->agcThresholdGateText->setText(s); - m_settings.m_agcThresholdGate = agcThresholdGate; + ui->agcGainText->setText(s); + m_settings.m_agcGain = value; applySettings(); } @@ -271,7 +246,7 @@ void WDSPRxGUI::on_flipSidebands_clicked(bool checked) void WDSPRxGUI::on_fftWindow_currentIndexChanged(int index) { - m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow = (FFTWindow::Function) index; + m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow = index; applySettings(); } @@ -594,7 +569,7 @@ void WDSPRxGUI::applyBandwidths(unsigned int spanLog2, bool force) m_settings.m_dsb = dsb; m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2 = spanLog2; - m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth = bw * 100; + m_settings.m_filterBank[m_settings.m_filterIndex].m_highCutoff = bw * 100; m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff = lw * 100; applySettings(force); @@ -611,7 +586,7 @@ void WDSPRxGUI::displaySettings() { m_channelMarker.blockSignals(true); m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); - m_channelMarker.setBandwidth(m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth * 2); + m_channelMarker.setBandwidth(m_settings.m_filterBank[m_settings.m_filterIndex].m_highCutoff * 2); m_channelMarker.setTitle(m_settings.m_title); m_channelMarker.setLowCutoff(m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff); @@ -629,7 +604,7 @@ void WDSPRxGUI::displaySettings() } else { - if (m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth < 0) + if (m_settings.m_filterBank[m_settings.m_filterIndex].m_highCutoff < 0) { m_channelMarker.setSidebands(ChannelMarker::lsb); ui->dsb->setIcon(m_iconDSBLSB); @@ -653,14 +628,15 @@ void WDSPRxGUI::displaySettings() ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); ui->agc->setChecked(m_settings.m_agc); - displayAGC(); - ui->agcClamping->setChecked(m_settings.m_agcClamping); + ui->agcGain->setValue(m_settings.m_agcGain); + QString s = QString::number((ui->agcGain->value()), 'f', 0); + ui->agcGainText->setText(s); ui->dnr->setChecked(m_settings.m_dnr); ui->audioBinaural->setChecked(m_settings.m_audioBinaural); ui->audioFlipChannels->setChecked(m_settings.m_audioFlipChannels); ui->audioMute->setChecked(m_settings.m_audioMute); ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); - ui->fftWindow->setCurrentIndex((int) m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow); + ui->fftWindow->setCurrentIndex(m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow); // Prevent uncontrolled triggering of applyBandwidths ui->spanLog2->blockSignals(true); @@ -674,8 +650,8 @@ void WDSPRxGUI::displaySettings() ui->dsb->setChecked(m_settings.m_dsb); ui->spanLog2->setValue(1 + ui->spanLog2->maximum() - m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2); - ui->BW->setValue(m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth / 100.0); - QString s = QString::number(m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth/1000.0, 'f', 1); + ui->BW->setValue(m_settings.m_filterBank[m_settings.m_filterIndex].m_highCutoff / 100.0); + s = QString::number(m_settings.m_filterBank[m_settings.m_filterIndex].m_highCutoff/1000.0, 'f', 1); if (m_settings.m_dsb) { ui->BWText->setText(tr("%1%2k").arg(QChar(0xB1, 0x00)).arg(s)); @@ -697,14 +673,6 @@ void WDSPRxGUI::displaySettings() ui->volume->setValue(volume); ui->volumeText->setText(QString("%1").arg(volume)); - ui->agcTimeLog2->setValue(m_settings.m_agcTimeLog2); - s = QString::number((1<agcTimeLog2->value()), 'f', 0); - ui->agcTimeText->setText(s); - - ui->agcPowerThreshold->setValue(m_settings.m_agcPowerThreshold); - displayAGCPowerThreshold(ui->agcPowerThreshold->value()); - displayAGCThresholdGate(m_settings.m_agcThresholdGate); - updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); @@ -712,42 +680,9 @@ void WDSPRxGUI::displaySettings() blockApplySettings(false); } -void WDSPRxGUI::displayAGC() +void WDSPRxGUI::agcSetupDialog() { - // Disable controls only valid when AGC is enabled - ui->agcClamping->setEnabled(m_settings.m_agc); - ui->agcTimeLog2->setEnabled(m_settings.m_agc); - ui->agcTimeText->setEnabled(m_settings.m_agc); - ui->agcPowerThreshold->setEnabled(m_settings.m_agc); - ui->agcPowerThresholdText->setEnabled(m_settings.m_agc); - ui->agcThresholdGate->setEnabled(m_settings.m_agc); - ui->agcThresholdGateText->setEnabled(m_settings.m_agc); -} - -void WDSPRxGUI::displayAGCPowerThreshold(int value) -{ - if (value == WDSPRxSettings::m_minPowerThresholdDB) - { - ui->agcPowerThresholdText->setText("---"); - } - else - { - QString s = QString::number(value, 'f', 0); - ui->agcPowerThresholdText->setText(s); - } -} - -void WDSPRxGUI::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); + // TODO } void WDSPRxGUI::leaveEvent(QEvent* event) @@ -879,11 +814,8 @@ void WDSPRxGUI::makeUIConnections() QObject::connect(ui->lowCut, &TickedSlider::valueChanged, this, &WDSPRxGUI::on_lowCut_valueChanged); QObject::connect(ui->volume, &QDial::valueChanged, this, &WDSPRxGUI::on_volume_valueChanged); QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &WDSPRxGUI::on_agc_toggled); - QObject::connect(ui->agcClamping, &ButtonSwitch::toggled, this, &WDSPRxGUI::on_agcClamping_toggled); QObject::connect(ui->dnr, &ButtonSwitch::toggled, this, &WDSPRxGUI::on_dnr_toggled); - QObject::connect(ui->agcTimeLog2, &QDial::valueChanged, this, &WDSPRxGUI::on_agcTimeLog2_valueChanged); - QObject::connect(ui->agcPowerThreshold, &QDial::valueChanged, this, &WDSPRxGUI::on_agcPowerThreshold_valueChanged); - QObject::connect(ui->agcThresholdGate, &QDial::valueChanged, this, &WDSPRxGUI::on_agcThresholdGate_valueChanged); + QObject::connect(ui->agcGain, &QDial::valueChanged, this, &WDSPRxGUI::on_agcGain_valueChanged); QObject::connect(ui->audioMute, &QToolButton::toggled, this, &WDSPRxGUI::on_audioMute_toggled); QObject::connect(ui->spanLog2, &QSlider::valueChanged, this, &WDSPRxGUI::on_spanLog2_valueChanged); QObject::connect(ui->flipSidebands, &QPushButton::clicked, this, &WDSPRxGUI::on_flipSidebands_clicked); diff --git a/plugins/channelrx/wdsprx/wdsprxgui.h b/plugins/channelrx/wdsprx/wdsprxgui.h index 137697775..9b7a6178b 100644 --- a/plugins/channelrx/wdsprx/wdsprxgui.h +++ b/plugins/channelrx/wdsprx/wdsprxgui.h @@ -102,9 +102,6 @@ private: void applyBandwidths(unsigned int spanLog2, bool force = false); unsigned int spanLog2Max(); void displaySettings(); - void displayAGC(); - void displayAGCPowerThreshold(int value); - void displayAGCThresholdGate(int value); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); @@ -122,11 +119,8 @@ private slots: void on_lowCut_valueChanged(int value); void on_volume_valueChanged(int value); void on_agc_toggled(bool checked); - void on_agcClamping_toggled(bool checked); void on_dnr_toggled(bool checked); - void on_agcTimeLog2_valueChanged(int value); - void on_agcPowerThreshold_valueChanged(int value); - void on_agcThresholdGate_valueChanged(int value); + void on_agcGain_valueChanged(int value); void on_audioMute_toggled(bool checked); void on_spanLog2_valueChanged(int value); void on_flipSidebands_clicked(bool checked); @@ -138,6 +132,7 @@ private slots: void audioSelect(const QPoint& p); void dnrSetupDialog(const QPoint& p); void dnrSetup(int valueChanged); + void agcSetupDialog(); void tick(); }; diff --git a/plugins/channelrx/wdsprx/wdsprxgui.ui b/plugins/channelrx/wdsprx/wdsprxgui.ui index e67737b97..2dd18eee0 100644 --- a/plugins/channelrx/wdsprx/wdsprxgui.ui +++ b/plugins/channelrx/wdsprx/wdsprxgui.ui @@ -377,42 +377,7 @@ - Bart - - - - - B-H - - - - - FT - - - - - Ham - - - - - Han - - - - - Rec - - - - - Kai - - - - - Black + B-H4 @@ -856,40 +821,7 @@ - - - Toggle AGC clamping to maximum allowable signal - - - CL - - - true - - - - - - - Toggle Digital Noise Reduction - - - NR - - - true - - - - - - - Qt::Vertical - - - - - + 24 @@ -900,120 +832,21 @@ AGC time constant (ms in log2 steps) - 4 + -20 - 11 + 120 1 - 7 + 80 - - - - 35 - 0 - - - - AGC time constant (ms) - - - 0000 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - - - - - 24 - 24 - - - - Power threshold (dB) - - - -120 - - - 0 - - - 1 - - - -40 - - - - - - - - 26 - 0 - - - - Power threshold (dB) - - - -000 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - - - - - 24 - 24 - - - - Power threshold gate (ms) - - - 68 - - - 1 - - - 4 - - - - - + 22 @@ -1031,6 +864,19 @@ + + + + Toggle Digital Noise Reduction + + + NR + + + true + + + diff --git a/plugins/channelrx/wdsprx/wdsprxsettings.cpp b/plugins/channelrx/wdsprx/wdsprxsettings.cpp index ee1fab216..8a3e4aead 100644 --- a/plugins/channelrx/wdsprx/wdsprxsettings.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsettings.cpp @@ -45,19 +45,13 @@ void WDSPRxSettings::resetToDefaults() m_audioFlipChannels = false; m_dsb = false; m_audioMute = false; - m_agc = false; - m_agcClamping = false; - m_agcPowerThreshold = -100; - m_agcThresholdGate = 4; - m_agcTimeLog2 = 7; + m_agc = true; + m_agcMode = AGCMedium; + m_agcGain = 80; + m_agcSlope = 35; // 3.5 dB + m_agcHangThreshold = 0; m_volume = 1.0; m_inputFrequencyOffset = 0; - m_dnr = false; - m_dnrScheme = 0; - m_dnrAboveAvgFactor = 40.0f; - m_dnrSigmaFactor = 4.0f; - m_dnrNbPeaks = 20; - m_dnrAlpha = 1.0; m_rgbColor = QColor(0, 255, 0).rgb(); m_title = "WDSP Receiver"; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; @@ -87,10 +81,10 @@ QByteArray WDSPRxSettings::serialize() const s.writeBool(9, m_audioFlipChannels); s.writeBool(10, m_dsb); s.writeBool(11, m_agc); - s.writeS32(12, m_agcTimeLog2); - s.writeS32(13, m_agcPowerThreshold); - s.writeS32(14, m_agcThresholdGate); - s.writeBool(15, m_agcClamping); + s.writeS32(12, (int) m_agcMode); + s.writeS32(13, m_agcGain); + s.writeS32(14, m_agcSlope); + s.writeS32(15, m_agcHangThreshold); s.writeString(16, m_title); s.writeString(17, m_audioDeviceName); s.writeBool(18, m_useReverseAPI); @@ -108,17 +102,11 @@ QByteArray WDSPRxSettings::serialize() const s.writeBlob(26, m_geometryBytes); s.writeBool(27, m_hidden); s.writeU32(29, m_filterIndex); - s.writeBool(30, m_dnr); - s.writeS32(31, m_dnrScheme); - s.writeFloat(32, m_dnrAboveAvgFactor); - s.writeFloat(33, m_dnrSigmaFactor); - s.writeS32(34, m_dnrNbPeaks); - s.writeFloat(35, m_dnrAlpha); for (unsigned int i = 0; i < 10; i++) { s.writeS32(100 + 10*i, m_filterBank[i].m_spanLog2); - s.writeS32(101 + 10*i, m_filterBank[i].m_rfBandwidth / 100.0); + s.writeS32(101 + 10*i, m_filterBank[i].m_highCutoff / 100.0); s.writeS32(102 + 10*i, m_filterBank[i].m_lowCutoff / 100.0); s.writeS32(103 + 10*i, (int) m_filterBank[i].m_fftWindow); s.writeBool(104 + 10*i, m_filterBank[i].m_dnr); @@ -163,12 +151,13 @@ bool WDSPRxSettings::deserialize(const QByteArray& data) d.readBool(8, &m_audioBinaural, false); d.readBool(9, &m_audioFlipChannels, false); d.readBool(10, &m_dsb, false); - d.readBool(11, &m_agc, false); - d.readS32(12, &m_agcTimeLog2, 7); - d.readS32(13, &m_agcPowerThreshold, -40); - d.readS32(14, &m_agcThresholdGate, 4); - d.readBool(15, &m_agcClamping, false); - d.readString(16, &m_title, "SSB Demodulator"); + d.readBool(11, &m_agc, true); + d.readS32(12, &tmp, 2); + m_agcMode = (AGCMode) tmp; + d.readS32(13, &m_agcGain, 80); + d.readS32(14, &m_agcSlope, 35); + d.readS32(15, &m_agcHangThreshold, 0); + d.readString(16, &m_title, "WDSP Receiver"); d.readString(17, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName); d.readBool(18, &m_useReverseAPI, false); d.readString(19, &m_reverseAPIAddress, "127.0.0.1"); @@ -197,18 +186,12 @@ bool WDSPRxSettings::deserialize(const QByteArray& data) d.readBool(27, &m_hidden, false); d.readU32(29, &utmp, 0); m_filterIndex = utmp < 10 ? utmp : 0; - d.readBool(30, &m_dnr, false); - d.readS32(31, &m_dnrScheme, 0); - d.readFloat(32, &m_dnrAboveAvgFactor, 40.0f); - d.readFloat(33, &m_dnrSigmaFactor, 4.0f); - d.readS32(34, &m_dnrNbPeaks, 20); - d.readFloat(35, &m_dnrAlpha, 1.0); for (unsigned int i = 0; (i < 10); i++) { d.readS32(100 + 10*i, &m_filterBank[i].m_spanLog2, 3); d.readS32(101 + 10*i, &tmp, 30); - m_filterBank[i].m_rfBandwidth = tmp * 100.0; + m_filterBank[i].m_highCutoff = tmp * 100.0; d.readS32(102+ 10*i, &tmp, 3); m_filterBank[i].m_lowCutoff = tmp * 100.0; d.readS32(103 + 10*i, &tmp, (int) FFTWindow::Blackman); diff --git a/plugins/channelrx/wdsprx/wdsprxsettings.h b/plugins/channelrx/wdsprx/wdsprxsettings.h index 2495c3c21..1b3e64237 100644 --- a/plugins/channelrx/wdsprx/wdsprxsettings.h +++ b/plugins/channelrx/wdsprx/wdsprxsettings.h @@ -28,9 +28,9 @@ class Serializable; struct WDSPRxFilterSettings { int m_spanLog2; - Real m_rfBandwidth; + Real m_highCutoff; Real m_lowCutoff; - FFTWindow::Function m_fftWindow; + int m_fftWindow; // 0: 4-term Blackman-Harris, 1: 7-term Blackman-Harris bool m_dnr; int m_dnrScheme; float m_dnrAboveAvgFactor; @@ -40,16 +40,24 @@ struct WDSPRxFilterSettings WDSPRxFilterSettings() : m_spanLog2(3), - m_rfBandwidth(3000), + m_highCutoff(3000), m_lowCutoff(300), - m_fftWindow(FFTWindow::Blackman) + m_fftWindow(0) {} }; struct WDSPRxSettings { + enum AGCMode + { + AGCLong, + AGCSlow, + AGCMedium, + AGCFast, + }; + qint32 m_inputFrequencyOffset; - // Real m_rfBandwidth; + // Real m_highCutoff; // Real m_lowCutoff; Real m_volume; // int m_spanLog2; @@ -58,10 +66,10 @@ struct WDSPRxSettings bool m_dsb; bool m_audioMute; bool m_agc; - bool m_agcClamping; - int m_agcTimeLog2; - int m_agcPowerThreshold; - int m_agcThresholdGate; + AGCMode m_agcMode; + int m_agcGain; //!< Fixed gain if AGC is off else top gain + int m_agcSlope; + int m_agcHangThreshold; bool m_dnr; int m_dnrScheme; float m_dnrAboveAvgFactor; diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index 027b465da..6aa836f61 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -26,6 +26,10 @@ #include "util/messagequeue.h" #include "maincore.h" #include "RXA.hpp" +#include "nbp.hpp" +#include "meter.hpp" +#include "patchpanel.hpp" +#include "wcpAGC.hpp" #include "wdsprxsink.h" @@ -34,9 +38,19 @@ const int WDSPRxSink::m_agcTarget = 3276; // 32768/10 -10 dB amplitude => -20 dB const int WDSPRxSink::m_wdspSampleRate = 48000; const int WDSPRxSink::m_wdspBufSize = 512; +WDSPRxSink::SpectrumProbe::SpectrumProbe(SampleVector& sampleVector) : + m_sampleVector(sampleVector) +{} + +void WDSPRxSink::SpectrumProbe::proceed(const double *in, int nb_samples) +{ + for (int i = 0; i < nb_samples; i++) { + m_sampleVector.push_back(Sample{static_cast(in[2*i]*SDR_RX_SCALED), static_cast(in[2*i+1]*SDR_RX_SCALED)}); + } +} + WDSPRxSink::WDSPRxSink() : m_audioBinaual(false), - m_audioFlipChannels(false), m_dsb(false), m_audioMute(false), m_agc(12000, m_agcTarget, 1e-2), @@ -48,13 +62,13 @@ WDSPRxSink::WDSPRxSink() : m_squelchDelayLine(2*48000), m_audioActive(false), m_spectrumSink(nullptr), + m_spectrumProbe(m_sampleBuffer), + m_inCount(0), m_audioFifo(24000), m_audioSampleRate(48000) { m_Bandwidth = 5000; - m_LowCutoff = 300; m_volume = 2.0; - m_spanLog2 = 3; m_channelSampleRate = 48000; m_channelFrequencyOffset = 0; @@ -67,10 +81,9 @@ WDSPRxSink::WDSPRxSink() : m_demodBufferFill = 0; m_usb = true; - m_magsq = 0.0; - m_magsqSum = 0.0; - m_magsqPeak = 0.0; - m_magsqCount = 0; + m_sAvg = 0.0; + m_sPeak = 0.0; + m_sCount = 1; m_rxa = WDSP::RXA::create_rxa( m_wdspSampleRate, // input samplerate @@ -78,11 +91,8 @@ WDSPRxSink::WDSPRxSink() : m_wdspSampleRate, // sample rate for mainstream dsp processing (dsp) m_wdspBufSize // number complex samples processed per buffer in mainstream dsp processing ); - SSBFilter = new fftfilt(m_LowCutoff / m_audioSampleRate, m_Bandwidth / m_audioSampleRate, m_ssbFftLen); - DSBFilter = new fftfilt((2.0f * m_Bandwidth) / m_audioSampleRate, 2 * m_ssbFftLen); - - m_lowpassI.create(101, m_audioSampleRate, m_Bandwidth * 1.2); - m_lowpassQ.create(101, m_audioSampleRate, m_Bandwidth * 1.2); + m_rxa->setSpectrumProbe(&m_spectrumProbe); + WDSP::RXA::SetPassband(*m_rxa, 0, m_Bandwidth); applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); applySettings(m_settings, true); @@ -91,8 +101,6 @@ WDSPRxSink::WDSPRxSink() : WDSPRxSink::~WDSPRxSink() { WDSP::RXA::destroy_rxa(m_rxa); - delete SSBFilter; - delete DSBFilter; } void WDSPRxSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) @@ -127,159 +135,105 @@ void WDSPRxSink::feed(const SampleVector::const_iterator& begin, const SampleVec } } +void WDSPRxSink::getMagSqLevels(double& avg, double& peak, int& nbSamples) +{ + avg = m_sAvg; + peak = m_sPeak; + nbSamples = m_sCount; +} + void WDSPRxSink::processOneSample(Complex &ci) { - fftfilt::cmplx *sideband; - int n_out = 0; - int decim = 1<<(m_spanLog2 - 1); - unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1) + 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; - if (m_dsb) { - n_out = DSBFilter->runDSB(ci, &sideband); - } else { - n_out = SSBFilter->runSSB(ci, &sideband, m_usb); - } - - for (int i = 0; i < n_out; i++) + if (++m_inCount == m_rxa->get_insize()) { - // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display - // smart decimation with bit gain using float arithmetic (23 bits significand) + WDSP::RXA::xrxa(m_rxa); - m_sum += sideband[i]; - - if (!(m_undersampleCount++ & decim_mask)) + for (int i = 0; i < m_rxa->get_outsize(); i++) { - Real avgr = m_sum.real() / decim; - Real avgi = m_sum.imag() / decim; - m_magsq = (avgr * avgr + avgi * avgi) / (SDR_RX_SCALED*SDR_RX_SCALED); - - m_magsqSum += m_magsq; - - if (m_magsq > m_magsqPeak) + if (i == 0) { - m_magsqPeak = m_magsq; + 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); } - m_magsqCount++; - - if (!m_dsb & !m_usb) - { // invert spectrum for LSB - m_sampleBuffer.push_back(Sample(avgi, avgr)); + if (m_audioMute) + { + m_audioBuffer[m_audioBufferFill].r = 0; + m_audioBuffer[m_audioBufferFill].l = 0; } else { - m_sampleBuffer.push_back(Sample(avgr, avgi)); - } + const double& cr = m_rxa->get_outbuff()[2*i]; + const double& ci = m_rxa->get_outbuff()[2*i+1]; + 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; - m_sum.real(0.0); - m_sum.imag(0.0); - } - - float agcVal = m_agcActive ? m_agc.feedAndGetValue(sideband[i]) : 1.0; - fftfilt::cmplx& delayedSample = m_squelchDelayLine.readBack(m_agc.getStepDownDelay()); - m_audioActive = delayedSample.real() != 0.0; - - // Prevent overload based on squared magnitude variation - // Only if AGC is active - if (m_agcActive && m_agcClamping && (agcVal > 100.0 || agcVal == 0.0)) - { - // qDebug("WDSPRxSink::processOneSample: %f", agcVal); - m_agc.reset(m_agcTarget*m_agcTarget); - m_squelchDelayLine.write(fftfilt::cmplx{0.0, 0.0}); - } - else - { - m_squelchDelayLine.write(sideband[i]*agcVal); - } - - if (m_audioMute) - { - m_audioBuffer[m_audioBufferFill].r = 0; - m_audioBuffer[m_audioBufferFill].l = 0; - } - else - { - fftfilt::cmplx z = (m_agcActive && m_agcClamping) ? - fftfilt::cmplx{m_lowpassI.filter(delayedSample.real()), m_lowpassQ.filter(delayedSample.imag())} - : delayedSample; - - if (m_audioBinaual) - { - if (m_audioFlipChannels) + if (m_audioBinaual) { - m_audioBuffer[m_audioBufferFill].r = (qint16)(z.imag() * m_volume); - m_audioBuffer[m_audioBufferFill].l = (qint16)(z.real() * m_volume); + m_demodBuffer[m_demodBufferFill++] = zr * m_volume; + m_demodBuffer[m_demodBufferFill++] = zi * m_volume; } else { - m_audioBuffer[m_audioBufferFill].r = (qint16)(z.real() * m_volume); - m_audioBuffer[m_audioBufferFill].l = (qint16)(z.imag() * m_volume); + Real demod = (zr + zi) * 0.7; + qint16 sample = (qint16)(demod * m_volume); + m_demodBuffer[m_demodBufferFill++] = sample; } - m_demodBuffer[m_demodBufferFill++] = z.real(); - m_demodBuffer[m_demodBufferFill++] = z.imag(); - } - else - { - Real demod = (z.real() + z.imag()) * 0.7; - qint16 sample = (qint16)(demod * m_volume); - m_audioBuffer[m_audioBufferFill].l = sample; - m_audioBuffer[m_audioBufferFill].r = sample; - m_demodBuffer[m_demodBufferFill++] = (z.real() + z.imag()) * 0.7; - } - - if (m_demodBufferFill >= m_demodBuffer.size()) - { - QList dataPipes; - MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes); - - if (dataPipes.size() > 0) + if (m_demodBufferFill >= m_demodBuffer.size()) { - QList::iterator it = dataPipes.begin(); + QList dataPipes; + MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes); - for (; it != dataPipes.end(); ++it) + if (dataPipes.size() > 0) { - DataFifo *fifo = qobject_cast((*it)->m_element); + QList::iterator it = dataPipes.begin(); - if (fifo) + for (; it != dataPipes.end(); ++it) { - fifo->write( - (quint8*) &m_demodBuffer[0], - m_demodBuffer.size() * sizeof(qint16), - m_audioBinaual ? DataFifo::DataTypeCI16 : DataFifo::DataTypeI16 - ); + DataFifo *fifo = qobject_cast((*it)->m_element); + + if (fifo) + { + fifo->write( + (quint8*) &m_demodBuffer[0], + m_demodBuffer.size() * sizeof(qint16), + m_audioBinaual ? DataFifo::DataTypeCI16 : DataFifo::DataTypeI16 + ); + } } } + + m_demodBufferFill = 0; + } + } // audio sample + + if (++m_audioBufferFill == m_audioBuffer.size()) + { + std::size_t res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], std::min(m_audioBufferFill, m_audioBuffer.size())); + + if (res != m_audioBufferFill) { + qDebug("WDSPRxSink::processOneSample: %lu/%lu samples written", res, m_audioBufferFill); } - m_demodBufferFill = 0; + m_audioBufferFill = 0; } - } + } // result loop - ++m_audioBufferFill; - - if (m_audioBufferFill >= m_audioBuffer.size()) + if (m_spectrumSink && (m_sampleBuffer.size() != 0)) { - std::size_t res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], std::min(m_audioBufferFill, m_audioBuffer.size())); - - if (res != m_audioBufferFill) { - qDebug("WDSPRxSink::processOneSample: %lu/%lu samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; + m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false); + m_sampleBuffer.clear(); } + + m_inCount = 0; } - - if (m_spectrumSink && (m_sampleBuffer.size() != 0)) - { - m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), !m_dsb); - m_sampleBuffer.clear(); - } -} - -void WDSPRxSink::setDNR(bool dnr) -{ - SSBFilter->setDNR(dnr); } void WDSPRxSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) @@ -317,28 +271,6 @@ void WDSPRxSink::applyAudioSampleRate(int sampleRate) WDSP::RXA::setOutputSamplerate(m_rxa, sampleRate); - SSBFilter->create_filter(m_LowCutoff / (float) sampleRate, m_Bandwidth / (float) sampleRate, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow); - DSBFilter->create_dsb_filter(m_Bandwidth / (float) sampleRate, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow); - - m_lowpassI.create(101, sampleRate, m_Bandwidth * 1.2); - m_lowpassQ.create(101, sampleRate, m_Bandwidth * 1.2); - - int agcNbSamples = (sampleRate / 1000) * (1< m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f); m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f); m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate; - SSBFilter->create_filter(m_LowCutoff / (float) m_audioSampleRate, m_Bandwidth / (float) m_audioSampleRate, settings.m_filterBank[settings.m_filterIndex].m_fftWindow); - DSBFilter->create_dsb_filter(m_Bandwidth / (float) m_audioSampleRate, settings.m_filterBank[settings.m_filterIndex].m_fftWindow); - m_lowpassI.create(101, m_audioSampleRate, m_Bandwidth * 1.2); - m_lowpassQ.create(101, m_audioSampleRate, m_Bandwidth * 1.2); + + WDSP::RXA::SetPassband(*m_rxa, fLow, fHigh); + WDSP::NBP::NBPSetWindow(*m_rxa, m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow); + } + + if ((m_settings.m_agc != settings.m_agc) + || (m_settings.m_agcMode != settings.m_agcMode) + || (m_settings.m_agcGain != settings.m_agcGain) || force) + { + if (settings.m_agc) + { + WDSP::WCPAGC::SetAGCMode(*m_rxa, (int) settings.m_agcMode + 1); + WDSP::WCPAGC::SetAGCFixed(*m_rxa, (double) settings.m_agcGain); + } + else + { + WDSP::WCPAGC::SetAGCMode(*m_rxa, 0); + WDSP::WCPAGC::SetAGCTop(*m_rxa, (double) settings.m_agcGain); + } } if ((m_settings.m_volume != settings.m_volume) || force) @@ -438,75 +403,15 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) m_volume /= 4.0; // for 3276.8 } - if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) || - (m_settings.m_agcPowerThreshold != settings.m_agcPowerThreshold) || - (m_settings.m_agcThresholdGate != settings.m_agcThresholdGate) || - (m_settings.m_agcClamping != settings.m_agcClamping) || force) - { - int agcNbSamples = (m_audioSampleRate / 1000) * (1<setDNRScheme((FFTNoiseReduction::Scheme) settings.m_dnrScheme); - } - - if ((m_settings.m_dnrAboveAvgFactor != settings.m_dnrAboveAvgFactor) || force) { - SSBFilter->setDNRAboveAvgFactor(settings.m_dnrAboveAvgFactor); - } - - if ((m_settings.m_dnrSigmaFactor != settings.m_dnrSigmaFactor) || force) { - SSBFilter->setDNRSigmaFactor(settings.m_dnrSigmaFactor); - } - - if ((m_settings.m_dnrNbPeaks != settings.m_dnrNbPeaks) || force) { - SSBFilter->setDNRNbPeaks(settings.m_dnrNbPeaks); - } - - if ((m_settings.m_dnrAlpha != settings.m_dnrAlpha) || force) { - SSBFilter->setDNRAlpha(settings.m_dnrAlpha); - } - - m_spanLog2 = settings.m_filterBank[settings.m_filterIndex].m_spanLog2; m_audioBinaual = settings.m_audioBinaural; - m_audioFlipChannels = settings.m_audioFlipChannels; m_dsb = settings.m_dsb; m_audioMute = settings.m_audioMute; m_agcActive = settings.m_agc; diff --git a/plugins/channelrx/wdsprx/wdsprxsink.h b/plugins/channelrx/wdsprx/wdsprxsink.h index d16b8c6ca..1a11401ee 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.h +++ b/plugins/channelrx/wdsprx/wdsprxsink.h @@ -28,6 +28,7 @@ #include "dsp/firfilter.h" #include "audio/audiofifo.h" #include "util/doublebufferfifo.h" +#include "bufferprobe.hpp" #include "wdsprxsettings.h" @@ -51,31 +52,22 @@ public: void applyAudioSampleRate(int sampleRate); AudioFifo *getAudioFifo() { return &m_audioFifo; } - double getMagSq() const { return m_magsq; } + double getMagSq() const { return m_sAvg; } bool getAudioActive() const { return m_audioActive; } void setChannel(ChannelAPI *channel) { m_channel = channel; } void setAudioFifoLabel(const QString& label) { m_audioFifo.setLabel(label); } - void setDNR(bool dnr); - - void getMagSqLevels(double& avg, double& peak, int& nbSamples) - { - if (m_magsqCount > 0) - { - m_magsq = m_magsqSum / m_magsqCount; - m_magSqLevelStore.m_magsq = m_magsq; - m_magSqLevelStore.m_magsqPeak = m_magsqPeak; - } - - avg = m_magSqLevelStore.m_magsq; - peak = m_magSqLevelStore.m_magsqPeak; - nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; - - m_magsqSum = 0.0f; - m_magsqPeak = 0.0f; - m_magsqCount = 0; - } + void getMagSqLevels(double& avg, double& peak, int& nbSamples); private: + class SpectrumProbe : public WDSP::BufferProbe + { + public: + SpectrumProbe(SampleVector& sampleVector); + virtual void proceed(const double *in, int nbSamples); + private: + SampleVector& m_sampleVector; + }; + struct MagSqLevelsStore { MagSqLevelsStore() : @@ -90,23 +82,18 @@ private: ChannelAPI *m_channel; Real m_Bandwidth; - Real m_LowCutoff; Real m_volume; - int m_spanLog2; fftfilt::cmplx m_sum; int m_undersampleCount; int m_channelSampleRate; int m_channelFrequencyOffset; bool m_audioBinaual; - bool m_audioFlipChannels; bool m_usb; bool m_dsb; bool m_audioMute; - double m_magsq; - double m_magsqSum; - double m_magsqPeak; - int m_magsqCount; - MagSqLevelsStore m_magSqLevelStore; + double m_sAvg; + double m_sPeak; + int m_sCount; MagAGC m_agc; bool m_agcActive; bool m_agcClamping; @@ -115,19 +102,18 @@ private: int m_agcThresholdGate; //!< Gate length in number of samples befor threshold triggers DoubleBufferFIFO m_squelchDelayLine; bool m_audioActive; //!< True if an audio signal is produced (no AGC or AGC and above threshold) - Lowpass m_lowpassI; - Lowpass m_lowpassQ; - NCOF m_nco; Interpolator m_interpolator; Real m_interpolatorDistance; Real m_interpolatorDistanceRemain; - fftfilt* SSBFilter; - fftfilt* DSBFilter; + // fftfilt* SSBFilter; + // fftfilt* DSBFilter; SpectrumVis* m_spectrumSink; SampleVector m_sampleBuffer; + SpectrumProbe m_spectrumProbe; + int m_inCount; AudioVector m_audioBuffer; std::size_t m_audioBufferFill; diff --git a/wdsp/CMakeLists.txt b/wdsp/CMakeLists.txt index 6657666c8..3f09f5c39 100644 --- a/wdsp/CMakeLists.txt +++ b/wdsp/CMakeLists.txt @@ -59,6 +59,7 @@ set(wdsp_HEADERS bandpass.hpp bldr.hpp bps.hpp + bufferprobe.hpp calculus.hpp cblock.hpp cfcomp.hpp @@ -132,4 +133,3 @@ if (LINUX) ) install(TARGETS wdsp_make_interface wdsp_make_calculus DESTINATION ${INSTALL_BIN_DIR}) endif() - diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 4d4629079..2a8478e6d 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -84,454 +84,456 @@ RXA* RXA::create_rxa ( rxa->outbuff = new double[1 * rxa->dsp_outsize * 2]; // (double *) malloc0 (1 * ch.dsp_outsize * sizeof (complex)); rxa->midbuff = new double[2 * rxa->dsp_size * 2]; // (double *) malloc0 (2 * ch.dsp_size * sizeof (complex)); - // shift to select a slice of spectrum + // Ftequency shifter - shift to select a slice of spectrum rxa->shift.p = SHIFT::create_shift ( - 1, // run - rxa->dsp_insize, // input buffer size + 1, // run + rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer rxa->inbuff, // pointer to output buffer - rxa->in_rate, // samplerate - 0.0); // amount to shift (Hz) + rxa->in_rate, // samplerate + 0.0); // amount to shift (Hz) - // resample to dsp rate for main processing + // Input resampler - resample to dsp rate for main processing rxa->rsmpin.p = RESAMPLE::create_resample ( - 0, // run - will be turned ON below if needed - rxa->dsp_insize, // input buffer size + 0, // run - will be turned ON below if needed + rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - rxa->in_rate, // input samplerate - rxa->dsp_rate, // output samplerate - 0.0, // select cutoff automatically - 0, // select ncoef automatically - 1.0); // gain + rxa->in_rate, // input samplerate + rxa->dsp_rate, // output samplerate + 0.0, // select cutoff automatically + 0, // select ncoef automatically + 1.0); // gain - // signal generator + // Signal generator rxa->gen0.p = GEN::create_gen ( - 0, // run - rxa->dsp_size, // buffer size + 0, // run + rxa->dsp_size, // buffer size rxa->midbuff, // input buffer rxa->midbuff, // output buffer - rxa->dsp_rate, // sample rate - 2); // mode + rxa->dsp_rate, // sample rate + 2); // mode - // adc (input) meter + // Input meter - ADC rxa->adcmeter.p = METER::create_meter ( - 1, // run - 0, // optional pointer to another 'run' - rxa->dsp_size, // size + 0, // run + 0, // optional pointer to another 'run' + rxa->dsp_size, // size rxa->midbuff, // pointer to buffer - rxa->dsp_rate, // samplerate - 0.100, // averaging time constant - 0.100, // peak decay time constant + rxa->dsp_rate, // samplerate + 0.100, // averaging time constant + 0.100, // peak decay time constant rxa->meter, // result vector rxa->pmtupdate, // locks for meter access - RXA_ADC_AV, // index for average value - RXA_ADC_PK, // index for peak value - -1, // index for gain value - 0); // pointer for gain computation + RXA_ADC_AV, // index for average value + RXA_ADC_PK, // index for peak value + -1, // index for gain value + 0); // pointer for gain computation + + // Notched bandpass section // notch database rxa->ndb.p = NOTCHDB::create_notchdb ( - 0, // master run for all nbp's - 1024); // max number of notches + 0, // master run for all nbp's + 1024); // max number of notches // notched bandpass rxa->nbp0.p = NBP::create_nbp ( - 1, // run, always runs - 0, // run the notches - 0, // position - rxa->dsp_size, // buffer size - std::max(2048, rxa->dsp_size), // number of coefficients - 0, // minimum phase flag + 1, // run, always runs + 0, // run the notches + 0, // position + rxa->dsp_size, // buffer size + std::max(2048, rxa->dsp_size), // number of coefficients + 0, // minimum phase flag rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - -4150.0, // lower filter frequency - -150.0, // upper filter frequency - rxa->dsp_rate, // sample rate - 0, // wintype - 1.0, // gain - 1, // auto-increase notch width - 1025, // max number of passbands - rxa->ndb.p); // addr of database pointer + -4150.0, // lower filter frequency + -150.0, // upper filter frequency + rxa->dsp_rate, // sample rate + 0, // wintype + 1.0, // gain + 1, // auto-increase notch width + 1025, // max number of passbands + rxa->ndb.p); // addr of database pointer // bandpass for snba rxa->bpsnba.p = BPSNBA::create_bpsnba ( - 0, // bpsnba run flag - 0, // run the notches - 0, // position - rxa->dsp_size, // size - std::max(2048, rxa->dsp_size), // number of filter coefficients - 0, // minimum phase flag + 0, // bpsnba run flag + 0, // run the notches + 0, // position + rxa->dsp_size, // size + std::max(2048, rxa->dsp_size), // number of filter coefficients + 0, // minimum phase flag rxa->midbuff, // input buffer rxa->midbuff, // output buffer - rxa->dsp_rate, // samplerate - + 250.0, // abs value of cutoff nearest zero - + 5700.0, // abs value of cutoff farthest zero - - 5700.0, // current low frequency - - 250.0, // current high frequency - 0, // wintype - 1.0, // gain - 1, // auto-increase notch width - 1025, // max number of passbands + rxa->dsp_rate, // samplerate + + 250.0, // abs value of cutoff nearest zero + + 5700.0, // abs value of cutoff farthest zero + - 5700.0, // current low frequency + - 250.0, // current high frequency + 0, // wintype + 1.0, // gain + 1, // auto-increase notch width + 1025, // max number of passbands rxa->ndb.p); // addr of database pointer - // send spectrum display + // Post filter display send - send spectrum display (after S-meter in the block diagram) rxa->sender.p = SENDER::create_sender ( 0, // run - 0, // flag - 0, // mode - rxa->dsp_size, // size - rxa->midbuff, // pointer to input buffer - 0, // arg0 <- disp - 1, // arg1 <- ss - 0, // arg2 <- LO - 0); // arg3 <- NOT USED + 0, // flag + 0, // mode + rxa->dsp_size, // size + rxa->midbuff // pointer to input buffer + ); + + // End notched bandpass section // S-meter rxa->smeter.p = METER::create_meter ( - 1, // run - 0, // optional pointer to another 'run' - rxa->dsp_size, // size + 1, // run + 0, // optional pointer to another 'run' + rxa->dsp_size, // size rxa->midbuff, // pointer to buffer - rxa->dsp_rate, // samplerate - 0.100, // averaging time constant - 0.100, // peak decay time constant + rxa->dsp_rate, // samplerate + 0.100, // averaging time constant + 0.100, // peak decay time constant rxa->meter, // result vector rxa->pmtupdate, // locks for meter access - RXA_S_AV, // index for average value - RXA_S_PK, // index for peak value - -1, // index for gain value - 0); // pointer for gain computation + RXA_S_AV, // index for average value + RXA_S_PK, // index for peak value + -1, // index for gain value + 0); // pointer for gain computation - // AM squelch + // AM squelch capture (for other modes than FM) rxa->amsq.p = AMSQ::create_amsq ( - 0, // run - rxa->dsp_size, // buffer size + 0, // run + rxa->dsp_size, // buffer size rxa->midbuff, // pointer to signal input buffer used by xamsq rxa->midbuff, // pointer to signal output buffer used by xamsq rxa->midbuff, // pointer to trigger buffer that xamsqcap will capture - rxa->dsp_rate, // sample rate - 0.010, // time constant for averaging signal level - 0.070, // signal up transition time - 0.070, // signal down transition time - 0.009, // signal level to initiate tail - 0.010, // signal level to initiate unmute - 0.000, // minimum tail length - 1.500, // maximum tail length - 0.0); // muted gain + rxa->dsp_rate, // sample rate + 0.010, // time constant for averaging signal level + 0.070, // signal up transition time + 0.070, // signal down transition time + 0.009, // signal level to initiate tail + 0.010, // signal level to initiate unmute + 0.000, // minimum tail length + 1.500, // maximum tail length + 0.0); // muted gain - // AM demod + // AM/SAM demodulator rxa->amd.p = AMD::create_amd ( - 0, // run - OFF by default - rxa->dsp_size, // buffer size + 0, // run - OFF by default + rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - 0, // mode: 0->AM, 1->SAM - 1, // levelfade: 0->OFF, 1->ON - 0, // sideband mode: 0->OFF - rxa->dsp_rate, // sample rate - -2000.0, // minimum lock frequency - +2000.0, // maximum lock frequency - 1.0, // zeta - 250.0, // omegaN - 0.02, // tauR - 1.4); // tauI + 0, // mode: 0->AM, 1->SAM + 1, // levelfade: 0->OFF, 1->ON + 0, // sideband mode: 0->OFF + rxa->dsp_rate, // sample rate + -2000.0, // minimum lock frequency + +2000.0, // maximum lock frequency + 1.0, // zeta + 250.0, // omegaN + 0.02, // tauR + 1.4); // tauI - // FM demod + // FM demodulator rxa->fmd.p = FMD::create_fmd ( - 0, // run - rxa->dsp_size, // buffer size + 0, // run + rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - rxa->dsp_rate, // sample rate - 5000.0, // deviation - 300.0, // f_low - 3000.0, // f_high - -8000.0, // fmin - +8000.0, // fmax - 1.0, // zeta - 20000.0, // omegaN - 0.02, // tau - for dc removal - 0.5, // audio gain - 1, // run tone filter - 254.1, // ctcss frequency - std::max(2048, rxa->dsp_size), // # coefs for de-emphasis filter - 0, // min phase flag for de-emphasis filter - std::max(2048, rxa->dsp_size), // # coefs for audio cutoff filter - 0); // min phase flag for audio cutoff filter + rxa->dsp_rate, // sample rate + 5000.0, // deviation + 300.0, // f_low + 3000.0, // f_high + -8000.0, // fmin + +8000.0, // fmax + 1.0, // zeta + 20000.0, // omegaN + 0.02, // tau - for dc removal + 0.5, // audio gain + 1, // run tone filter + 254.1, // ctcss frequency + std::max(2048, rxa->dsp_size), // # coefs for de-emphasis filter + 0, // min phase flag for de-emphasis filter + std::max(2048, rxa->dsp_size), // # coefs for audio cutoff filter + 0); // min phase flag for audio cutoff filter - // FM squelch + // FM squelch apply rxa->fmsq.p = FMSQ::create_fmsq ( - 0, // run - rxa->dsp_size, // buffer size + 0, // run + rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input signal buffer rxa->midbuff, // pointer to output signal buffer rxa->fmd.p->audio, // pointer to trigger buffer - rxa->dsp_rate, // sample rate - 5000.0, // cutoff freq for noise filter (Hz) + rxa->dsp_rate, // sample rate + 5000.0, // cutoff freq for noise filter (Hz) &rxa->fmd.p->pllpole, // pointer to pole frequency of the fmd pll (Hz) - 0.100, // delay time after channel flush - 0.001, // tau for noise averaging - 0.100, // tau for long noise averaging - 0.050, // signal up transition time - 0.010, // signal down transition time - 0.750, // noise level to initiate tail - 0.562, // noise level to initiate unmute - 0.000, // minimum tail time - 1.200, // maximum tail time - std::max(2048, rxa->dsp_size), // number of coefficients for noise filter - 0); // minimum phase flag + 0.100, // delay time after channel flush + 0.001, // tau for noise averaging + 0.100, // tau for long noise averaging + 0.050, // signal up transition time + 0.010, // signal down transition time + 0.750, // noise level to initiate tail + 0.562, // noise level to initiate unmute + 0.000, // minimum tail time + 1.200, // maximum tail time + std::max(2048, rxa->dsp_size), // number of coefficients for noise filter + 0); // minimum phase flag - // snba + // Spectral noise blanker (SNB) rxa->snba.p = SNBA::create_snba ( - 0, // run + 0, // run rxa->midbuff, // input buffer rxa->midbuff, // output buffer - rxa->dsp_rate, // input / output sample rate - 12000, // internal processing sample rate - rxa->dsp_size, // buffer size - 4, // overlap factor to use - 256, // frame size to use; sized for 12K rate - 64, // asize - 2, // npasses - 8.0, // k1 - 20.0, // k2 - 10, // b - 2, // pre - 2, // post - 0.5, // pmultmin - 200.0, // output resampler low cutoff - 5400.0); // output resampler high cutoff + rxa->dsp_rate, // input / output sample rate + 12000, // internal processing sample rate + rxa->dsp_size, // buffer size + 4, // overlap factor to use + 256, // frame size to use; sized for 12K rate + 64, // asize + 2, // npasses + 8.0, // k1 + 20.0, // k2 + 10, // b + 2, // pre + 2, // post + 0.5, // pmultmin + 200.0, // output resampler low cutoff + 5400.0); // output resampler high cutoff - // EQ + // Equalizer { double default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0}; //double default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0}; double default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; rxa->eqp.p = EQP::create_eqp ( - 0, // run - OFF by default - rxa->dsp_size, // buffer size - std::max(2048, rxa->dsp_size), // number of filter coefficients - 0, // minimum phase flag - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer - 10, // number of frequencies - default_F, // frequency vector - default_G, // gain vector - 0, // cutoff mode - 0, // wintype - rxa->dsp_rate); // sample rate + 0, // run - OFF by default + rxa->dsp_size, // buffer size + std::max(2048, rxa->dsp_size), // number of filter coefficients + 0, // minimum phase flag + rxa->midbuff, // pointer to input buffer + rxa->midbuff, // pointer to output buffer + 10, // number of frequencies + default_F, // frequency vector + default_G, // gain vector + 0, // cutoff mode + 0, // wintype + rxa->dsp_rate); // sample rate } - // ANF + // Auto notch filter rxa->anf.p = ANF::create_anf ( - 0, // run - OFF by default - 0, // position - rxa->dsp_size, // buffer size + 0, // run - OFF by default + 0, // position + rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - ANF_DLINE_SIZE, // dline_size - 64, // taps - 16, // delay - 0.0001, // two_mu - 0.1, // gamma - 1.0, // lidx - 0.0, // lidx_min - 200.0, // lidx_max - 6.25e-12, // ngamma - 6.25e-10, // den_mult - 1.0, // lincr - 3.0); // ldecr + ANF_DLINE_SIZE, // dline_size + 64, // taps + 16, // delay + 0.0001, // two_mu + 0.1, // gamma + 1.0, // lidx + 0.0, // lidx_min + 200.0, // lidx_max + 6.25e-12, // ngamma + 6.25e-10, // den_mult + 1.0, // lincr + 3.0); // ldecr - // ANR + // LMS noise reduction (ANR or "NR") rxa->anr.p = ANR::create_anr ( - 0, // run - OFF by default - 0, // position - rxa->dsp_size, // buffer size + 0, // run - OFF by default + 0, // position + rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - ANR_DLINE_SIZE, // dline_size - 64, // taps - 16, // delay - 0.0001, // two_mu - 0.1, // gamma - 120.0, // lidx - 120.0, // lidx_min - 200.0, // lidx_max - 0.001, // ngamma - 6.25e-10, // den_mult - 1.0, // lincr - 3.0); // ldecr + ANR_DLINE_SIZE, // dline_size + 64, // taps + 16, // delay + 0.0001, // two_mu + 0.1, // gamma + 120.0, // lidx + 120.0, // lidx_min + 200.0, // lidx_max + 0.001, // ngamma + 6.25e-10, // den_mult + 1.0, // lincr + 3.0); // ldecr - - // EMNR + // Spectral noise reduyction (EMNR or "NR2") rxa->emnr.p = EMNR::create_emnr ( - 0, // run - 0, // position - rxa->dsp_size, // buffer size + 0, // run + 0, // position + rxa->dsp_size, // buffer size rxa->midbuff, // input buffer rxa->midbuff, // output buffer - 4096, // FFT size - 4, // overlap - rxa->dsp_rate, // samplerate - 0, // window type - 1.0, // gain - 2, // gain method - 0, // npe_method - 1); // ae_run + 4096, // FFT size + 4, // overlap + rxa->dsp_rate, // samplerate + 0, // window type + 1.0, // gain + 2, // gain method + 0, // npe_method + 1); // ae_run // AGC rxa->agc.p = WCPAGC::create_wcpagc ( - 1, // run - 3, // mode - 1, // peakmode = envelope + 1, // run + 3, // mode + 1, // peakmode = envelope rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - rxa->dsp_size, // buffer size - rxa->dsp_rate, // sample rate - 0.001, // tau_attack - 0.250, // tau_decay - 4, // n_tau - 10000.0, // max_gain - 1.5, // var_gain - 1000.0, // fixed_gain - 1.0, // max_input - 1.0, // out_target - 0.250, // tau_fast_backaverage - 0.005, // tau_fast_decay - 5.0, // pop_ratio - 1, // hang_enable - 0.500, // tau_hang_backmult - 0.250, // hangtime - 0.250, // hang_thresh - 0.100); // tau_hang_decay + rxa->dsp_size, // buffer size + rxa->dsp_rate, // sample rate + 0.001, // tau_attack + 0.250, // tau_decay + 4, // n_tau + 10000.0, // max_gain + 1.5, // var_gain + 1000.0, // fixed_gain + 1.0, // max_input + 1.0, // out_target + 0.250, // tau_fast_backaverage + 0.005, // tau_fast_decay + 5.0, // pop_ratio + 1, // hang_enable + 0.500, // tau_hang_backmult + 0.250, // hangtime + 0.250, // hang_thresh + 0.100); // tau_hang_decay - // agc gain meter + // AGC meter rxa->agcmeter.p = METER::create_meter ( - 1, // run - 0, // optional pointer to another 'run' - rxa->dsp_size, // size + 0, // run + 0, // optional pointer to another 'run' + rxa->dsp_size, // size rxa->midbuff, // pointer to buffer - rxa->dsp_rate, // samplerate - 0.100, // averaging time constant - 0.100, // peak decay time constant + rxa->dsp_rate, // samplerate + 0.100, // averaging time constant + 0.100, // peak decay time constant rxa->meter, // result vector rxa->pmtupdate, // locks for meter access - RXA_AGC_AV, // index for average value - RXA_AGC_PK, // index for peak value - RXA_AGC_GAIN, // index for gain value + RXA_AGC_AV, // index for average value + RXA_AGC_PK, // index for peak value + RXA_AGC_GAIN, // index for gain value &rxa->agc.p->gain); // pointer for gain computation - // bandpass filter + // Bandpass filter - After spectral noise reduction in the block diagram rxa->bp1.p = BANDPASS::create_bandpass ( - 1, // run - used only with ( AM || ANF || ANR || EMNR) - 0, // position - rxa->dsp_size, // buffer size - std::max(2048, rxa->dsp_size), // number of coefficients - 0, // flag for minimum phase + 1, // run - used only with ( AM || ANF || ANR || EMNR) + 0, // position + rxa->dsp_size, // buffer size + std::max(2048, rxa->dsp_size), // number of coefficients + 0, // flag for minimum phase rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - -4150.0, // lower filter frequency - -150.0, // upper filter frequency - rxa->dsp_rate, // sample rate - 1, // wintype - 1.0); // gain + -4150.0, // lower filter frequency + -150.0, // upper filter frequency + rxa->dsp_rate, // sample rate + 1, // wintype + 1.0); // gain - // pull phase & scope display data + // Scope/phase display send - pull phase & scope display data rxa->sip1.p = SIPHON::create_siphon ( - 1, // run - needed only for phase display - 0, // position - 0, // mode - 0, // disp - rxa->dsp_size, // size of input buffer + 0, // run - needed only for phase display + 0, // position + 0, // mode + 0, // disp + rxa->dsp_size, // size of input buffer rxa->midbuff, // input buffer - 4096, // number of samples to store - 4096, // fft size for spectrum - 0); // specmode + 4096, // number of samples to store + 4096, // fft size for spectrum + 0); // specmode - // carrier block + // AM carrier block rxa->cbl.p = CBL::create_cbl ( - 0, // run - needed only if set to ON - rxa->dsp_size, // buffer size + 0, // run - needed only if set to ON + rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - 0, // mode - rxa->dsp_rate, // sample rate - 0.02); // tau + 0, // mode + rxa->dsp_rate, // sample rate + 0.02); // tau - // peaking filter + // CW peaking filter rxa->speak.p = SPEAK::create_speak ( - 0, // run - rxa->dsp_size, // buffer size, + 0, // run + rxa->dsp_size, // buffer size, rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - rxa->dsp_rate, // sample rate - 600.0, // center frequency - 100.0, // bandwidth - 2.0, // gain - 4, // number of stages - 1); // design + rxa->dsp_rate, // sample rate + 600.0, // center frequency + 100.0, // bandwidth + 2.0, // gain + 4, // number of stages + 1); // design - // multiple peak filter + // Dolly filter (multiple peak filter) - default is 2 for RTTY { int def_enable[2] = {1, 1}; double def_freq[2] = {2125.0, 2295.0}; double def_bw[2] = {75.0, 75.0}; double def_gain[2] = {1.0, 1.0}; rxa->mpeak.p = MPEAK::create_mpeak ( - 0, // run - rxa->dsp_size, // size + 0, // run + rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - rxa->dsp_rate, // sample rate - 2, // number of peaking filters - def_enable, // enable vector - def_freq, // frequency vector - def_bw, // bandwidth vector - def_gain, // gain vector - 4 ); // number of stages + rxa->dsp_rate, // sample rate + 2, // number of peaking filters + def_enable, // enable vector + def_freq, // frequency vector + def_bw, // bandwidth vector + def_gain, // gain vector + 4 ); // number of stages } - // syllabic squelch + // Syllabic squelch (Voice suelch) - Not in the block diagram rxa->ssql.p = SSQL::create_ssql( - 0, // run - rxa->dsp_size, // size + 0, // run + rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - rxa->dsp_rate, // sample rate - 0.070, // signal up transition time - 0.070, // signal down transition time - 0.0, // muted gain - 0.1, // mute time-constant - 0.1, // unmute time-constant - 0.08, // window threshold - 0.8197, // trigger threshold - 2400, // ring size for f_to_v converter - 2000.0); // max freq for f_to_v converter + rxa->dsp_rate, // sample rate + 0.070, // signal up transition time + 0.070, // signal down transition time + 0.0, // muted gain + 0.1, // mute time-constant + 0.1, // unmute time-constant + 0.08, // window threshold + 0.8197, // trigger threshold + 2400, // ring size for f_to_v converter + 2000.0); // max freq for f_to_v converter - // patchpanel + // PatchPanel rxa->panel.p = PANEL::create_panel ( - 1, // run - rxa->dsp_size, // size + 1, // run + rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - 4.0, // gain1 - 1.0, // gain2I - 1.0, // gain2Q - 3, // 3 for I and Q - 0); // no copy + 4.0, // gain1 + 1.0, // gain2I + 1.0, // gain2Q + 3, // 3 for I and Q + 0); // no copy - // resample + // AM squelch apply - absent but in the block diagram + + // Output resampler rxa->rsmpout.p = RESAMPLE::create_resample ( - 0, // run - will be turned ON below if needed - rxa->dsp_size, // input buffer size + 0, // run - will be turned ON below if needed + rxa->dsp_size, // input buffer size rxa->midbuff, // pointer to input buffer rxa->outbuff, // pointer to output buffer - rxa->dsp_rate, // input sample rate - rxa->out_rate, // output sample rate - 0.0, // select cutoff automatically - 0, // select ncoef automatically - 1.0); // gain + rxa->dsp_rate, // input sample rate + rxa->out_rate, // output sample rate + 0.0, // select cutoff automatically + 0, // select ncoef automatically + 1.0); // gain // turn OFF / ON resamplers as needed ResCheck (*rxa); @@ -825,6 +827,12 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->csDSP.unlock(); } +void RXA::setSpectrumProbe(BufferProbe *spectrumProbe) +{ + SENDER::SetSpectrum(*this, 1, spectrumProbe); + sender.p->run = 1; +} + /******************************************************************************************************** * * * RXA Mode & Filter Controls * @@ -843,8 +851,8 @@ void RXA::SetMode (RXA& rxa, int mode) rxa.snba.p->run, rxa.emnr.p->run, rxa.anf.p->run, - rxa.anr.p->run) - ; + rxa.anr.p->run + ); rxa.csDSP.lock(); rxa.mode = mode; rxa.amd.p->run = 0; @@ -861,7 +869,6 @@ void RXA::SetMode (RXA& rxa, int mode) rxa.amd.p->mode = 1; break; case RXA_DSB: - break; case RXA_FM: rxa.fmd.p->run = 1; @@ -1033,9 +1040,9 @@ void RXA::bpsnbaSet (RXA& rxa) void RXA::SetPassband (RXA& rxa, double f_low, double f_high) { - BANDPASS::SetBandpassFreqs (rxa, f_low, f_high); - SNBA::SetSNBAOutputBandwidth (rxa, f_low, f_high); - NBP::NBPSetFreqs (rxa, f_low, f_high); + BANDPASS::SetBandpassFreqs (rxa, f_low, f_high); // After spectral noise reduction ( AM || ANF || ANR || EMNR) + SNBA::SetSNBAOutputBandwidth (rxa, f_low, f_high); // Spectral noise blanker (SNB) + NBP::NBPSetFreqs (rxa, f_low, f_high); // Notched bandpass } void RXA::SetNC (RXA& rxa, int nc) diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 02e797044..51ae351f0 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -63,6 +63,7 @@ class PANEL; class SIPHON; class CBL; class SSQL; +class BufferProbe; class WDSP_API RXA : public Unit { @@ -97,7 +98,7 @@ public: int mode; double meter[RXA_METERTYPE_LAST]; - QRecursiveMutex* pmtupdate[RXA_METERTYPE_LAST]; + QRecursiveMutex *pmtupdate[RXA_METERTYPE_LAST]; struct { METER *p; @@ -216,6 +217,7 @@ public: int get_outsize() const { return dsp_outsize; } double *get_inbuff() { return inbuff; } double *get_outbuff() { return outbuff; } + void setSpectrumProbe(BufferProbe *_spectrumProbe); static void setInputSamplerate (RXA *rxa, int in_rate); static void setOutputSamplerate (RXA *rxa, int out_rate); static void setDSPSamplerate (RXA *rxa, int dsp_rate); diff --git a/wdsp/bufferprobe.hpp b/wdsp/bufferprobe.hpp new file mode 100644 index 000000000..7949eb6bd --- /dev/null +++ b/wdsp/bufferprobe.hpp @@ -0,0 +1,39 @@ +/* bufferProbe.hpp + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2013 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +The author can be reached by email at + +warren@wpratt.com + +This interface can be subclassed to receive complex samples from a block +Typically spectrum samples from the sender + +*/ + +namespace WDSP { + +class BufferProbe +{ +public: + virtual void proceed(const double *buffer, int nb_samples) = 0; +}; + +} // namespace diff --git a/wdsp/meter.cpp b/wdsp/meter.cpp index 7459bc295..523d03104 100644 --- a/wdsp/meter.cpp +++ b/wdsp/meter.cpp @@ -28,6 +28,8 @@ warren@wpratt.com #include "comm.hpp" #include "meterlog10.hpp" #include "meter.hpp" +#include "RXA.hpp" +#include "TXA.hpp" namespace WDSP { @@ -147,16 +149,14 @@ void METER::setSize_meter (METER *a, int size) * * ********************************************************************************************************/ -// PORT -// double GetRXAMeter (int channel, int mt) -// { -// double val; -// CRITICAL_SECTION* a = rxa[channel].pmtupdate[mt]; -// EnterCriticalSection (a); -// val = rxa[channel].meter[mt]; -// LeaveCriticalSection (a); -// return val; -// } +double METER::GetMeter (RXA& rxa, int mt) +{ + double val; + rxa.pmtupdate[mt]->lock(); + val = rxa.meter[mt]; + rxa.pmtupdate[mt]->unlock(); + return val; +} /******************************************************************************************************** * * @@ -164,15 +164,13 @@ void METER::setSize_meter (METER *a, int size) * * ********************************************************************************************************/ -// PORT -// double GetTXAMeter (int channel, int mt) -// { -// double val; -// CRITICAL_SECTION* a = txa[channel].pmtupdate[mt]; -// EnterCriticalSection (a); -// val = txa[channel].meter[mt]; -// LeaveCriticalSection (a); -// return val; -// } +double METER::GetMeter (TXA& txa, int mt) +{ + double val; + txa.pmtupdate[mt]->lock(); + val = txa.meter[mt]; + txa.pmtupdate[mt]->unlock(); + return val; +} } // namespace WDSP diff --git a/wdsp/meter.hpp b/wdsp/meter.hpp index e9764b0c1..cb6b45bf4 100644 --- a/wdsp/meter.hpp +++ b/wdsp/meter.hpp @@ -34,6 +34,9 @@ warren@wpratt.com namespace WDSP { +class RXA; +class TXA; + class WDSP_API METER { public: @@ -76,19 +79,16 @@ public: static void setBuffers_meter (METER *a, double* in); static void setSamplerate_meter (METER *a, int rate); static void setSize_meter (METER *a, int size); + // RXA Properties + static double GetMeter (RXA& rxa, int mt); + // TXA Properties + static double GetMeter (TXA& txa, int mt); private: static void calc_meter (METER *a); }; -// RXA Properties - - // __declspec (dllexport) double GetRXAMeter (int channel, int mt); - -// TXA Properties - - // __declspec (dllexport) double GetTXAMeter (int channel, int mt); } // namespace WDSP diff --git a/wdsp/sender.cpp b/wdsp/sender.cpp index 3d3ff2733..8abb4df9c 100644 --- a/wdsp/sender.cpp +++ b/wdsp/sender.cpp @@ -28,20 +28,11 @@ warren@wpratt.com #include "comm.hpp" #include "sender.hpp" #include "RXA.hpp" +#include "bufferprobe.hpp" namespace WDSP { -void SENDER::calc_sender (SENDER *a) -{ - a->out = new double[a->size * 2]; // (double *) malloc0 (a->size * sizeof (complex)); -} - -void decalc_sender (SENDER *a) -{ - delete[] (a->out); -} - -SENDER* SENDER::create_sender (int run, int flag, int mode, int size, double* in, int arg0, int arg1, int arg2, int arg3) +SENDER* SENDER::create_sender (int run, int flag, int mode, int size, double* in) { SENDER *a = new SENDER; a->run = run; @@ -49,23 +40,17 @@ SENDER* SENDER::create_sender (int run, int flag, int mode, int size, double* in a->mode = mode; a->size = size; a->in = in; - a->arg0 = arg0; - a->arg1 = arg1; - a->arg2 = arg2; - a->arg3 = arg3; - calc_sender (a); + a->spectrumProbe = nullptr; return a; } void SENDER::destroy_sender (SENDER *a) { - decalc_sender (a); delete (a); } -void SENDER::flush_sender (SENDER *a) +void SENDER::flush_sender (SENDER *) { - memset (a->out, 0, a->size * sizeof (dcomplex)); } void SENDER::xsender (SENDER *a) @@ -76,14 +61,9 @@ void SENDER::xsender (SENDER *a) { case 0: { - int i; - dINREAL* outf = (dINREAL *)a->out; - for (i = 0; i < a->size; i++) - { - outf [2 * i + 0] = (dINREAL)a->in[2 * i + 0]; - outf [2 * i + 1] = (dINREAL)a->in[2 * i + 1]; + if (a->spectrumProbe) { + a->spectrumProbe->proceed(a->in, a->size); } - // Spectrum2 (1, a->arg0, a->arg1, a->arg2, outf); break; } } @@ -102,9 +82,7 @@ void SENDER::setSamplerate_sender (SENDER *a, int) void SENDER::setSize_sender (SENDER *a, int size) { - decalc_sender (a); a->size = size; - calc_sender (a); } /******************************************************************************************************** @@ -113,15 +91,13 @@ void SENDER::setSize_sender (SENDER *a, int size) * * ********************************************************************************************************/ -void SENDER::SetSpectrum (RXA& rxa, int flag, int disp, int ss, int LO) +void SENDER::SetSpectrum (RXA& rxa, int flag, BufferProbe *spectrumProbe) { SENDER *a; rxa.csDSP.lock(); a = rxa.sender.p; a->flag = flag; - a->arg0 = disp; - a->arg1 = ss; - a->arg2 = LO; + a->spectrumProbe = spectrumProbe; rxa.csDSP.unlock(); } diff --git a/wdsp/sender.hpp b/wdsp/sender.hpp index 32875cc51..ea4b8bdd9 100644 --- a/wdsp/sender.hpp +++ b/wdsp/sender.hpp @@ -38,6 +38,7 @@ warren@wpratt.com namespace WDSP { class RXA; +class BufferProbe; class WDSP_API SENDER { @@ -47,14 +48,9 @@ public: int mode; // selects the specific processing and function call int size; // size of the data buffer (complex samples) double* in; // buffer from which to take the data - int arg0; // parameters that can be passed to the function called - int arg1; - int arg2; - int arg3; - double* out; // internally created buffer into which processed data is placed - // a pointer to *out is passed to the external function that is called + BufferProbe *spectrumProbe; // this is the data handler actually - static SENDER* create_sender (int run, int flag, int mode, int size, double* in, int arg0, int arg1, int arg2, int arg3); + static SENDER* create_sender (int run, int flag, int mode, int size, double* in); static void destroy_sender (SENDER *a); static void flush_sender (SENDER *a); static void xsender (SENDER *a); @@ -62,12 +58,8 @@ public: static void setSamplerate_sender (SENDER *a, int rate); static void setSize_sender (SENDER *a, int size); // RXA Properties - static void SetSpectrum (RXA& rxa, int flag, int disp, int ss, int LO); + static void SetSpectrum (RXA& rxa, int flag, BufferProbe *spectrumProbe); // TXA Properties - -private: - static void calc_sender (SENDER *a); - static void decalc_sender (SENDER *a); }; } // namespace WDSP