diff --git a/plugins/channelrx/demodm17/CMakeLists.txt b/plugins/channelrx/demodm17/CMakeLists.txt index 391206960..68d6d892c 100644 --- a/plugins/channelrx/demodm17/CMakeLists.txt +++ b/plugins/channelrx/demodm17/CMakeLists.txt @@ -44,7 +44,7 @@ if(NOT SERVER_MODE) m17statustextdialog.h ) set(TARGET_NAME demodm17) - set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB "Qt5::Widgets" Qt5::Charts) set(TARGET_LIB_GUI "sdrgui") set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) else() diff --git a/plugins/channelrx/demodm17/m17demod.h b/plugins/channelrx/demodm17/m17demod.h index 6d10c8277..e5177b2a9 100644 --- a/plugins/channelrx/demodm17/m17demod.h +++ b/plugins/channelrx/demodm17/m17demod.h @@ -242,6 +242,10 @@ public: ); } + void getBERT(uint32_t& bertErrors, uint32_t& bertBits) { + m_basebandSink->getBERT(bertErrors, bertBits); + } + uint32_t getLSFCount() const { return m_basebandSink->getLSFCount(); } const QString& getSrcCall() const { return m_basebandSink->getSrcCall(); } const QString& getDestcCall() const { return m_basebandSink->getDestcCall(); } diff --git a/plugins/channelrx/demodm17/m17demodbaseband.h b/plugins/channelrx/demodm17/m17demodbaseband.h index 446b61165..884438657 100644 --- a/plugins/channelrx/demodm17/m17demodbaseband.h +++ b/plugins/channelrx/demodm17/m17demodbaseband.h @@ -103,6 +103,10 @@ public: ); } + void getBERT(uint32_t& bertErrors, uint32_t& bertBits) { + m_sink.getBERT(bertErrors, bertBits); + } + uint32_t getLSFCount() const { return m_sink.getLSFCount(); } const QString& getSrcCall() const { return m_sink.getSrcCall(); } const QString& getDestcCall() const { return m_sink.getDestcCall(); } diff --git a/plugins/channelrx/demodm17/m17demodgui.cpp b/plugins/channelrx/demodm17/m17demodgui.cpp index 137ddd621..4ea256a6e 100644 --- a/plugins/channelrx/demodm17/m17demodgui.cpp +++ b/plugins/channelrx/demodm17/m17demodgui.cpp @@ -284,6 +284,22 @@ void M17DemodGUI::on_aprsClearTable_clicked() ui->aprsPackets->setRowCount(0); } +void M17DemodGUI::on_totButton_toggled(bool checked) +{ + m_showBERTotalOrCurrent = checked; + ui->curButton->blockSignals(true); + ui->curButton->setChecked(!checked); + ui->curButton->blockSignals(false); +} + +void M17DemodGUI::on_curButton_toggled(bool checked) +{ + m_showBERTotalOrCurrent = !checked; + ui->totButton->blockSignals(true); + ui->totButton->setChecked(!checked); + ui->totButton->blockSignals(false); +} + void M17DemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; @@ -360,7 +376,10 @@ M17DemodGUI::M17DemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_squelchOpen(false), m_audioSampleRate(-1), m_lsfCount(0), - m_tickCount(0) + m_tickCount(0), + m_lastBERErrors(0), + m_lastBERBits(0), + m_showBERTotalOrCurrent(true) { setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/channelrx/demodm17/readme.md"; @@ -427,6 +446,15 @@ M17DemodGUI::M17DemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban ui->dcdLabel->setPixmap(QIcon(":/carrier.png").pixmap(QSize(20, 20))); ui->lockLabel->setPixmap(QIcon(":/locked.png").pixmap(QSize(20, 20))); + m_berChart.setTheme(QChart::ChartThemeDark); + m_berChart.legend()->hide(); + ui->berChart->setChart(&m_berChart); + ui->berChart->setRenderHint(QPainter::Antialiasing); + m_berChart.addAxis(&m_berChartXAxis, Qt::AlignBottom); + m_berChart.addAxis(&m_berChartYAxis, Qt::AlignLeft); + m_berChart.layout()->setContentsMargins(0, 0, 0, 0); + m_berChart.setMargins(QMargins(1, 1, 1, 1)); + updateMyPosition(); displaySettings(); makeUIConnections(); @@ -503,6 +531,9 @@ void M17DemodGUI::displaySettings() ui->traceDecayText->setText(QString("%1").arg(m_settings.m_traceDecay)); m_scopeVisXY->setDecay(m_settings.m_traceDecay); + ui->totButton->setChecked(m_showBERTotalOrCurrent); + ui->curButton->setChecked(!m_showBERTotalOrCurrent); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); @@ -665,6 +696,73 @@ void M17DemodGUI::tick() m_lsfCount = m_m17Demod->getLSFCount(); } + if (((status == 5) || (status == 4)) && (sync_word_type == 3)) + { + uint32_t bertErrors, bertBits; + m_m17Demod->getBERT(bertErrors, bertBits); + uint32_t bertErrorsDelta = bertErrors - m_lastBERErrors; + uint32_t bertBitsDelta = bertBits - m_lastBERBits; + m_lastBERErrors = bertErrors; + m_lastBERBits = bertBits; + + m_berPoints.append(BERPoint{ + QDateTime::currentDateTime(), + bertErrors, + bertBits, + bertErrorsDelta, + bertBitsDelta + }); + m_currentErrors.append(bertErrorsDelta); + + uint32_t maxY; + QLineSeries *series; + + if (m_showBERTotalOrCurrent) + { + ui->berCounts->setText(tr("%1/%2").arg(bertErrors).arg(bertBits)); + series = addBERSeries(true, maxY); + + if (bertBits > 0) { + ui->berRatio->setText(tr("%1").arg((double) bertErrors / (double) bertBits, 0, 'e', 2)); + } + } + else + { + ui->berCounts->setText(tr("%1/%2").arg(bertErrorsDelta).arg(bertBitsDelta)); + series = addBERSeries(false, maxY); + + if (bertBitsDelta > 0) { + ui->berRatio->setText(tr("%1").arg((double) bertErrorsDelta / (double) bertBitsDelta, 0, 'e', 2)); + } + } + + if (series) + { + m_berChart.removeAllSeries(); + m_berChart.removeAxis(&m_berChartXAxis); + m_berChart.removeAxis(&m_berChartYAxis); + + m_berChart.addSeries(series); + + m_berChartXAxis.setRange(m_berPoints.front().m_dateTime, m_berPoints.back().m_dateTime); + m_berChartXAxis.setFormat("hh:mm:ss"); + m_berChart.addAxis(&m_berChartXAxis, Qt::AlignBottom); + series->attachAxis(&m_berChartXAxis); + + m_berChartYAxis.setRange(0, maxY == 0 ? 1 : maxY); + m_berChart.addAxis(&m_berChartYAxis, Qt::AlignLeft); + series->attachAxis(&m_berChartYAxis); + } + } + else + { + // qDebug("M17DemodGUI::tick: BER reset: status: %d sync_word_type: %d", status, sync_word_type); + m_lastBERErrors = 0; + m_lastBERBits = 0; + m_berPoints.clear(); + m_currentErrors.clear(); + } + } m_tickCount++; @@ -697,6 +795,37 @@ QString M17DemodGUI::getStatus(int status, int sync_word_type, bool streamElsePa } } +QLineSeries *M17DemodGUI::addBERSeries(bool total, uint32_t& maxVal) +{ + if (m_berPoints.size() < 2) { + return nullptr; + } + + QLineSeries *series = new QLineSeries(); + + if (!total) { + maxVal = *std::max_element(m_currentErrors.begin(), m_currentErrors.end()); + } else { + maxVal = m_berPoints.back().m_totalErrors; + } + + for (auto berPoint : m_berPoints) + { + double x = berPoint.m_dateTime.toMSecsSinceEpoch(); + double y; + + if (total) { + y = berPoint.m_totalErrors; + } else { + y = berPoint.m_currentErrors; + } + + series->append(x, y); + } + + return series; +} + void M17DemodGUI::makeUIConnections() { QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &M17DemodGUI::on_deltaFrequency_changed); @@ -714,6 +843,8 @@ void M17DemodGUI::makeUIConnections() QObject::connect(ui->audioMute, &QToolButton::toggled, this, &M17DemodGUI::on_audioMute_toggled); QObject::connect(ui->viewStatusLog, &QPushButton::clicked, this, &M17DemodGUI::on_viewStatusLog_clicked); QObject::connect(ui->aprsClearTable, &QPushButton::clicked, this, &M17DemodGUI::on_aprsClearTable_clicked); + QObject::connect(ui->totButton, &ButtonSwitch::toggled, this, &M17DemodGUI::on_totButton_toggled); + QObject::connect(ui->curButton, &ButtonSwitch::toggled, this, &M17DemodGUI::on_curButton_toggled); } void M17DemodGUI::updateAbsoluteCenterFrequency() diff --git a/plugins/channelrx/demodm17/m17demodgui.h b/plugins/channelrx/demodm17/m17demodgui.h index 9b0513441..042014d44 100644 --- a/plugins/channelrx/demodm17/m17demodgui.h +++ b/plugins/channelrx/demodm17/m17demodgui.h @@ -20,6 +20,8 @@ #define INCLUDE_M17DEMODGUI_H #include +#include +#include #include "channel/channelgui.h" #include "dsp/dsptypes.h" @@ -72,6 +74,15 @@ protected: void resizeEvent(QResizeEvent* size); private: + struct BERPoint + { + QDateTime m_dateTime; + uint32_t m_totalErrors; + uint32_t m_totalBits; + uint32_t m_currentErrors; + uint32_t m_currentBits; + }; + Ui::M17DemodGUI* ui; PluginAPI* m_pluginAPI; DeviceUISet* m_deviceUISet; @@ -92,6 +103,15 @@ private: int m_audioSampleRate; uint32_t m_lsfCount; uint32_t m_tickCount; + uint32_t m_lastBERErrors; + uint32_t m_lastBERBits; + bool m_showBERTotalOrCurrent; + + QChart m_berChart; + QDateTimeAxis m_berChartXAxis; + QValueAxis m_berChartYAxis; + QList m_berPoints; + QList m_currentErrors; float m_myLatitude; float m_myLongitude; @@ -112,6 +132,7 @@ private: void updateAbsoluteCenterFrequency(); QString getStatus(int status, int sync_word_type, bool streamElsePacket, int packetProtocol); void packetReceived(QByteArray packet); + QLineSeries *addBERSeries(bool total, uint32_t& maxVal); void leaveEvent(QEvent*); void enterEvent(QEvent*); @@ -131,6 +152,8 @@ private slots: void on_highPassFilter_toggled(bool checked); void on_audioMute_toggled(bool checked); void on_aprsClearTable_clicked(); + void on_totButton_toggled(bool checked); + void on_curButton_toggled(bool checked); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); void on_viewStatusLog_clicked(); diff --git a/plugins/channelrx/demodm17/m17demodgui.ui b/plugins/channelrx/demodm17/m17demodgui.ui index 2b1248a0f..d3dd7375a 100644 --- a/plugins/channelrx/demodm17/m17demodgui.ui +++ b/plugins/channelrx/demodm17/m17demodgui.ui @@ -1634,6 +1634,150 @@ + + + BER test data + + + + :/ruler.png:/ruler.png + + + + + + BER test + + + + + 80 + 10 + 140 + 28 + + + + + 100 + 0 + + + + Errors/Bits counts + + + QFrame::Box + + + QFrame::Sunken + + + 0/1000000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 230 + 10 + 30 + 28 + + + + + 30 + 0 + + + + Total BER + + + BER + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 265 + 10 + 85 + 28 + + + + + 30 + 0 + + + + Bit Error Rate + + + QFrame::Box + + + QFrame::Sunken + + + 0.00e+00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 0 + 5 + 36 + 35 + + + + Select total BER counts + + + TOT + + + + + + 40 + 5 + 36 + 35 + + + + Select current BER counts + + + CUR + + + + + + 0 + 43 + 480 + 165 + + + + @@ -1669,6 +1813,11 @@
gui/tvscreen.h
1 + + QChartView + QGraphicsView +
QtCharts
+
diff --git a/plugins/channelrx/demodm17/m17demodprocessor.h b/plugins/channelrx/demodm17/m17demodprocessor.h index f9f5d8428..867d86027 100644 --- a/plugins/channelrx/demodm17/m17demodprocessor.h +++ b/plugins/channelrx/demodm17/m17demodprocessor.h @@ -92,6 +92,12 @@ public: viterbiCost = m_viterbiCost; } + void getBERT(uint32_t& bertErrors, uint32_t& bertBits) + { + bertErrors = m_prbs.errors(); + bertBits = m_prbs.bits(); + } + private: std::vector m_currentPacket; size_t m_packetFrameCounter; diff --git a/plugins/channelrx/demodm17/m17demodsink.h b/plugins/channelrx/demodm17/m17demodsink.h index aae877525..aab2fefbb 100644 --- a/plugins/channelrx/demodm17/m17demodsink.h +++ b/plugins/channelrx/demodm17/m17demodsink.h @@ -105,6 +105,10 @@ public: ); } + void getBERT(uint32_t& bertErrors, uint32_t& bertBits) { + m_m17DemodProcessor.getBERT(bertErrors, bertBits); + } + uint32_t getLSFCount() const { return m_m17DemodProcessor.getLSFCount(); } const QString& getSrcCall() const { return m_m17DemodProcessor.getSrcCall(); } const QString& getDestcCall() const { return m_m17DemodProcessor.getDestcCall(); }