1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-17 05:41:56 -05:00

Remote channel sink: decimation and shift: GUI changes

This commit is contained in:
f4exb 2019-04-25 15:48:47 +02:00
parent e55f33ffc9
commit 07c5bd19b8
11 changed files with 418 additions and 18 deletions

1
debian/changelog vendored
View File

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

View File

@ -15,13 +15,15 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "remotesinkgui.h"
#include <QLocale>
#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<<m_settings.m_log2Decim);
QLocale loc;
ui->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

View File

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

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>320</width>
<height>100</height>
<height>157</height>
</rect>
</property>
<property name="sizePolicy">
@ -43,7 +43,7 @@
<x>10</x>
<y>10</y>
<width>301</width>
<height>81</height>
<height>141</height>
</rect>
</property>
<property name="windowTitle">
@ -65,6 +65,188 @@
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QVBoxLayout" name="decimationLayer">
<property name="spacing">
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="decimationStageLayer">
<item>
<widget class="QLabel" name="decimationLabel">
<property name="text">
<string>Dec</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="decimationFactor">
<property name="maximumSize">
<size>
<width>55</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Decimation factor</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="channelRateText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Effective channel rate (kS/s)</string>
</property>
<property name="text">
<string>0000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filterChainText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Filter chain stages left to right (L: low, C: center, H: high) </string>
</property>
<property name="text">
<string>LLLLLL</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="offsetFrequencyText">
<property name="minimumSize">
<size>
<width>85</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Offset frequency with thousands separator (Hz)</string>
</property>
<property name="text">
<string>-9,999,999 Hz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="decimationShiftLayer">
<property name="rightMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="positionLabel">
<property name="text">
<string>Pos</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="position">
<property name="toolTip">
<string>Center frequency position</string>
</property>
<property name="maximum">
<number>2</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filterChainIndex">
<property name="minimumSize">
<size>
<width>24</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Filter chain hash code</string>
</property>
<property name="text">
<string>000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="dataAddressLayout">
<item>
@ -292,19 +474,6 @@
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QString>
#include "hbfilterchainconverter.h"
double HBFilterChainConverter::convertToIndexes(unsigned int log2, unsigned int chainHash, std::vector<unsigned int>& 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;
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_DSP_HBFILTERCHAINCONVERTER_H
#define SDRBASE_DSP_HBFILTERCHAINCONVERTER_H
#include <vector>
#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<unsigned int>& 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

View File

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