1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 09:48:45 -05:00

SDRDaemonSink GUI: sample rate active feedback

This commit is contained in:
f4exb 2018-08-31 07:38:30 +02:00
parent 90fb223b01
commit f8383f8cff
11 changed files with 142 additions and 99 deletions

View File

@ -58,6 +58,7 @@ SDRdaemonSinkGui::SDRdaemonSinkGui(DeviceUISet *deviceUISet, QWidget* parent) :
m_lastCountUnrecoverable = 0;
m_lastCountRecovered = 0;
m_lastSampleCount = 0;
m_lastTimestampRateCorrection = 0;
m_resetCounts = true;
m_paletteGreenText.setColor(QPalette::WindowText, Qt::green);
@ -176,12 +177,6 @@ bool SDRdaemonSinkGui::handleMessage(const Message& message)
blockApplySettings(false);
return true;
}
else if (SDRdaemonSinkOutput::MsgReportSDRdaemonSinkStreamTiming::match(message))
{
m_samplesCount = ((SDRdaemonSinkOutput::MsgReportSDRdaemonSinkStreamTiming&)message).getSamplesCount();
updateWithStreamTime();
return true;
}
else if (SDRdaemonSinkOutput::MsgStartStop::match(message))
{
SDRdaemonSinkOutput::MsgStartStop& notif = (SDRdaemonSinkOutput::MsgStartStop&) message;
@ -339,7 +334,7 @@ void SDRdaemonSinkGui::on_apiAddress_returnPressed()
QString infoURL = QString("http://%1:%2/sdrdaemon").arg(m_settings.m_apiAddress).arg(m_settings.m_apiPort);
m_networkRequest.setUrl(QUrl(infoURL));
m_networkManager->get(m_networkRequest);
m_networkManager->get(m_networkRequest);
}
void SDRdaemonSinkGui::on_apiPort_returnPressed()
@ -360,7 +355,7 @@ void SDRdaemonSinkGui::on_apiPort_returnPressed()
QString infoURL = QString("http://%1:%2/sdrdaemon").arg(m_settings.m_apiAddress).arg(m_settings.m_apiPort);
m_networkRequest.setUrl(QUrl(infoURL));
m_networkManager->get(m_networkRequest);
m_networkManager->get(m_networkRequest);
}
void SDRdaemonSinkGui::on_dataAddress_returnPressed()
@ -472,35 +467,17 @@ void SDRdaemonSinkGui::displayEventTimer()
ui->eventCountsTimeText->setText(s_time);
}
void SDRdaemonSinkGui::updateWithStreamTime()
{
int t_sec = 0;
int t_msec = 0;
if (m_settings.m_sampleRate > 0){
t_msec = ((m_samplesCount * 1000) / m_settings.m_sampleRate) % 1000;
t_sec = m_samplesCount / m_settings.m_sampleRate;
}
QTime t(0, 0, 0, 0);
t = t.addSecs(t_sec);
t = t.addMSecs(t_msec);
QString s_timems = t.toString("HH:mm:ss.zzz");
//ui->relTimeText->setText(s_timems); TODO with absolute time
}
void SDRdaemonSinkGui::tick()
{
if ((++m_tickCount & 0xf) == 0) // 16*50ms ~800ms
if (++m_tickCount == 20) // once per second
{
QString reportURL = QString("http://%1:%2/sdrdaemon/channel/report").arg(m_settings.m_apiAddress).arg(m_settings.m_apiPort);
m_networkRequest.setUrl(QUrl(reportURL));
m_networkManager->get(m_networkRequest);
// SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkStreamTiming* message = SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkStreamTiming::create();
// m_deviceSampleSink->getInputMessageQueue()->push(message);
displayEventTimer();
m_tickCount = 0;
}
}
@ -556,12 +533,22 @@ void SDRdaemonSinkGui::analyzeApiReply(const QJsonObject& jsonObject)
int queueLength = report["queueLength"].toInt();
QString queueLengthText = QString("%1/%2").arg(queueLength).arg(queueSize);
ui->queueLengthText->setText(queueLengthText);
ui->queueLengthGauge->setValue((queueLength*100)/queueSize);
int queueLengthPercent = (queueLength*100)/queueSize;
ui->queueLengthGauge->setValue(queueLengthPercent);
int unrecoverableCount = report["uncorrectableErrorsCount"].toInt();
int recoverableCount = report["correctableErrorsCount"].toInt();
int sampleCount = report["samplesCount"].toInt();
LimitedCounter<uint32_t, 2000000000> sampleCount(report["samplesCount"].toInt());
uint64_t timestampUs = report["tvSec"].toInt()*1000000ULL + report["tvUSec"].toInt();
if (m_lastTimestampRateCorrection == 0) {
m_lastTimestampRateCorrection = timestampUs;
}
if ((timestampUs - m_lastTimestampRateCorrection > 600e6) && ((queueLengthPercent > 60) || (queueLengthPercent < 40)))
{
m_lastTimestampRateCorrection = timestampUs;
}
if (!m_resetCounts)
{
int recoverableCountDelta = recoverableCount - m_lastCountRecovered;
@ -572,12 +559,19 @@ void SDRdaemonSinkGui::analyzeApiReply(const QJsonObject& jsonObject)
displayEventCounts();
}
if ((sampleCount - m_lastSampleCount) == 0) {
LimitedCounter<uint32_t, 2000000000> sampleCountDelta = sampleCount - m_lastSampleCount;
if (sampleCountDelta.value() == 0) {
ui->allFramesDecoded->setStyleSheet("QToolButton { background-color : blue; }");
}
double remoteStreamRate = (sampleCount - m_lastSampleCount) / (double) (timestampUs - m_lastTimestampUs);
ui->remoteStreamRateText->setText(QString("%1").arg(remoteStreamRate * 1e6, 0, 'f', 0));
double remoteStreamRate = sampleCountDelta.value()*1e6 / (double) (timestampUs - m_lastTimestampUs);
if (remoteStreamRate != 0)
{
m_rateMovingAverage(remoteStreamRate);
ui->remoteStreamRateText->setText(QString("%1").arg(m_rateMovingAverage.instantAverage(), 0, 'f', 0));
}
m_resetCounts = false;
m_lastCountRecovered = recoverableCount;
@ -610,3 +604,16 @@ void SDRdaemonSinkGui::analyzeApiReply(const QJsonObject& jsonObject)
ui->infoText->setText(infoLine);
}
}
void SDRdaemonSinkGui::sampleRateCorrection(int queueLength, int queueSize, int64_t timeDelta)
{
int nbBlocksDiff = queueLength - (queueSize/2);
int nbSamplesDiff = nbBlocksDiff * 127 * 127;
float sampleCorr = (nbSamplesDiff * 50000.0) / timeDelta;
int chunkCorr = roundf(sampleCorr);
qDebug("SDRdaemonSinkGui::sampleRateCorrection: %d samples", -chunkCorr);
SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection* message = SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection::create(-chunkCorr);
m_deviceSampleSink->getInputMessageQueue()->push(message);
}

View File

@ -24,6 +24,8 @@
#include <QNetworkRequest>
#include "util/messagequeue.h"
#include "util/limitedcounter.h"
#include "util/movingaverage.h"
#include "sdrdaemonsinksettings.h"
#include "sdrdaemonsinkoutput.h"
@ -69,7 +71,8 @@ private:
int m_sampleRate;
quint64 m_deviceCenterFrequency; //!< Center frequency in device
int m_samplesCount;
std::size_t m_tickCount;
MovingAverageUtil<double, double, 120> m_rateMovingAverage; // ~2mn average
uint32_t m_tickCount;
std::size_t m_nbSinceLastFlowCheck;
int m_lastEngineState;
bool m_doApplySettings;
@ -79,8 +82,9 @@ private:
uint32_t m_countRecovered;
uint32_t m_lastCountUnrecoverable;
uint32_t m_lastCountRecovered;
uint32_t m_lastSampleCount;
uint64_t m_lastTimestampUs;
LimitedCounter<uint32_t, 2000000000> m_lastSampleCount;
uint64_t m_lastTimestampUs;
uint64_t m_lastTimestampRateCorrection;
bool m_resetCounts;
QTime m_time;
@ -98,13 +102,13 @@ private:
void displayTime();
void sendControl(bool force = false);
void sendSettings();
void updateWithStreamTime();
void updateSampleRateAndFrequency();
void updateTxDelayTooltip();
void displayEventCounts();
void displayEventStatus(int recoverableCount, int unrecoverableCount);
void displayEventTimer();
void analyzeApiReply(const QJsonObject& jsonObject);
void sampleRateCorrection(int queueLength, int queueSize, int64_t timeDelta);
private slots:
void handleInputMessages();

View File

@ -36,9 +36,7 @@
MESSAGE_CLASS_DEFINITION(SDRdaemonSinkOutput::MsgConfigureSDRdaemonSink, Message)
MESSAGE_CLASS_DEFINITION(SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkWork, Message)
MESSAGE_CLASS_DEFINITION(SDRdaemonSinkOutput::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkStreamTiming, Message)
MESSAGE_CLASS_DEFINITION(SDRdaemonSinkOutput::MsgConfigureSDRdaemonSinkChunkCorrection, Message)
MESSAGE_CLASS_DEFINITION(SDRdaemonSinkOutput::MsgReportSDRdaemonSinkStreamTiming, Message)
SDRdaemonSinkOutput::SDRdaemonSinkOutput(DeviceSinkAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
@ -211,18 +209,6 @@ bool SDRdaemonSinkOutput::handleMessage(const Message& message)
return true;
}
else if (MsgConfigureSDRdaemonSinkStreamTiming::match(message))
{
MsgReportSDRdaemonSinkStreamTiming *report;
if (m_sdrDaemonSinkThread != 0 && getMessageQueueToGUI())
{
report = MsgReportSDRdaemonSinkStreamTiming::create(m_sdrDaemonSinkThread->getSamplesCount());
getMessageQueueToGUI()->push(report);
}
return true;
}
else if (MsgConfigureSDRdaemonSinkChunkCorrection::match(message))
{
MsgConfigureSDRdaemonSinkChunkCorrection& conf = (MsgConfigureSDRdaemonSinkChunkCorrection&) message;

View File

@ -114,43 +114,6 @@ public:
{ }
};
class MsgConfigureSDRdaemonSinkStreamTiming : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgConfigureSDRdaemonSinkStreamTiming* create()
{
return new MsgConfigureSDRdaemonSinkStreamTiming();
}
private:
MsgConfigureSDRdaemonSinkStreamTiming() :
Message()
{ }
};
class MsgReportSDRdaemonSinkStreamTiming : public Message {
MESSAGE_CLASS_DECLARATION
public:
std::size_t getSamplesCount() const { return m_samplesCount; }
static MsgReportSDRdaemonSinkStreamTiming* create(std::size_t samplesCount)
{
return new MsgReportSDRdaemonSinkStreamTiming(samplesCount);
}
protected:
std::size_t m_samplesCount;
MsgReportSDRdaemonSinkStreamTiming(std::size_t samplesCount) :
Message(),
m_samplesCount(samplesCount)
{ }
};
SDRdaemonSinkOutput(DeviceSinkAPI *deviceAPI);
virtual ~SDRdaemonSinkOutput();
virtual void destroy();

View File

@ -67,7 +67,7 @@ private:
int m_samplesChunkSize;
SampleSourceFifo* m_sampleFifo;
std::size_t m_samplesCount;
uint32_t m_samplesCount;
int m_chunkCorrection;
int m_samplerate;

View File

@ -0,0 +1,84 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Edouard Griffiths, F4EXB. //
// //
// 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 //
// //
// 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_UTIL_LIMITEDCOUNTER_H_
#define SDRBASE_UTIL_LIMITEDCOUNTER_H_
#include <stdint.h>
template<typename StoreType, uint32_t Limit>
class LimitedCounter
{
public:
LimitedCounter() :
m_counter(0)
{}
LimitedCounter(StoreType value) :
m_counter(value < Limit ? value : Limit - 1)
{}
void operator=(const LimitedCounter& rhs) {
m_counter = rhs.m_counter;
}
LimitedCounter& operator++() {
m_counter++;
if (m_counter >= Limit) {
m_counter = 0;
}
return *this;
}
LimitedCounter operator++(int) {
LimitedCounter temp = *this ;
m_counter++;
if (m_counter >= Limit) {
m_counter = 0;
}
return temp;
}
LimitedCounter& operator+=(const uint32_t rhs) {
m_counter += rhs;
if (m_counter >= Limit) {
m_counter -= Limit;
}
return *this;
}
StoreType value() const { return m_counter; }
friend LimitedCounter operator-(const LimitedCounter lhs, const LimitedCounter& rhs)
{
LimitedCounter result;
if (lhs.m_counter < rhs.m_counter) {
result.m_counter = Limit - lhs.m_counter + rhs.m_counter + 1;
} else {
result.m_counter = lhs.m_counter - rhs.m_counter;
}
return result;
}
private:
StoreType m_counter;
};
#endif /* SDRBASE_UTIL_LIMITEDCOUNTER_H_ */

View File

@ -58,6 +58,7 @@ class MovingAverageUtil
double asDouble() const { return ((double)m_total) / N; }
float asFloat() const { return ((float)m_total) / N; }
operator T() const { return m_total / N; }
T instantAverage() const { return m_total / (m_num_samples == 0 ? 1 : m_num_samples); }
private:
T m_samples[N];

View File

@ -50,7 +50,6 @@ SDRDaemonChannelSource::SDRDaemonChannelSource(DeviceSinkAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
m_sourceThread(0),
m_running(false),
m_samplesCount(0),
m_nbCorrectableErrors(0),
m_nbUncorrectableErrors(0)
{
@ -77,7 +76,6 @@ SDRDaemonChannelSource::~SDRDaemonChannelSource()
void SDRDaemonChannelSource::pull(Sample& sample)
{
m_dataReadQueue.readSample(sample);
m_samplesCount++;
}
void SDRDaemonChannelSource::start()
@ -235,8 +233,8 @@ void SDRDaemonChannelSource::handleDataBlock(SDRDaemonDataBlock* dataBlock)
}
// update counters
if (dataBlock->m_rxControlBlock.m_recoveryCount > paramsCM256.RecoveryCount) {
m_nbUncorrectableErrors += SDRDaemonNbOrginalBlocks - dataBlock->m_rxControlBlock.m_originalCount;
if (dataBlock->m_rxControlBlock.m_originalCount < SDRDaemonNbOrginalBlocks - paramsCM256.RecoveryCount) {
m_nbUncorrectableErrors += SDRDaemonNbOrginalBlocks - paramsCM256.RecoveryCount - dataBlock->m_rxControlBlock.m_originalCount;
} else {
m_nbCorrectableErrors += dataBlock->m_rxControlBlock.m_recoveryCount;
}
@ -409,7 +407,7 @@ void SDRDaemonChannelSource::webapiFormatChannelReport(SWGSDRangel::SWGChannelRe
response.getSdrDaemonChannelSourceReport()->setTvUSec(tv.tv_usec);
response.getSdrDaemonChannelSourceReport()->setQueueSize(m_dataReadQueue.size());
response.getSdrDaemonChannelSourceReport()->setQueueLength(m_dataReadQueue.length());
response.getSdrDaemonChannelSourceReport()->setSamplesCount(m_dataReadQueue.readSampleCount());
response.getSdrDaemonChannelSourceReport()->setSamplesCount(m_dataReadQueue.readSampleCount().value());
response.getSdrDaemonChannelSourceReport()->setCorrectableErrorsCount(m_nbCorrectableErrors);
response.getSdrDaemonChannelSourceReport()->setUncorrectableErrorsCount(m_nbUncorrectableErrors);
}

View File

@ -110,7 +110,6 @@ private:
bool m_running;
SDRDaemonChannelSourceSettings m_settings;
uint64_t m_samplesCount;
CM256::cm256_block m_cm256DescriptorBlocks[2*SDRDaemonNbOrginalBlocks]; //!< CM256 decoder descriptors (block addresses and block indexes)
SDRDaemonMetaDataFEC m_currentMeta;

View File

@ -30,7 +30,6 @@ SDRDaemonDataReadQueue::SDRDaemonDataReadQueue() :
m_maxSize(MinimumMaxSize),
m_blockIndex(1),
m_sampleIndex(0),
m_sampleCount(0),
m_full(false)
{}

View File

@ -25,6 +25,8 @@
#include <QQueue>
#include "util/limitedcounter.h"
class SDRDaemonDataBlock;
class Sample;
@ -40,7 +42,7 @@ public:
uint32_t length() const { return m_dataReadQueue.size(); } //!< Returns queue length
uint32_t size() const { return m_maxSize; } //!< Returns queue size (max length)
void setSize(uint32_t size); //!< Sets the queue size (max length)
uint32_t readSampleCount() const { return m_sampleCount; } //!< Returns the absolute number of samples read
LimitedCounter<uint32_t, 2000000000> readSampleCount() const { return m_sampleCount; } //!< Returns the absolute number of samples read
static const uint32_t MinimumMaxSize;
@ -50,7 +52,7 @@ private:
uint32_t m_maxSize;
uint32_t m_blockIndex;
uint32_t m_sampleIndex;
uint32_t m_sampleCount;
LimitedCounter<uint32_t, 2000000000> m_sampleCount; //!< use a counter capped below 2^31 as it is going to be converted to an int in the web interface
bool m_full; //!< full condition was hit
};