From 54866a1a1e978b41955e09446fc8f343b61695d6 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 14 Dec 2021 07:56:02 +0100 Subject: [PATCH] Remote output/source: use queue langth for rate control and derive rate from Tx side. Other fixes --- .../channeltx/remotesource/remotesource.cpp | 100 ++++++++-- plugins/channeltx/remotesource/remotesource.h | 27 +++ .../remotesource/remotesourcebaseband.cpp | 16 +- .../remotesource/remotesourcegui.cpp | 70 ++++++- .../channeltx/remotesource/remotesourcegui.h | 9 + .../channeltx/remotesource/remotesourcegui.ui | 186 +++++++++++++++++- .../remotesource/remotesourcesettings.cpp | 6 + .../remotesource/remotesourcesettings.h | 2 + .../samplesink/remoteoutput/remoteoutput.cpp | 109 +++------- .../samplesink/remoteoutput/remoteoutput.h | 16 +- sdrbase/channel/remotedatareadqueue.cpp | 3 +- sdrbase/resources/webapi/doc/html2/index.html | 8 +- .../doc/swagger/include/RemoteSource.yaml | 4 + .../api/swagger/include/RemoteSource.yaml | 4 + swagger/sdrangel/code/html2/index.html | 8 +- .../qt5/client/SWGRemoteSourceSettings.cpp | 46 +++++ .../code/qt5/client/SWGRemoteSourceSettings.h | 12 ++ 17 files changed, 503 insertions(+), 123 deletions(-) diff --git a/plugins/channeltx/remotesource/remotesource.cpp b/plugins/channeltx/remotesource/remotesource.cpp index 5dbc85caa..5522d7f4c 100644 --- a/plugins/channeltx/remotesource/remotesource.cpp +++ b/plugins/channeltx/remotesource/remotesource.cpp @@ -28,6 +28,8 @@ #include "SWGRemoteSourceReport.h" #include "dsp/devicesamplesink.h" +#include "dsp/hbfilterchainconverter.h" +#include "dsp/dspcommands.h" #include "device/deviceapi.h" #include "feature/feature.h" #include "settings/serializable.h" @@ -40,13 +42,17 @@ MESSAGE_CLASS_DEFINITION(RemoteSource::MsgConfigureRemoteSource, Message) MESSAGE_CLASS_DEFINITION(RemoteSource::MsgQueryStreamData, Message) MESSAGE_CLASS_DEFINITION(RemoteSource::MsgReportStreamData, Message) +MESSAGE_CLASS_DEFINITION(RemoteSource::MsgBasebandSampleRateNotification, Message) const char* const RemoteSource::m_channelIdURI = "sdrangel.channeltx.remotesource"; const char* const RemoteSource::m_channelId ="RemoteSource"; RemoteSource::RemoteSource(DeviceAPI *deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource), - m_deviceAPI(deviceAPI) + m_deviceAPI(deviceAPI), + m_centerFrequency(0), + m_frequencyOffset(0), + m_basebandSampleRate(48000) { setObjectName(m_channelId); @@ -96,7 +102,27 @@ void RemoteSource::pull(SampleVector::iterator& begin, unsigned int nbSamples) bool RemoteSource::handleMessage(const Message& cmd) { - if (MsgConfigureRemoteSource::match(cmd)) + if (DSPSignalNotification::match(cmd)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + + qDebug() << "RemoteSource::handleMessage: DSPSignalNotification:" + << " inputSampleRate: " << notif.getSampleRate() + << " centerFrequency: " << notif.getCenterFrequency(); + + m_basebandSampleRate = notif.getSampleRate(); + calculateFrequencyOffset(m_settings.m_log2Interp, m_settings.m_filterChainHash); // This is when device sample rate changes + m_centerFrequency = notif.getCenterFrequency(); + + if (m_guiMessageQueue) + { + MsgBasebandSampleRateNotification *msg = MsgBasebandSampleRateNotification::create(notif.getSampleRate()); + m_guiMessageQueue->push(msg); + } + + return true; + } + else if (MsgConfigureRemoteSource::match(cmd)) { MsgConfigureRemoteSource& cfg = (MsgConfigureRemoteSource&) cmd; qDebug() << "MsgConfigureRemoteSource::handleMessage: MsgConfigureRemoteSource"; @@ -158,27 +184,39 @@ bool RemoteSource::deserialize(const QByteArray& data) void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool force) { qDebug() << "RemoteSource::applySettings:" - << " m_dataAddress: " << settings.m_dataAddress - << " m_dataPort: " << settings.m_dataPort - << " m_rgbColor: " << settings.m_rgbColor - << " m_title: " << settings.m_title - << " m_useReverseAPI: " << settings.m_useReverseAPI - << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress - << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex - << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex - << " m_reverseAPIPort: " << settings.m_reverseAPIPort - << " force: " << force; + << "m_log2Interp:" << settings.m_log2Interp + << "m_filterChainHash:" << settings.m_filterChainHash + << "m_dataAddress:" << settings.m_dataAddress + << "m_dataPort:" << settings.m_dataPort + << "m_rgbColor:" << settings.m_rgbColor + << "m_title:" << settings.m_title + << "m_useReverseAPI:" << settings.m_useReverseAPI + << "m_reverseAPIAddress:" << settings.m_reverseAPIAddress + << "m_reverseAPIChannelIndex:" << settings.m_reverseAPIChannelIndex + << "m_reverseAPIDeviceIndex:" << settings.m_reverseAPIDeviceIndex + << "m_reverseAPIPort:" << settings.m_reverseAPIPort + << "force:" << force; QList reverseAPIKeys; + if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) { + reverseAPIKeys.append("log2Interp"); + } + if ((m_settings.m_filterChainHash != settings.m_filterChainHash) || force) { + reverseAPIKeys.append("filterChainHash"); + } if ((m_settings.m_dataAddress != settings.m_dataAddress) || force) { reverseAPIKeys.append("dataAddress"); } - if ((m_settings.m_dataPort != settings.m_dataPort) || force) { reverseAPIKeys.append("dataPort"); } + if ((m_settings.m_log2Interp != settings.m_log2Interp) + || (m_settings.m_filterChainHash != settings.m_filterChainHash) || force) { + calculateFrequencyOffset(settings.m_log2Interp, settings.m_filterChainHash); + } + if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only @@ -214,6 +252,23 @@ void RemoteSource::applySettings(const RemoteSourceSettings& settings, bool forc m_settings = settings; } +void RemoteSource::validateFilterChainHash(RemoteSourceSettings& settings) +{ + unsigned int s = 1; + + for (unsigned int i = 0; i < settings.m_log2Interp; i++) { + s *= 3; + } + + settings.m_filterChainHash = settings.m_filterChainHash >= s ? s-1 : settings.m_filterChainHash; +} + +void RemoteSource::calculateFrequencyOffset(uint32_t log2Interp, uint32_t filterChainHash) +{ + double shiftFactor = HBFilterChainConverter::getShiftFactor(log2Interp, filterChainHash); + m_frequencyOffset = m_basebandSampleRate * shiftFactor; +} + int RemoteSource::webapiSettingsGet( SWGSDRangel::SWGChannelSettings& response, QString& errorMessage) @@ -274,6 +329,12 @@ void RemoteSource::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("title")) { settings.m_title = *response.getRemoteSourceSettings()->getTitle(); } + if (channelSettingsKeys.contains("log2Interp")) { + settings.m_log2Interp = response.getRemoteSourceSettings()->getLog2Interp(); + } + if (channelSettingsKeys.contains("filterChainHash")) { + settings.m_filterChainHash = response.getRemoteSourceSettings()->getFilterChainHash(); + } if (channelSettingsKeys.contains("streamIndex")) { settings.m_streamIndex = response.getRemoteSourceSettings()->getStreamIndex(); } @@ -325,6 +386,8 @@ void RemoteSource::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response.getRemoteSourceSettings()->setTitle(new QString(settings.m_title)); } + response.getRemoteSourceSettings()->setLog2Interp(settings.m_log2Interp); + response.getRemoteSourceSettings()->setFilterChainHash(settings.m_filterChainHash); response.getRemoteSourceSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); if (response.getRemoteSourceSettings()->getReverseApiAddress()) { @@ -367,8 +430,9 @@ void RemoteSource::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& resp response.getRemoteSourceReport()->setUncorrectableErrorsCount(m_basebandSource->getNbUncorrectableErrors()); response.getRemoteSourceReport()->setNbOriginalBlocks(currentMeta.m_nbOriginalBlocks); response.getRemoteSourceReport()->setNbFecBlocks(currentMeta.m_nbFECBlocks); - response.getRemoteSourceReport()->setCenterFreq(currentMeta.m_centerFrequency); - response.getRemoteSourceReport()->setSampleRate(currentMeta.m_sampleRate); + response.getRemoteSourceReport()->setCenterFreq(m_frequencyOffset); + double channelSampleRate = ((double) m_basebandSampleRate) / (1<setSampleRate(channelSampleRate); response.getRemoteSourceReport()->setDeviceCenterFreq(m_deviceAPI->getSampleSink()->getCenterFrequency()); response.getRemoteSourceReport()->setDeviceSampleRate(m_deviceAPI->getSampleSink()->getSampleRate()); } @@ -445,6 +509,12 @@ void RemoteSource::webapiFormatChannelSettings( if (channelSettingsKeys.contains("rgbColor") || force) { swgRemoteSourceSettings->setRgbColor(settings.m_rgbColor); } + if (channelSettingsKeys.contains("log2Interp") || force) { + swgRemoteSourceSettings->setLog2Interp(settings.m_log2Interp); + } + if (channelSettingsKeys.contains("filterChainHash") || force) { + swgRemoteSourceSettings->setFilterChainHash(settings.m_filterChainHash); + } if (channelSettingsKeys.contains("title") || force) { swgRemoteSourceSettings->setTitle(new QString(settings.m_title)); } diff --git a/plugins/channeltx/remotesource/remotesource.h b/plugins/channeltx/remotesource/remotesource.h index 3f478a8a0..b178ea503 100644 --- a/plugins/channeltx/remotesource/remotesource.h +++ b/plugins/channeltx/remotesource/remotesource.h @@ -154,6 +154,27 @@ public: { } }; + + class MsgBasebandSampleRateNotification : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgBasebandSampleRateNotification* create(int sampleRate) { + return new MsgBasebandSampleRateNotification(sampleRate); + } + + int getBasebandSampleRate() const { return m_sampleRate; } + + private: + + MsgBasebandSampleRateNotification(int sampleRate) : + Message(), + m_sampleRate(sampleRate) + { } + + int m_sampleRate; + }; + RemoteSource(DeviceAPI *deviceAPI); virtual ~RemoteSource(); @@ -218,7 +239,13 @@ private: QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; + uint64_t m_centerFrequency; + int64_t m_frequencyOffset; + uint32_t m_basebandSampleRate; + void applySettings(const RemoteSourceSettings& settings, bool force = false); + static void validateFilterChainHash(RemoteSourceSettings& settings); + void calculateFrequencyOffset(uint32_t log2Interp, uint32_t filterChainHash); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); void webapiReverseSendSettings(QList& channelSettingsKeys, const RemoteSourceSettings& settings, bool force); void sendChannelSettings( diff --git a/plugins/channeltx/remotesource/remotesourcebaseband.cpp b/plugins/channeltx/remotesource/remotesourcebaseband.cpp index c74917aed..3e82ed9c5 100644 --- a/plugins/channeltx/remotesource/remotesourcebaseband.cpp +++ b/plugins/channeltx/remotesource/remotesourcebaseband.cpp @@ -164,15 +164,23 @@ bool RemoteSourceBaseband::handleMessage(const Message& cmd) void RemoteSourceBaseband::applySettings(const RemoteSourceSettings& settings, bool force) { qDebug() << "RemoteSourceBaseband::applySettings:" - << " m_dataAddress: " << settings.m_dataAddress - << " m_dataPort: " << settings.m_dataPort - << " force: " << force; + << "m_log2Interp:" << settings.m_log2Interp + << "m_filterChainHash:" << settings.m_filterChainHash + << "m_dataAddress:" << settings.m_dataAddress + << "m_dataPort:" << settings.m_dataPort + << "force:" << force; if ((settings.m_dataAddress != m_settings.m_dataAddress) || (settings.m_dataPort != m_settings.m_dataPort) || force) { m_source.dataBind(settings.m_dataAddress, settings.m_dataPort); } + if ((m_settings.m_filterChainHash != settings.m_filterChainHash) + || (m_settings.m_log2Interp != settings.m_log2Interp) || force) + { + m_channelizer->setInterpolation(settings.m_log2Interp, settings.m_filterChainHash); + } + m_settings = settings; } @@ -184,4 +192,4 @@ int RemoteSourceBaseband::getChannelSampleRate() const void RemoteSourceBaseband::newRemoteSampleRate(unsigned int sampleRate) { m_channelizer->setChannelization(sampleRate, 0); // Adjust channelizer to match remote sample rate -} \ No newline at end of file +} diff --git a/plugins/channeltx/remotesource/remotesourcegui.cpp b/plugins/channeltx/remotesource/remotesourcegui.cpp index db72f107f..523adf778 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.cpp +++ b/plugins/channeltx/remotesource/remotesourcegui.cpp @@ -19,6 +19,7 @@ #include "device/deviceapi.h" #include "device/deviceuiset.h" +#include "dsp/hbfilterchainconverter.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" #include "mainwindow.h" @@ -65,7 +66,14 @@ bool RemoteSourceGUI::deserialize(const QByteArray& data) bool RemoteSourceGUI::handleMessage(const Message& message) { - if (RemoteSource::MsgConfigureRemoteSource::match(message)) + if (RemoteSource::MsgBasebandSampleRateNotification::match(message)) + { + RemoteSource::MsgBasebandSampleRateNotification& notif = (RemoteSource::MsgBasebandSampleRateNotification&) message; + m_basebandSampleRate = notif.getBasebandSampleRate(); + displayRateAndShift(); + return true; + } + else if (RemoteSource::MsgConfigureRemoteSource::match(message)) { const RemoteSource::MsgConfigureRemoteSource& cfg = (RemoteSource::MsgConfigureRemoteSource&) message; m_settings = cfg.getSettings(); @@ -149,6 +157,8 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_remoteSampleRate(48000), + m_basebandSampleRate(48000), + m_shiftFrequencyFactor(0.0), m_countUnrecoverable(0), m_countRecovered(0), m_lastCountUnrecoverable(0), @@ -189,6 +199,8 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_time.start(); displaySettings(); + displayPosition(); + displayRateAndShift(); applySettings(true); } @@ -233,6 +245,25 @@ void RemoteSourceGUI::displaySettings() blockApplySettings(false); } +void RemoteSourceGUI::displayRateAndShift() +{ + int shift = m_shiftFrequencyFactor * m_basebandSampleRate; + double channelSampleRate = ((double) m_basebandSampleRate) / (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 RemoteSourceGUI::displayPosition() +{ + ui->filterChainIndex->setText(tr("%1").arg(m_settings.m_filterChainHash)); + QString s; + HBFilterChainConverter::convertToString(m_settings.m_log2Interp, m_settings.m_filterChainHash, s); + ui->filterChainText->setText(s); +} + void RemoteSourceGUI::displayStreamIndex() { if (m_deviceUISet->m_deviceMIMOEngine) { @@ -319,6 +350,18 @@ void RemoteSourceGUI::onMenuDialogCalled(const QPoint &p) resetContextMenuType(); } +void RemoteSourceGUI::on_interpolationFactor_currentIndexChanged(int index) +{ + m_settings.m_log2Interp = index; + applyInterpolation(); +} + +void RemoteSourceGUI::on_position_valueChanged(int value) +{ + m_settings.m_filterChainHash = value; + applyPosition(); +} + void RemoteSourceGUI::on_dataAddress_returnPressed() { m_settings.m_dataAddress = ui->dataAddress->text(); @@ -364,6 +407,31 @@ void RemoteSourceGUI::on_eventCountsReset_clicked(bool checked) displayEventTimer(); } +void RemoteSourceGUI::applyInterpolation() +{ + uint32_t maxHash = 1; + + for (uint32_t i = 0; i < m_settings.m_log2Interp; i++) { + maxHash *= 3; + } + + ui->position->setMaximum(maxHash-1); + ui->position->setValue(m_settings.m_filterChainHash); + m_settings.m_filterChainHash = ui->position->value(); + applyPosition(); +} + +void RemoteSourceGUI::applyPosition() +{ + ui->filterChainIndex->setText(tr("%1").arg(m_settings.m_filterChainHash)); + QString s; + m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Interp, m_settings.m_filterChainHash, s); + ui->filterChainText->setText(s); + + displayRateAndShift(); + applySettings(); +} + void RemoteSourceGUI::displayEventCounts() { QString nstr = QString("%1").arg(m_countUnrecoverable, 3, 10, QChar('0')); diff --git a/plugins/channeltx/remotesource/remotesourcegui.h b/plugins/channeltx/remotesource/remotesourcegui.h index bcd66a805..b2e04fbe9 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.h +++ b/plugins/channeltx/remotesource/remotesourcegui.h @@ -57,7 +57,9 @@ private: ChannelMarker m_channelMarker; RemoteSourceSettings m_settings; int m_remoteSampleRate; + int m_basebandSampleRate; bool m_doApplySettings; + double m_shiftFrequencyFactor; //!< Channel frequency shift factor RemoteSource* m_remoteSrc; MessageQueue m_inputMessageQueue; @@ -78,6 +80,8 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); + void displayRateAndShift(); + void displayPosition(); void displayStreamIndex(); bool handleMessage(const Message& message); @@ -88,8 +92,13 @@ private: void displayEventStatus(int recoverableCount, int unrecoverableCount); void displayEventTimer(); + void applyInterpolation(); + void applyPosition(); + private slots: void handleSourceMessages(); + void on_interpolationFactor_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/channeltx/remotesource/remotesourcegui.ui b/plugins/channeltx/remotesource/remotesourcegui.ui index 756ba24c7..81d1463f1 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.ui +++ b/plugins/channeltx/remotesource/remotesourcegui.ui @@ -7,7 +7,7 @@ 0 0 320 - 140 + 221 @@ -43,7 +43,7 @@ 10 10 301 - 121 + 191 @@ -156,6 +156,188 @@ + + + + 3 + + + + + + + Int + + + + + + + + 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 + + + + + + + diff --git a/plugins/channeltx/remotesource/remotesourcesettings.cpp b/plugins/channeltx/remotesource/remotesourcesettings.cpp index d67675827..060435807 100644 --- a/plugins/channeltx/remotesource/remotesourcesettings.cpp +++ b/plugins/channeltx/remotesource/remotesourcesettings.cpp @@ -34,6 +34,8 @@ void RemoteSourceSettings::resetToDefaults() m_dataPort = 9090; m_rgbColor = QColor(140, 4, 4).rgb(); m_title = "Remote source"; + m_log2Interp = 0; + m_filterChainHash = 0; m_channelMarker = nullptr; m_streamIndex = 0; m_useReverseAPI = false; @@ -57,6 +59,8 @@ QByteArray RemoteSourceSettings::serialize() const s.writeU32(9, m_reverseAPIChannelIndex); s.writeS32(10, m_streamIndex); s.writeBlob(11, m_rollupState); + s.writeU32(12, m_log2Interp); + s.writeU32(13, m_filterChainHash); return s.final(); } @@ -103,6 +107,8 @@ bool RemoteSourceSettings::deserialize(const QByteArray& data) m_reverseAPIChannelIndex = tmp > 99 ? 99 : tmp; d.readS32(10, &m_streamIndex, 0); d.readBlob(11, &m_rollupState); + d.readU32(13, &m_filterChainHash, 0); + d.readS32(14, &m_streamIndex, 0); return true; } diff --git a/plugins/channeltx/remotesource/remotesourcesettings.h b/plugins/channeltx/remotesource/remotesourcesettings.h index 379890388..3cd4e4c65 100644 --- a/plugins/channeltx/remotesource/remotesourcesettings.h +++ b/plugins/channeltx/remotesource/remotesourcesettings.h @@ -29,6 +29,8 @@ struct RemoteSourceSettings uint16_t m_dataPort; //!< Listening data port quint32 m_rgbColor; QString m_title; + uint32_t m_log2Interp; + uint32_t m_filterChainHash; int m_streamIndex; bool m_useReverseAPI; QString m_reverseAPIAddress; diff --git a/plugins/samplesink/remoteoutput/remoteoutput.cpp b/plugins/samplesink/remoteoutput/remoteoutput.cpp index b5742ddd2..375a2d84f 100644 --- a/plugins/samplesink/remoteoutput/remoteoutput.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutput.cpp @@ -46,8 +46,6 @@ MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteData, Message) MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgReportRemoteFixedData, Message) MESSAGE_CLASS_DEFINITION(RemoteOutput::MsgRequestFixedData, Message) -const uint32_t RemoteOutput::NbSamplesForRateCorrection = 5000000; - RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) : m_deviceAPI(deviceAPI), m_settings(), @@ -59,15 +57,7 @@ RemoteOutput::RemoteOutput(DeviceAPI *deviceAPI) : m_masterTimer(deviceAPI->getMasterTimer()), m_tickCount(0), m_greaterTickCount(0), - m_tickMultiplier(1), - m_lastRemoteSampleCount(0), - m_lastSampleCount(0), - m_lastRemoteTimestampRateCorrection(0), - m_lastTimestampRateCorrection(0), - m_lastQueueLength(-2), - m_nbRemoteSamplesSinceRateCorrection(0), - m_nbSamplesSinceRateCorrection(0), - m_chunkSizeCorrection(0) + m_tickMultiplier(1) { m_deviceAPI->setNbSinkStreams(1); m_networkManager = new QNetworkAccessManager(); @@ -102,14 +92,9 @@ bool RemoteOutput::start() m_remoteOutputWorker->connectTimer(m_masterTimer); startWorker(); - // restart auto rate correction - m_lastRemoteTimestampRateCorrection = 0; - m_lastTimestampRateCorrection = 0; - m_lastQueueLength = -2; // set first value out of bounds - m_chunkSizeCorrection = 0; - mutexLocker.unlock(); - //applySettings(m_generalSettings, m_settings, true); + applySampleRate(); + qDebug("RemoteOutput::start: started"); return true; @@ -337,9 +322,9 @@ void RemoteOutput::applySampleRate() m_remoteOutputWorker->setSamplerate(m_sampleRate); } - m_tickMultiplier = (21*NbSamplesForRateCorrection) / (2*m_sampleRate); // two times per sample filling period plus small extension - m_tickMultiplier /= 20; // greter tick (one per second) - m_tickMultiplier = m_tickMultiplier < 1 ? 1 : m_tickMultiplier; // not below 1 second + m_tickMultiplier = 480000 / m_sampleRate; + m_tickMultiplier = m_tickMultiplier < 1 ? 1 : m_tickMultiplier > 10 ? 10 : m_tickMultiplier; + m_greaterTickCount = 0; DSPSignalNotification *notif = new DSPSignalNotification(m_sampleRate, m_centerFrequency); m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); @@ -548,7 +533,7 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString& { MsgReportRemoteData::RemoteData msgRemoteData; QJsonObject report = jsonObject["RemoteSourceReport"].toObject(); - uint64_t centerFrequency = report["deviceCenterFreq"].toInt(); + uint64_t centerFrequency = report["deviceCenterFreq"].toInt() + report["centerFreq"].toInt(); if (centerFrequency != m_centerFrequency) { @@ -556,7 +541,7 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString& applyCenterFrequency(); } - int remoteRate = report["deviceSampleRate"].toInt(); + int remoteRate = report["sampleRate"].toInt(); if (remoteRate != m_sampleRate) { @@ -572,7 +557,6 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString& msgRemoteData.m_queueSize = queueSize; int queueLength = report["queueLength"].toInt(); msgRemoteData.m_queueLength = queueLength; - int queueLengthPercent = (queueLength*100)/queueSize; uint64_t remoteTimestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt(); msgRemoteData.m_timestampUs = remoteTimestampUs; int intRemoteSampleCount = report["samplesCount"].toInt(); @@ -593,62 +577,11 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString& return; } - if (++m_greaterTickCount != m_tickMultiplier) { - return; - } - - uint32_t remoteSampleCountDelta; - - if (remoteSampleCount < m_lastRemoteSampleCount) { - remoteSampleCountDelta = (0xFFFFFFFFU - m_lastRemoteSampleCount) + remoteSampleCount + 1; - } else { - remoteSampleCountDelta = remoteSampleCount - m_lastRemoteSampleCount; - } - - uint32_t sampleCountDelta, sampleCount; - uint64_t timestampUs; - sampleCount = m_remoteOutputWorker->getSamplesCount(timestampUs); - - if (sampleCount < m_lastSampleCount) { - sampleCountDelta = (0xFFFFFFFFU - m_lastSampleCount) + sampleCount + 1; - } else { - sampleCountDelta = sampleCount - m_lastSampleCount; - } - - // on initial state wait for queue stabilization - if ((m_lastRemoteTimestampRateCorrection == 0) && (queueLength >= m_lastQueueLength-1) && (queueLength <= m_lastQueueLength+1)) + if (++m_greaterTickCount == m_tickMultiplier) { - m_lastRemoteTimestampRateCorrection = remoteTimestampUs; - m_lastTimestampRateCorrection = timestampUs; - m_nbRemoteSamplesSinceRateCorrection = 0; - m_nbSamplesSinceRateCorrection = 0; + queueLengthCompensation(m_sampleRate, queueLength, queueSize); + m_greaterTickCount = 0; } - else - { - m_nbRemoteSamplesSinceRateCorrection += remoteSampleCountDelta; - m_nbSamplesSinceRateCorrection += sampleCountDelta; - - qDebug("RemoteOutput::analyzeApiReply: queueLengthPercent: %d m_nbSamplesSinceRateCorrection: %u", - queueLengthPercent, - m_nbRemoteSamplesSinceRateCorrection); - - if (m_nbRemoteSamplesSinceRateCorrection > NbSamplesForRateCorrection) - { - sampleRateCorrection(remoteTimestampUs - m_lastRemoteTimestampRateCorrection, - timestampUs - m_lastTimestampRateCorrection, - m_nbRemoteSamplesSinceRateCorrection, - m_nbSamplesSinceRateCorrection); - m_lastRemoteTimestampRateCorrection = remoteTimestampUs; - m_lastTimestampRateCorrection = timestampUs; - m_nbRemoteSamplesSinceRateCorrection = 0; - m_nbSamplesSinceRateCorrection = 0; - } - } - - m_lastRemoteSampleCount = remoteSampleCount; - m_lastSampleCount = sampleCount; - m_lastQueueLength = queueLength; - m_greaterTickCount = 0; } else if (jsonObject.contains("remoteOutputSettings")) { @@ -685,16 +618,18 @@ void RemoteOutput::analyzeApiReply(const QJsonObject& jsonObject, const QString& } } -void RemoteOutput::sampleRateCorrection(double remoteTimeDeltaUs, double timeDeltaUs, uint32_t remoteSampleCount, uint32_t sampleCount) +void RemoteOutput::queueLengthCompensation( + int nominalSR, + int queueLength, + int queueSize +) { - double deltaSR = (remoteSampleCount/remoteTimeDeltaUs) - (sampleCount/timeDeltaUs); - double chunkCorr = 50000 * deltaSR; // for 50ms chunk intervals (50000us) - m_chunkSizeCorrection += roundf(chunkCorr); - - qDebug("RemoteOutput::sampleRateCorrection: remote: %u / %f us local: %u / %f us corr: %d (%f) samples", - remoteSampleCount, remoteTimeDeltaUs, sampleCount, timeDeltaUs, m_chunkSizeCorrection, chunkCorr); - - MsgConfigureRemoteOutputChunkCorrection* message = MsgConfigureRemoteOutputChunkCorrection::create(m_chunkSizeCorrection); + int deltaQueueBlocks = (queueSize/2) - queueLength; + int blockMultiplier = nominalSR / 4000; + blockMultiplier = blockMultiplier < 12 ? 12 : blockMultiplier; + int corr = deltaQueueBlocks * blockMultiplier; + qDebug("RemoteOutput::queueLengthCompensation: deltaQueueBlocks: %d corr: %d", deltaQueueBlocks, corr); + MsgConfigureRemoteOutputChunkCorrection* message = MsgConfigureRemoteOutputChunkCorrection::create(corr); getInputMessageQueue()->push(message); } diff --git a/plugins/samplesink/remoteoutput/remoteoutput.h b/plugins/samplesink/remoteoutput/remoteoutput.h index 1fbe5aaad..12a292cb4 100644 --- a/plugins/samplesink/remoteoutput/remoteoutput.h +++ b/plugins/samplesink/remoteoutput/remoteoutput.h @@ -288,16 +288,6 @@ private: QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; - uint32_t m_lastRemoteSampleCount; - uint32_t m_lastSampleCount; - uint64_t m_lastRemoteTimestampRateCorrection; - uint64_t m_lastTimestampRateCorrection; - int m_lastQueueLength; - uint32_t m_nbRemoteSamplesSinceRateCorrection; - uint32_t m_nbSamplesSinceRateCorrection; - int m_chunkSizeCorrection; - static const uint32_t NbSamplesForRateCorrection; - void startWorker(); void stopWorker(); void applySettings(const RemoteOutputSettings& settings, bool force = false); @@ -306,7 +296,11 @@ private: void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); void analyzeApiReply(const QJsonObject& jsonObject, const QString& answer); - void sampleRateCorrection(double remoteTimeDeltaUs, double timeDeltaUs, uint32_t remoteSampleCount, uint32_t sampleCount); + void queueLengthCompensation( + int nominalSR, + int queueLength, + int queueSize + ); void webapiReverseSendSettings(QList& deviceSettingsKeys, const RemoteOutputSettings& settings, bool force); void webapiReverseSendStartStop(bool start); diff --git a/sdrbase/channel/remotedatareadqueue.cpp b/sdrbase/channel/remotedatareadqueue.cpp index 8eaf29138..1b9fceeb7 100644 --- a/sdrbase/channel/remotedatareadqueue.cpp +++ b/sdrbase/channel/remotedatareadqueue.cpp @@ -91,13 +91,14 @@ void RemoteDataReadQueue::readSample(Sample& s, bool scaleForTx) m_sampleIndex = 0; convertDataToSample(s, m_blockIndex, m_sampleIndex, scaleForTx); m_sampleIndex++; - m_sampleCount++; } else { s = Sample{0, 0}; } + m_sampleCount++; + return; } diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 752aac43e..7493bc9f9 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -10004,6 +10004,12 @@ margin-bottom: 20px; "title" : { "type" : "string" }, + "log2Interp" : { + "type" : "integer" + }, + "filterChainHash" : { + "type" : "integer" + }, "streamIndex" : { "type" : "integer", "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." @@ -51597,7 +51603,7 @@ except ApiException as e:
- Generated 2021-12-12T19:10:03.240+01:00 + Generated 2021-12-12T22:32:39.234+01:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/RemoteSource.yaml b/sdrbase/resources/webapi/doc/swagger/include/RemoteSource.yaml index 04023bb2d..38431203d 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/RemoteSource.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/RemoteSource.yaml @@ -11,6 +11,10 @@ RemoteSourceSettings: type: integer title: type: string + log2Interp: + type: integer + filterChainHash: + type: integer streamIndex: description: MIMO channel. Not relevant when connected to SI (single Rx). type: integer diff --git a/swagger/sdrangel/api/swagger/include/RemoteSource.yaml b/swagger/sdrangel/api/swagger/include/RemoteSource.yaml index 752d44862..660e0f4f5 100644 --- a/swagger/sdrangel/api/swagger/include/RemoteSource.yaml +++ b/swagger/sdrangel/api/swagger/include/RemoteSource.yaml @@ -11,6 +11,10 @@ RemoteSourceSettings: type: integer title: type: string + log2Interp: + type: integer + filterChainHash: + type: integer streamIndex: description: MIMO channel. Not relevant when connected to SI (single Rx). type: integer diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 752aac43e..7493bc9f9 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -10004,6 +10004,12 @@ margin-bottom: 20px; "title" : { "type" : "string" }, + "log2Interp" : { + "type" : "integer" + }, + "filterChainHash" : { + "type" : "integer" + }, "streamIndex" : { "type" : "integer", "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." @@ -51597,7 +51603,7 @@ except ApiException as e:
- Generated 2021-12-12T19:10:03.240+01:00 + Generated 2021-12-12T22:32:39.234+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGRemoteSourceSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGRemoteSourceSettings.cpp index fdbabcdd7..a01c118a4 100644 --- a/swagger/sdrangel/code/qt5/client/SWGRemoteSourceSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGRemoteSourceSettings.cpp @@ -36,6 +36,10 @@ SWGRemoteSourceSettings::SWGRemoteSourceSettings() { m_rgb_color_isSet = false; title = nullptr; m_title_isSet = false; + log2_interp = 0; + m_log2_interp_isSet = false; + filter_chain_hash = 0; + m_filter_chain_hash_isSet = false; stream_index = 0; m_stream_index_isSet = false; use_reverse_api = 0; @@ -66,6 +70,10 @@ SWGRemoteSourceSettings::init() { m_rgb_color_isSet = false; title = new QString(""); m_title_isSet = false; + log2_interp = 0; + m_log2_interp_isSet = false; + filter_chain_hash = 0; + m_filter_chain_hash_isSet = false; stream_index = 0; m_stream_index_isSet = false; use_reverse_api = 0; @@ -94,6 +102,8 @@ SWGRemoteSourceSettings::cleanup() { } + + if(reverse_api_address != nullptr) { delete reverse_api_address; } @@ -124,6 +134,10 @@ SWGRemoteSourceSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + ::SWGSDRangel::setValue(&log2_interp, pJson["log2Interp"], "qint32", ""); + + ::SWGSDRangel::setValue(&filter_chain_hash, pJson["filterChainHash"], "qint32", ""); + ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); @@ -166,6 +180,12 @@ SWGRemoteSourceSettings::asJsonObject() { if(title != nullptr && *title != QString("")){ toJsonValue(QString("title"), title, obj, QString("QString")); } + if(m_log2_interp_isSet){ + obj->insert("log2Interp", QJsonValue(log2_interp)); + } + if(m_filter_chain_hash_isSet){ + obj->insert("filterChainHash", QJsonValue(filter_chain_hash)); + } if(m_stream_index_isSet){ obj->insert("streamIndex", QJsonValue(stream_index)); } @@ -231,6 +251,26 @@ SWGRemoteSourceSettings::setTitle(QString* title) { this->m_title_isSet = true; } +qint32 +SWGRemoteSourceSettings::getLog2Interp() { + return log2_interp; +} +void +SWGRemoteSourceSettings::setLog2Interp(qint32 log2_interp) { + this->log2_interp = log2_interp; + this->m_log2_interp_isSet = true; +} + +qint32 +SWGRemoteSourceSettings::getFilterChainHash() { + return filter_chain_hash; +} +void +SWGRemoteSourceSettings::setFilterChainHash(qint32 filter_chain_hash) { + this->filter_chain_hash = filter_chain_hash; + this->m_filter_chain_hash_isSet = true; +} + qint32 SWGRemoteSourceSettings::getStreamIndex() { return stream_index; @@ -318,6 +358,12 @@ SWGRemoteSourceSettings::isSet(){ if(title && *title != QString("")){ isObjectUpdated = true; break; } + if(m_log2_interp_isSet){ + isObjectUpdated = true; break; + } + if(m_filter_chain_hash_isSet){ + isObjectUpdated = true; break; + } if(m_stream_index_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGRemoteSourceSettings.h b/swagger/sdrangel/code/qt5/client/SWGRemoteSourceSettings.h index ce0acbbb7..944a825ff 100644 --- a/swagger/sdrangel/code/qt5/client/SWGRemoteSourceSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGRemoteSourceSettings.h @@ -55,6 +55,12 @@ public: QString* getTitle(); void setTitle(QString* title); + qint32 getLog2Interp(); + void setLog2Interp(qint32 log2_interp); + + qint32 getFilterChainHash(); + void setFilterChainHash(qint32 filter_chain_hash); + qint32 getStreamIndex(); void setStreamIndex(qint32 stream_index); @@ -92,6 +98,12 @@ private: QString* title; bool m_title_isSet; + qint32 log2_interp; + bool m_log2_interp_isSet; + + qint32 filter_chain_hash; + bool m_filter_chain_hash_isSet; + qint32 stream_index; bool m_stream_index_isSet;