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\