1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-10 18:43:28 -05:00

WDSP Rx: first basic implementation

This commit is contained in:
f4exb 2024-06-24 10:20:14 +02:00
parent d0391e3516
commit b2303b028c
16 changed files with 672 additions and 1103 deletions

View File

@ -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);
}

View File

@ -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<<value), 'f', 0);
ui->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<<ui->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);

View File

@ -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();
};

View File

@ -377,42 +377,7 @@
</property>
<item>
<property name="text">
<string>Bart</string>
</property>
</item>
<item>
<property name="text">
<string>B-H</string>
</property>
</item>
<item>
<property name="text">
<string>FT</string>
</property>
</item>
<item>
<property name="text">
<string>Ham</string>
</property>
</item>
<item>
<property name="text">
<string>Han</string>
</property>
</item>
<item>
<property name="text">
<string>Rec</string>
</property>
</item>
<item>
<property name="text">
<string>Kai</string>
</property>
</item>
<item>
<property name="text">
<string>Black</string>
<string>B-H4</string>
</property>
</item>
<item>
@ -856,40 +821,7 @@
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="agcClamping">
<property name="toolTip">
<string>Toggle AGC clamping to maximum allowable signal</string>
</property>
<property name="text">
<string>CL</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="dnr">
<property name="toolTip">
<string>Toggle Digital Noise Reduction</string>
</property>
<property name="text">
<string>NR</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="agcTimeLog2">
<widget class="QDial" name="agcGain">
<property name="maximumSize">
<size>
<width>24</width>
@ -900,120 +832,21 @@
<string>AGC time constant (ms in log2 steps)</string>
</property>
<property name="minimum">
<number>4</number>
<number>-20</number>
</property>
<property name="maximum">
<number>11</number>
<number>120</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>7</number>
<number>80</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="agcTimeText">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>AGC time constant (ms)</string>
</property>
<property name="text">
<string>0000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="agcPowerThreshold">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Power threshold (dB)</string>
</property>
<property name="minimum">
<number>-120</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>-40</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="agcPowerThresholdText">
<property name="minimumSize">
<size>
<width>26</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Power threshold (dB)</string>
</property>
<property name="text">
<string>-000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="agcThresholdGate">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Power threshold gate (ms)</string>
</property>
<property name="maximum">
<number>68</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="agcThresholdGateText">
<widget class="QLabel" name="agcGainText">
<property name="minimumSize">
<size>
<width>22</width>
@ -1031,6 +864,19 @@
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="dnr">
<property name="toolTip">
<string>Toggle Digital Noise Reduction</string>
</property>
<property name="text">
<string>NR</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">

View File

@ -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);

View File

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

View File

@ -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<FixReal>(in[2*i]*SDR_RX_SCALED), static_cast<FixReal>(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<ObjectPipe*> dataPipes;
MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes);
if (dataPipes.size() > 0)
if (m_demodBufferFill >= m_demodBuffer.size())
{
QList<ObjectPipe*>::iterator it = dataPipes.begin();
QList<ObjectPipe*> dataPipes;
MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes);
for (; it != dataPipes.end(); ++it)
if (dataPipes.size() > 0)
{
DataFifo *fifo = qobject_cast<DataFifo*>((*it)->m_element);
QList<ObjectPipe*>::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<DataFifo*>((*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_settings.m_agcTimeLog2);
int agcThresholdGate = (sampleRate / 1000) * m_settings.m_agcThresholdGate; // ms
if (m_agcNbSamples != agcNbSamples)
{
m_agc.resize(agcNbSamples, agcNbSamples/2, m_agcTarget);
m_agc.setStepDownDelay(agcNbSamples);
m_agcNbSamples = agcNbSamples;
}
if (m_agcThresholdGate != agcThresholdGate)
{
m_agc.setGate(agcThresholdGate);
m_agcThresholdGate = agcThresholdGate;
}
m_audioFifo.setSize(sampleRate);
m_audioSampleRate = sampleRate;
m_audioBuffer.resize(sampleRate / 10);
@ -367,8 +299,8 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
qDebug() << "WDSPRxSink::applySettings:"
<< " 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_spanLog2: " << settings.m_filterBank[settings.m_filterIndex].m_spanLog2
<< " 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
@ -377,16 +309,10 @@ void WDSPRxSink::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
@ -396,40 +322,79 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< " force: " << force;
if((m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth != settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth) ||
if((m_settings.m_filterBank[m_settings.m_filterIndex].m_highCutoff != settings.m_filterBank[settings.m_filterIndex].m_highCutoff) ||
(m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff != settings.m_filterBank[settings.m_filterIndex].m_lowCutoff) ||
(m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow != settings.m_filterBank[settings.m_filterIndex].m_fftWindow) || force)
(m_settings.m_filterBank[m_settings.m_filterIndex].m_fftWindow != settings.m_filterBank[settings.m_filterIndex].m_fftWindow) ||
(m_settings.m_dsb != settings.m_dsb) || force)
{
float band, lowCutoff;
float band, low, high, fLow, fHigh;
band = settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth;
lowCutoff = settings.m_filterBank[settings.m_filterIndex].m_lowCutoff;
band = settings.m_filterBank[settings.m_filterIndex].m_highCutoff;
high = band;
low = settings.m_filterBank[settings.m_filterIndex].m_lowCutoff;
if (band < 0) {
if (band < 0)
{
band = -band;
lowCutoff = -lowCutoff;
m_usb = false;
} else {
}
else
{
m_usb = true;
}
if (band < 100.0f)
{
band = 100.0f;
lowCutoff = 0;
}
m_Bandwidth = band;
m_LowCutoff = lowCutoff;
if (high < low)
{
if (settings.m_dsb)
{
fLow = high;
fHigh = -high;
}
else
{
fLow = high;
fHigh = low;
}
}
else
{
if (settings.m_dsb)
{
fLow = -high;
fHigh = high;
}
else
{
fLow = low;
fHigh = high;
}
}
Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > 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<<settings.m_agcTimeLog2);
m_agc.setThresholdEnable(settings.m_agcPowerThreshold != -WDSPRxSettings::m_minPowerThresholdDB);
double agcPowerThreshold = CalcDb::powerFromdB(settings.m_agcPowerThreshold) * (SDR_RX_SCALED*SDR_RX_SCALED);
int agcThresholdGate = (m_audioSampleRate / 1000) * settings.m_agcThresholdGate; // ms
bool agcClamping = settings.m_agcClamping;
if (m_agcNbSamples != agcNbSamples)
{
m_agc.resize(agcNbSamples, agcNbSamples/2, m_agcTarget);
m_agc.setStepDownDelay(agcNbSamples);
m_agcNbSamples = agcNbSamples;
}
if (m_agcPowerThreshold != agcPowerThreshold)
{
m_agc.setThreshold(agcPowerThreshold);
m_agcPowerThreshold = agcPowerThreshold;
}
if (m_agcThresholdGate != agcThresholdGate)
{
m_agc.setGate(agcThresholdGate);
m_agcThresholdGate = agcThresholdGate;
}
if (m_agcClamping != agcClamping)
{
m_agcClamping = agcClamping;
}
qDebug() << "WDSPRxSink::applySettings: AGC:"
<< " agcNbSamples: " << agcNbSamples
<< " agcPowerThreshold: " << agcPowerThreshold
<< " agcThresholdGate: " << agcThresholdGate
<< " agcClamping: " << agcClamping;
if ((m_settings.m_audioBinaural != settings.m_audioBinaural) || force) {
WDSP::PANEL::SetPanelBinaural(*m_rxa, settings.m_audioBinaural ? 1 : 0);
}
if ((m_settings.m_dnr != settings.m_dnr) || force) {
setDNR(settings.m_dnr);
if ((m_settings.m_audioFlipChannels != settings.m_audioFlipChannels) || force) {
WDSP::PANEL::SetPanelCopy(*m_rxa, settings.m_audioFlipChannels ? 3 : 0);
}
if ((m_settings.m_dnrScheme != settings.m_dnrScheme) || force) {
SSBFilter->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;

View File

@ -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<fftfilt::cmplx> m_squelchDelayLine;
bool m_audioActive; //!< True if an audio signal is produced (no AGC or AGC and above threshold)
Lowpass<Real> m_lowpassI;
Lowpass<Real> 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;

View File

@ -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()

View File

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

View File

@ -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);

39
wdsp/bufferprobe.hpp Normal file
View File

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

View File

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

View File

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

View File

@ -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();
}

View File

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