mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-05-31 06:12:26 -04:00
SDRDaemonSink: transfer sample rate control from GUI to core (1)
This commit is contained in:
parent
5d856f8a80
commit
a0a9dc4dae
@ -99,6 +99,7 @@ SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceUISet *deviceUISet, QWidget* parent) :
|
|||||||
|
|
||||||
SDRdaemonSinkGui::~SDRdaemonSinkGui()
|
SDRdaemonSinkGui::~SDRdaemonSinkGui()
|
||||||
{
|
{
|
||||||
|
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
||||||
delete m_networkManager;
|
delete m_networkManager;
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
@ -684,8 +685,8 @@ void SDRdaemonSinkGui::sampleRateCorrection(int queueLength, int queueSize, int6
|
|||||||
int chunkCorr = -roundf(sampleCorr);
|
int chunkCorr = -roundf(sampleCorr);
|
||||||
m_chunkSizeCorrection += chunkCorr;
|
m_chunkSizeCorrection += chunkCorr;
|
||||||
|
|
||||||
qDebug("SDRdaemonSinkGui::sampleRateCorrection: %d (%d) samples", m_chunkSizeCorrection, chunkCorr);
|
// qDebug("SDRdaemonSinkGui::sampleRateCorrection: %d (%d) samples", m_chunkSizeCorrection, chunkCorr);
|
||||||
|
//
|
||||||
SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection* message = SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection::create(m_chunkSizeCorrection);
|
// SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection* message = SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection::create(m_chunkSizeCorrection);
|
||||||
m_deviceSampleSink->getInputMessageQueue()->push(message);
|
// m_deviceSampleSink->getInputMessageQueue()->push(message);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
|
||||||
#include "SWGDeviceSettings.h"
|
#include "SWGDeviceSettings.h"
|
||||||
#include "SWGDeviceState.h"
|
#include "SWGDeviceState.h"
|
||||||
@ -44,13 +47,24 @@ SDRdaemonSinkOutput::SDRdaemonSinkOutput(DeviceSinkAPI *deviceAPI) :
|
|||||||
m_sdrDaemonSinkThread(0),
|
m_sdrDaemonSinkThread(0),
|
||||||
m_deviceDescription("SDRdaemonSink"),
|
m_deviceDescription("SDRdaemonSink"),
|
||||||
m_startingTimeStamp(0),
|
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()
|
SDRdaemonSinkOutput::~SDRdaemonSinkOutput()
|
||||||
{
|
{
|
||||||
|
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
||||||
stop();
|
stop();
|
||||||
|
delete m_networkManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDRdaemonSinkOutput::destroy()
|
void SDRdaemonSinkOutput::destroy()
|
||||||
@ -433,4 +447,117 @@ void SDRdaemonSinkOutput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport&
|
|||||||
response.getSdrDaemonSinkReport()->setSampleCount(m_sdrDaemonSinkThread ? (int) m_sdrDaemonSinkThread->getSamplesCount() : 0);
|
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);
|
||||||
|
}
|
||||||
|
@ -17,18 +17,23 @@
|
|||||||
#ifndef INCLUDE_SDRDAEMONSINKOUTPUT_H
|
#ifndef INCLUDE_SDRDAEMONSINKOUTPUT_H
|
||||||
#define INCLUDE_SDRDAEMONSINKOUTPUT_H
|
#define INCLUDE_SDRDAEMONSINKOUTPUT_H
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
#include "dsp/devicesamplesink.h"
|
#include "dsp/devicesamplesink.h"
|
||||||
|
|
||||||
#include "sdrdaemonsinksettings.h"
|
#include "sdrdaemonsinksettings.h"
|
||||||
|
|
||||||
class SDRdaemonSinkThread;
|
class SDRdaemonSinkThread;
|
||||||
class DeviceSinkAPI;
|
class DeviceSinkAPI;
|
||||||
|
class QNetworkAccessManager;
|
||||||
|
class QNetworkReply;
|
||||||
|
class QJsonObject;
|
||||||
|
|
||||||
class SDRdaemonSinkOutput : public DeviceSampleSink {
|
class SDRdaemonSinkOutput : public DeviceSampleSink {
|
||||||
public:
|
public:
|
||||||
@ -165,10 +170,27 @@ private:
|
|||||||
QString m_deviceDescription;
|
QString m_deviceDescription;
|
||||||
std::time_t m_startingTimeStamp;
|
std::time_t m_startingTimeStamp;
|
||||||
const QTimer& m_masterTimer;
|
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 applySettings(const SDRdaemonSinkSettings& settings, bool force = false);
|
||||||
void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const SDRdaemonSinkSettings& settings);
|
void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const SDRdaemonSinkSettings& settings);
|
||||||
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
|
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
|
#endif // INCLUDE_SDRDAEMONSINKOUTPUT_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user