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:
parent
90fb223b01
commit
f8383f8cff
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
84
sdrbase/util/limitedcounter.h
Normal file
84
sdrbase/util/limitedcounter.h
Normal 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_ */
|
@ -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];
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -30,7 +30,6 @@ SDRDaemonDataReadQueue::SDRDaemonDataReadQueue() :
|
||||
m_maxSize(MinimumMaxSize),
|
||||
m_blockIndex(1),
|
||||
m_sampleIndex(0),
|
||||
m_sampleCount(0),
|
||||
m_full(false)
|
||||
{}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user