From 07c5bd19b8b4bf3238a087203038f1c0eb10e6c4 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 25 Apr 2019 15:48:47 +0200 Subject: [PATCH] Remote channel sink: decimation and shift: GUI changes --- debian/changelog | 1 + .../channelrx/remotesink/remotesinkgui.cpp | 55 ++++- plugins/channelrx/remotesink/remotesinkgui.h | 7 + plugins/channelrx/remotesink/remotesinkgui.ui | 199 ++++++++++++++++-- .../channelrx/remotesink/remotesinkplugin.cpp | 2 +- .../remotesink/remotesinksettings.cpp | 7 + .../channelrx/remotesink/remotesinksettings.h | 2 + sdrbase/CMakeLists.txt | 2 + sdrbase/dsp/hbfilterchainconverter.cpp | 118 +++++++++++ sdrbase/dsp/hbfilterchainconverter.h | 39 ++++ sdrbase/sdrbase.pro | 4 +- 11 files changed, 418 insertions(+), 18 deletions(-) create mode 100644 sdrbase/dsp/hbfilterchainconverter.cpp create mode 100644 sdrbase/dsp/hbfilterchainconverter.h diff --git a/debian/changelog b/debian/changelog index e966de081..cc7a9b899 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,6 @@ sdrangel (4.5.6-1) unstable; urgency=medium + * Remote channel sink: implemented decimation with possible center shift. Issue #331 * Remote input: fixed version display * DSD demod: save PLL enable and autio mute in preset diff --git a/plugins/channelrx/remotesink/remotesinkgui.cpp b/plugins/channelrx/remotesink/remotesinkgui.cpp index a1365bdb8..56d55c716 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.cpp +++ b/plugins/channelrx/remotesink/remotesinkgui.cpp @@ -15,13 +15,15 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "remotesinkgui.h" +#include #include "device/devicesourceapi.h" #include "device/deviceuiset.h" #include "gui/basicchannelsettingsdialog.h" +#include "dsp/hbfilterchainconverter.h" #include "mainwindow.h" +#include "remotesinkgui.h" #include "remotesink.h" #include "ui_remotesinkgui.h" @@ -87,6 +89,7 @@ bool RemoteSinkGUI::handleMessage(const Message& message) m_channelMarker.setBandwidth(notif.getSampleRate()); m_sampleRate = notif.getSampleRate(); updateTxDelayTime(); + displayRateAndShift(); return true; } else if (RemoteSink::MsgConfigureRemoteSink::match(message)) @@ -171,6 +174,7 @@ void RemoteSinkGUI::displaySettings() m_channelMarker.setCenterFrequency(0); m_channelMarker.setTitle(m_settings.m_title); m_channelMarker.setBandwidth(m_sampleRate); // TODO + m_channelMarker.setMovable(false); // do not let user move the center arbitrarily m_channelMarker.blockSignals(false); m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only @@ -178,6 +182,8 @@ void RemoteSinkGUI::displaySettings() setWindowTitle(m_channelMarker.getTitle()); blockApplySettings(true); + ui->decimationFactor->setCurrentIndex(m_settings.m_log2Decim); + ui->position->setValue(m_settings.m_filterChainHash); ui->dataAddress->setText(m_settings.m_dataAddress); ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort)); QString s = QString::number(128 + m_settings.m_nbFECBlocks, 'f', 0); @@ -186,9 +192,21 @@ void RemoteSinkGUI::displaySettings() ui->txDelayText->setText(tr("%1%").arg(m_settings.m_txDelay)); ui->txDelay->setValue(m_settings.m_txDelay); updateTxDelayTime(); + applyDecimation(); blockApplySettings(false); } +void RemoteSinkGUI::displayRateAndShift() +{ + int shift = m_shiftFrequencyFactor * m_sampleRate; + double channelSampleRate = ((double) m_sampleRate) / (1<offsetFrequencyText->setText(tr("%1 Hz").arg(loc.toString(shift))); + ui->channelRateText->setText(tr("%1k").arg(QString::number(channelSampleRate / 1000.0, 'g', 5))); + m_channelMarker.setCenterFrequency(shift); + m_channelMarker.setBandwidth(channelSampleRate); +} + void RemoteSinkGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); @@ -244,6 +262,18 @@ void RemoteSinkGUI::onMenuDialogCalled(const QPoint &p) applySettings(); } +void RemoteSinkGUI::on_decimationFactor_currentIndexChanged(int index) +{ + m_settings.m_log2Decim = index; + applyDecimation(); +} + +void RemoteSinkGUI::on_position_valueChanged(int value) +{ + m_settings.m_filterChainHash = value; + applyPosition(); +} + void RemoteSinkGUI::on_dataAddress_returnPressed() { m_settings.m_dataAddress = ui->dataAddress->text(); @@ -312,6 +342,29 @@ void RemoteSinkGUI::updateTxDelayTime() ui->txDelayTime->setText(tr("%1µs").arg(QString::number(delay*1e6, 'f', 0))); } +void RemoteSinkGUI::applyDecimation() +{ + uint32_t maxHash = 1; + + for (uint32_t i = 0; i < m_settings.m_log2Decim; i++) { + maxHash *= 3; + } + + ui->position->setMaximum(maxHash-1); + m_settings.m_filterChainHash = ui->position->value(); + applyPosition(); +} + +void RemoteSinkGUI::applyPosition() +{ + ui->filterChainIndex->setText(tr("%1").arg(m_settings.m_filterChainHash)); + QString s; + m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Decim, m_settings.m_filterChainHash, s); + ui->filterChainText->setText(s); + + displayRateAndShift(); +} + void RemoteSinkGUI::tick() { if (++m_tickCount == 20) { // once per second diff --git a/plugins/channelrx/remotesink/remotesinkgui.h b/plugins/channelrx/remotesink/remotesinkgui.h index 5aabf6ecb..2b37290f6 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.h +++ b/plugins/channelrx/remotesink/remotesinkgui.h @@ -64,6 +64,7 @@ private: RemoteSinkSettings m_settings; int m_sampleRate; quint64 m_deviceCenterFrequency; //!< Center frequency in device + double m_shiftFrequencyFactor; //!< Channel frequency shift factor bool m_doApplySettings; RemoteSink* m_remoteSink; @@ -78,13 +79,19 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); + void displayRateAndShift(); void updateTxDelayTime(); void leaveEvent(QEvent*); void enterEvent(QEvent*); + void applyDecimation(); + void applyPosition(); + private slots: void handleSourceMessages(); + void on_decimationFactor_currentIndexChanged(int index); + void on_position_valueChanged(int value); void on_dataAddress_returnPressed(); void on_dataPort_returnPressed(); void on_dataApplyButton_clicked(bool checked); diff --git a/plugins/channelrx/remotesink/remotesinkgui.ui b/plugins/channelrx/remotesink/remotesinkgui.ui index 5f0236f5a..5d65923be 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.ui +++ b/plugins/channelrx/remotesink/remotesinkgui.ui @@ -7,7 +7,7 @@ 0 0 320 - 100 + 157 @@ -43,7 +43,7 @@ 10 10 301 - 81 + 141 @@ -65,6 +65,188 @@ 2 + + + + 3 + + + + + + + Dec + + + + + + + + 55 + 16777215 + + + + Decimation factor + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + + 50 + 0 + + + + Effective channel rate (kS/s) + + + 0000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 50 + 0 + + + + Filter chain stages left to right (L: low, C: center, H: high) + + + LLLLLL + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 85 + 0 + + + + Offset frequency with thousands separator (Hz) + + + -9,999,999 Hz + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + 10 + + + + + Pos + + + + + + + Center frequency position + + + 2 + + + 1 + + + Qt::Horizontal + + + + + + + + 24 + 0 + + + + Filter chain hash code + + + 000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + @@ -292,19 +474,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/plugins/channelrx/remotesink/remotesinkplugin.cpp b/plugins/channelrx/remotesink/remotesinkplugin.cpp index 08d8e3fed..318b5b5ee 100644 --- a/plugins/channelrx/remotesink/remotesinkplugin.cpp +++ b/plugins/channelrx/remotesink/remotesinkplugin.cpp @@ -27,7 +27,7 @@ const PluginDescriptor RemoteSinkPlugin::m_pluginDescriptor = { QString("Remote channel sink"), - QString("4.5.2"), + QString("4.5.6"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/remotesink/remotesinksettings.cpp b/plugins/channelrx/remotesink/remotesinksettings.cpp index a4299b3a8..b07aa1db0 100644 --- a/plugins/channelrx/remotesink/remotesinksettings.cpp +++ b/plugins/channelrx/remotesink/remotesinksettings.cpp @@ -42,6 +42,8 @@ void RemoteSinkSettings::resetToDefaults() m_dataPort = 9090; m_rgbColor = QColor(140, 4, 4).rgb(); m_title = "Remote sink"; + m_log2Decim = 0; + m_filterChainHash = 0; m_channelMarker = nullptr; m_useReverseAPI = false; m_reverseAPIAddress = "127.0.0.1"; @@ -64,6 +66,8 @@ QByteArray RemoteSinkSettings::serialize() const s.writeU32(9, m_reverseAPIPort); s.writeU32(10, m_reverseAPIDeviceIndex); s.writeU32(11, m_reverseAPIChannelIndex); + s.writeU32(12, m_log2Decim); + s.writeU32(13, m_filterChainHash); return s.final(); } @@ -117,6 +121,9 @@ bool RemoteSinkSettings::deserialize(const QByteArray& data) m_reverseAPIDeviceIndex = tmp > 99 ? 99 : tmp; d.readU32(11, &tmp, 0); m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp; + d.readU32(12, &tmp, 0); + m_log2Decim = tmp > 6 ? 6 : tmp; + d.readU32(13, &m_filterChainHash, 0); return true; } diff --git a/plugins/channelrx/remotesink/remotesinksettings.h b/plugins/channelrx/remotesink/remotesinksettings.h index ce4c4001b..f82bf52b6 100644 --- a/plugins/channelrx/remotesink/remotesinksettings.h +++ b/plugins/channelrx/remotesink/remotesinksettings.h @@ -37,6 +37,8 @@ struct RemoteSinkSettings uint16_t m_dataPort; quint32 m_rgbColor; QString m_title; + uint32_t m_log2Decim; + uint32_t m_filterChainHash; bool m_useReverseAPI; QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index f14f3f026..f871e7958 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -46,6 +46,7 @@ set(sdrbase_SOURCES dsp/filerecord.cpp dsp/freqlockcomplex.cpp dsp/interpolator.cpp + dsp/hbfilterchainconverter.cpp dsp/hbfiltertraits.cpp dsp/lowpass.cpp dsp/nco.cpp @@ -148,6 +149,7 @@ set(sdrbase_HEADERS dsp/filerecord.h dsp/freqlockcomplex.h dsp/gfft.h + dsp/hbfilterchainconverter.h dsp/iirfilter.h dsp/interpolator.h dsp/hbfiltertraits.h diff --git a/sdrbase/dsp/hbfilterchainconverter.cpp b/sdrbase/dsp/hbfilterchainconverter.cpp new file mode 100644 index 000000000..3afa37484 --- /dev/null +++ b/sdrbase/dsp/hbfilterchainconverter.cpp @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// 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 as version 3 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "hbfilterchainconverter.h" + +double HBFilterChainConverter::convertToIndexes(unsigned int log2, unsigned int chainHash, std::vector& chainIndexes) +{ + chainIndexes.clear(); + + if (log2 == 0) { + return 0.0; + } + + unsigned int s = 1; + unsigned int d = 1; + unsigned int u = chainHash; + + for (unsigned int i = 0; i < log2; i++) + { + s *= 3; + d *= 2; + } + + u %= s; // scale + unsigned int ix = log2; + double shift = 0.0; + double shift_stage = 1.0 / (1<<(log2+1)); + + // base3 conversion + do + { + int r = u % 3; + chainIndexes.push_back(r); + shift += (r-1)*shift_stage; + shift_stage *= 2; + u /= 3; + ix--; + } while (u); + + // continue shift with leading zeroes. ix has the number of leading zeroes. + for (unsigned int i = 0; i < ix; i++) + { + shift -= shift_stage; + shift_stage *= 2; + } + + return shift; +} + +double HBFilterChainConverter::convertToString(unsigned int log2, unsigned int chainHash, QString& chainString) +{ + if (log2 == 0) + { + chainString = "C"; + return 0.0; + } + + unsigned int s = 1; + unsigned int d = 1; + unsigned int u = chainHash; + chainString = ""; + + for (unsigned int i = 0; i < log2; i++) + { + s *= 3; + d *= 2; + } + + u %= s; // scale + unsigned int ix = log2; + double shift = 0.0; + double shift_stage = 1.0 / (1<<(log2+1)); + + // base3 conversion + do + { + int r = u % 3; + + if (r == 0) { + chainString = "L" + chainString; + } else if (r == 1) { + chainString = "C" + chainString; + } else if (r == 2) { + chainString = "H" + chainString; + } + + shift += (r-1)*shift_stage; + shift_stage *= 2; + u /= 3; + ix--; + } while (u); + + // continue shift with leading zeroes. ix has the number of leading zeroes. + for (unsigned int i = 0; i < ix; i++) + { + chainString = "L" + chainString; + shift -= shift_stage; + shift_stage *= 2; + } + + return shift; +} \ No newline at end of file diff --git a/sdrbase/dsp/hbfilterchainconverter.h b/sdrbase/dsp/hbfilterchainconverter.h new file mode 100644 index 000000000..c572261bf --- /dev/null +++ b/sdrbase/dsp/hbfilterchainconverter.h @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// 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 as version 3 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_DSP_HBFILTERCHAINCONVERTER_H +#define SDRBASE_DSP_HBFILTERCHAINCONVERTER_H + +#include +#include "export.h" + +class QString; + +class SDRBASE_API HBFilterChainConverter +{ +public: + // Converts the chain hash as a base3 number each digit representing a filter stage from lower (LSD) to upper level (MSD) + // The corresponding log2 of decimation or interpolation factor is also the number of filter stages + // A vector of indexes as base3 digits is filled in (0: low band, 1: center band, : high band) + // The shift factor of center frequency is returned. The actual shift is obtained by multiplying this factor by the sample rate. + static double convertToIndexes(unsigned int log2, unsigned int chainHash, std::vector& chainIndexes); + // Same but used only for display giving a string representation of the filter chain + static double convertToString(unsigned int log2, unsigned int chainHash, QString& chainString); +}; + +#endif // SDRBASE_DSP_HBFILTERCHAINCONVERTER_H \ No newline at end of file diff --git a/sdrbase/sdrbase.pro b/sdrbase/sdrbase.pro index 94d7209a2..02b223003 100644 --- a/sdrbase/sdrbase.pro +++ b/sdrbase/sdrbase.pro @@ -96,6 +96,7 @@ SOURCES += audio/audiodevicemanager.cpp\ dsp/filerecord.cpp\ dsp/freqlockcomplex.cpp\ dsp/interpolator.cpp\ + dsp/hbfilterchainconverter.cpp \ dsp/hbfiltertraits.cpp\ dsp/lowpass.cpp\ dsp/nco.cpp\ @@ -146,7 +147,7 @@ HEADERS += audio/audiodevicemanager.h\ audio/audiooutput.h\ audio/audioinput.h\ audio/audionetsink.h\ - audio/audioresampler.h\ + audio/audioresampler.h\ channel/channelsinkapi.h\ channel/channelsourceapi.h\ channel/remotedataqueue.h\ @@ -181,6 +182,7 @@ HEADERS += audio/audiodevicemanager.h\ dsp/filerecord.h\ dsp/freqlockcomplex.h\ dsp/gfft.h\ + dsp/hbfilterchainconverter.h \ dsp/hbfiltertraits.h\ dsp/iirfilter.h\ dsp/interpolator.h\