From a0a9dc4dae7397d39768f9ca7f851655ae03fbf4 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 3 Sep 2018 09:17:29 +0200 Subject: [PATCH] SDRDaemonSink: transfer sample rate control from GUI to core (1) --- .../sdrdaemonsink/sdrdaemonsinkgui.cpp | 9 +- .../sdrdaemonsink/sdrdaemonsinkoutput.cpp | 129 +++++++++++++++++- .../sdrdaemonsink/sdrdaemonsinkoutput.h | 26 +++- 3 files changed, 157 insertions(+), 7 deletions(-) diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp index 6ffe18fc5..d3dab32f9 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkgui.cpp @@ -99,6 +99,7 @@ SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : SDRdaemonSinkGui::~SDRdaemonSinkGui() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); delete m_networkManager; delete ui; } @@ -684,8 +685,8 @@ void SDRdaemonSinkGui::sampleRateCorrection(int queueLength, int queueSize, int6 int chunkCorr = -roundf(sampleCorr); m_chunkSizeCorrection += chunkCorr; - qDebug("SDRdaemonSinkGui::sampleRateCorrection: %d (%d) samples", m_chunkSizeCorrection, chunkCorr); - - SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection* message = SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection::create(m_chunkSizeCorrection); - m_deviceSampleSink->getInputMessageQueue()->push(message); +// qDebug("SDRdaemonSinkGui::sampleRateCorrection: %d (%d) samples", m_chunkSizeCorrection, chunkCorr); +// +// SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection* message = SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection::create(m_chunkSizeCorrection); +// m_deviceSampleSink->getInputMessageQueue()->push(message); } diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp index f83c9b7bd..4da9fb24d 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include "SWGDeviceSettings.h" #include "SWGDeviceState.h" @@ -44,13 +47,24 @@ SDRdaemonSinkOutput::SDRdaemonSinkOutput(DeviceSinkAPI *deviceAPI) : m_sdrDaemonSinkThread(0), m_deviceDescription("SDRdaemonSink"), m_startingTimeStamp(0), - m_masterTimer(deviceAPI->getMasterTimer()) + m_masterTimer(deviceAPI->getMasterTimer()), + m_tickCount(0), + m_lastSampleCount(0), + m_lastTimestampUs(0), + m_lastTimestampRateCorrection(0), + m_nbSamplesSinceRateCorrection(0), + m_chunkSizeCorrection(0) { + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + connect(&m_masterTimer, SIGNAL(timeout()), this, SLOT(tick())); } SDRdaemonSinkOutput::~SDRdaemonSinkOutput() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); stop(); + delete m_networkManager; } void SDRdaemonSinkOutput::destroy() @@ -433,4 +447,117 @@ void SDRdaemonSinkOutput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response.getSdrDaemonSinkReport()->setSampleCount(m_sdrDaemonSinkThread ? (int) m_sdrDaemonSinkThread->getSamplesCount() : 0); } +void SDRdaemonSinkOutput::tick() +{ + if (++m_tickCount == 20*60) // once per minute + { + QString reportURL; + reportURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/report") + .arg(m_settings.m_apiAddress) + .arg(m_settings.m_apiPort) + .arg(m_settings.m_deviceIndex) + .arg(m_settings.m_channelIndex); + + m_networkRequest.setUrl(QUrl(reportURL)); + m_networkManager->get(m_networkRequest); + + m_tickCount = 0; + } +} + +void SDRdaemonSinkOutput::networkManagerFinished(QNetworkReply *reply) +{ + if (reply->error()) + { + qInfo("SDRdaemonSinkOutput::networkManagerFinished: error: %s", qPrintable(reply->errorString())); + return; + } + + QString answer = reply->readAll(); + + try + { + QByteArray jsonBytes(answer.toStdString().c_str()); + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(jsonBytes, &error); + + if (error.error == QJsonParseError::NoError) + { + analyzeApiReply(doc.object()); + } + else + { + QString errorMsg = QString("Reply JSON error: ") + error.errorString() + QString(" at offset ") + QString::number(error.offset); + qInfo().noquote() << "SDRdaemonSinkOutput::networkManagerFinished" << errorMsg; + } + } + catch (const std::exception& ex) + { + QString errorMsg = QString("Error parsing request: ") + ex.what(); + qInfo().noquote() << "SDRdaemonSinkOutput::networkManagerFinished" << errorMsg; + } +} + +void SDRdaemonSinkOutput::analyzeApiReply(const QJsonObject& jsonObject) +{ + QString infoLine; + + if (jsonObject.contains("SDRDaemonChannelSourceReport")) + { + QJsonObject report = jsonObject["SDRDaemonChannelSourceReport"].toObject(); + int queueSize = report["queueSize"].toInt(); + queueSize = queueSize == 0 ? 10 : queueSize; + int queueLength = report["queueLength"].toInt(); + int queueLengthPercent = (queueLength*100)/queueSize; + uint64_t timestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt(); + + uint32_t sampleCountDelta, sampleCount; + sampleCount = report["samplesCount"].toInt(); + + qDebug("SDRdaemonSinkOutput::analyzeApiReply: sampleCount: %u m_nbSamplesSinceRateCorrection: %u", + sampleCount, + m_nbSamplesSinceRateCorrection); + + if (sampleCount < m_lastSampleCount) { + sampleCountDelta = (0xFFFFFFFFU - m_lastSampleCount) + sampleCount + 1; + } else { + sampleCountDelta = sampleCount - m_lastSampleCount; + } + + if (sampleCountDelta != 0) + { + if (m_lastTimestampRateCorrection == 0) { + m_lastTimestampRateCorrection = timestampUs; + } + else + { + m_nbSamplesSinceRateCorrection += sampleCountDelta; + + if ((m_nbSamplesSinceRateCorrection > 20000000) && ((queueLengthPercent > 60) || (queueLengthPercent < 40))) + { + sampleRateCorrection(queueLength, queueSize, timestampUs - m_lastTimestampRateCorrection); + m_lastTimestampRateCorrection = timestampUs; + m_nbSamplesSinceRateCorrection = 0; + } + } + } + + m_lastSampleCount = sampleCount; + m_lastTimestampUs = timestampUs; + } +} + +void SDRdaemonSinkOutput::sampleRateCorrection(int queueLength, int queueSize, int64_t timeDeltaUs) +{ + int nbBlocksDiff = queueLength - (queueSize/2); + int nbSamplesDiff = nbBlocksDiff * 127 * 127; + float sampleCorr = (nbSamplesDiff * 50000.0) / timeDeltaUs; // correction for ~50ms chunks (50000 us) + int chunkCorr = -roundf(sampleCorr); + m_chunkSizeCorrection += chunkCorr; + + qDebug("SDRdaemonSinkOutput::sampleRateCorrection: %d (%d) samples", m_chunkSizeCorrection, chunkCorr); + + MsgConfigureSDRdaemonSinkChunkCorrection* message = MsgConfigureSDRdaemonSinkChunkCorrection::create(m_chunkSizeCorrection); + getInputMessageQueue()->push(message); +} diff --git a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h index a5b4ada47..598626617 100644 --- a/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h +++ b/plugins/samplesink/sdrdaemonsink/sdrdaemonsinkoutput.h @@ -17,18 +17,23 @@ #ifndef INCLUDE_SDRDAEMONSINKOUTPUT_H #define INCLUDE_SDRDAEMONSINKOUTPUT_H -#include -#include #include #include #include +#include +#include +#include + #include "dsp/devicesamplesink.h" #include "sdrdaemonsinksettings.h" class SDRdaemonSinkThread; class DeviceSinkAPI; +class QNetworkAccessManager; +class QNetworkReply; +class QJsonObject; class SDRdaemonSinkOutput : public DeviceSampleSink { public: @@ -165,10 +170,27 @@ private: QString m_deviceDescription; std::time_t m_startingTimeStamp; const QTimer& m_masterTimer; + uint32_t m_tickCount; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + uint32_t m_lastSampleCount; + uint64_t m_lastTimestampUs; + uint64_t m_lastTimestampRateCorrection; + uint32_t m_nbSamplesSinceRateCorrection; + int m_chunkSizeCorrection; void applySettings(const SDRdaemonSinkSettings& settings, bool force = false); void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const SDRdaemonSinkSettings& settings); void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + + void analyzeApiReply(const QJsonObject& jsonObject); + void sampleRateCorrection(int queueLength, int queueSize, int64_t timeDeltaUs); + +private slots: + void tick(); + void networkManagerFinished(QNetworkReply *reply); }; #endif // INCLUDE_SDRDAEMONSINKOUTPUT_H