diff --git a/plugins/channelrx/demoddab/dabdemod.cpp b/plugins/channelrx/demoddab/dabdemod.cpp index 035eb3f35..ad0d16900 100644 --- a/plugins/channelrx/demoddab/dabdemod.cpp +++ b/plugins/channelrx/demoddab/dabdemod.cpp @@ -51,6 +51,7 @@ MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABFIBQuality, Message) MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABSampleRate, Message) MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABData, Message) MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABMOTData, Message) +MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABTII, Message) MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABReset, Message) MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABResetService, Message) @@ -238,6 +239,7 @@ bool DABDemod::handleMessage(const Message& cmd) { getMessageQueueToGUI()->push(new MsgDABProgramName(report)); } + m_basebandSink->getInputMessageQueue()->push(new MsgDABProgramName(report)); return true; } @@ -271,6 +273,16 @@ bool DABDemod::handleMessage(const Message& cmd) return true; } + else if (MsgDABTII::match(cmd)) + { + MsgDABTII& report = (MsgDABTII&)cmd; + if (getMessageQueueToGUI()) + { + getMessageQueueToGUI()->push(new MsgDABTII(report)); + } + + return true; + } else if (MsgDABReset::match(cmd)) { MsgDABReset& report = (MsgDABReset&)cmd; diff --git a/plugins/channelrx/demoddab/dabdemod.h b/plugins/channelrx/demoddab/dabdemod.h index bae6c38ee..a3c28180d 100644 --- a/plugins/channelrx/demoddab/dabdemod.h +++ b/plugins/channelrx/demoddab/dabdemod.h @@ -275,6 +275,26 @@ public: { } }; + class MsgDABTII : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getTII() const { return m_tii; } + + static MsgDABTII* create(int tii) + { + return new MsgDABTII(tii); + } + + private: + int m_tii; + + MsgDABTII(int tii) : + Message(), + m_tii(tii) + { } + }; + class MsgDABReset : public Message { MESSAGE_CLASS_DECLARATION diff --git a/plugins/channelrx/demoddab/dabdemodbaseband.cpp b/plugins/channelrx/demoddab/dabdemodbaseband.cpp index 7d408f476..4bcde8fbd 100644 --- a/plugins/channelrx/demoddab/dabdemodbaseband.cpp +++ b/plugins/channelrx/demoddab/dabdemodbaseband.cpp @@ -162,6 +162,12 @@ bool DABDemodBaseband::handleMessage(const Message& cmd) m_sink.resetService(); return true; } + else if (DABDemod::MsgDABProgramName::match(cmd)) + { + DABDemod::MsgDABProgramName& report = (DABDemod::MsgDABProgramName&) cmd; + m_sink.programAvailable(report.getName()); + return true; + } else { return false; diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp index f7e9bbbc4..efdc47ea4 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.cpp +++ b/plugins/channelrx/demoddab/dabdemodgui.cpp @@ -38,6 +38,7 @@ #include "gui/crightclickenabler.h" #include "gui/dialogpositioner.h" #include "channel/channelwebapiutils.h" +#include "feature/featurewebapiutils.h" #include "maincore.h" #include "dabdemod.h" @@ -47,6 +48,7 @@ #define PROGRAMS_COL_NAME 0 #define PROGRAMS_COL_ID 1 #define PROGRAMS_COL_FREQUENCY 2 +#define PROGRAMS_COL_ENSEMBLE 3 void DABDemodGUI::resizeTable() { @@ -57,6 +59,7 @@ void DABDemodGUI::resizeTable() ui->programs->setItem(row, PROGRAMS_COL_NAME, new QTableWidgetItem("Some Random Radio Station")); ui->programs->setItem(row, PROGRAMS_COL_ID, new QTableWidgetItem("123456")); ui->programs->setItem(row, PROGRAMS_COL_FREQUENCY, new QTableWidgetItem("200.000")); + ui->programs->setItem(row, PROGRAMS_COL_ENSEMBLE, new QTableWidgetItem("Some random ensemble")); ui->programs->resizeColumnsToContents(); ui->programs->removeRow(row); } @@ -142,9 +145,26 @@ bool DABDemodGUI::deserialize(const QByteArray& data) } } +int DABDemodGUI::findProgramRowById(int id) +{ + QString idText = QString::number(id); + for (int i = 0; i < ui->programs->rowCount(); i++) + { + if (ui->programs->item(i, PROGRAMS_COL_ID)->text() == idText) { + return i; + } + } + return -1; +} + // Add row to table void DABDemodGUI::addProgramName(const DABDemod::MsgDABProgramName& program) { + // Don't add duplicate + if (findProgramRowById(program.getId()) != -1) { + return; + } + ui->programs->setSortingEnabled(false); int row = ui->programs->rowCount(); ui->programs->setRowCount(row + 1); @@ -152,9 +172,11 @@ void DABDemodGUI::addProgramName(const DABDemod::MsgDABProgramName& program) QTableWidgetItem *nameItem = new QTableWidgetItem(); QTableWidgetItem *idItem = new QTableWidgetItem(); QTableWidgetItem *frequencyItem = new QTableWidgetItem(); + QTableWidgetItem *ensembleItem = new QTableWidgetItem(); ui->programs->setItem(row, PROGRAMS_COL_NAME, nameItem); ui->programs->setItem(row, PROGRAMS_COL_ID, idItem); ui->programs->setItem(row, PROGRAMS_COL_FREQUENCY, frequencyItem); + ui->programs->setItem(row, PROGRAMS_COL_ENSEMBLE, ensembleItem); nameItem->setText(program.getName()); idItem->setText(QString::number(program.getId())); double frequencyInHz; @@ -165,7 +187,10 @@ void DABDemodGUI::addProgramName(const DABDemod::MsgDABProgramName& program) frequencyItem->setData(Qt::UserRole, frequencyInHz+m_settings.m_inputFrequencyOffset); } else + { frequencyItem->setData(Qt::UserRole, 0.0); + } + ensembleItem->setText(ui->ensemble->text()); ui->programs->setSortingEnabled(true); filterRow(row); } @@ -178,13 +203,29 @@ void DABDemodGUI::on_programs_cellDoubleClicked(int row, int column) m_settings.m_program = ui->programs->item(row, PROGRAMS_COL_NAME)->text(); double frequencyInHz = ui->programs->item(row, PROGRAMS_COL_FREQUENCY)->data(Qt::UserRole).toDouble(); - ChannelWebAPIUtils::setCenterFrequency(m_dabDemod->getDeviceSetIndex(), frequencyInHz-m_settings.m_inputFrequencyOffset); - + double centreFreq = frequencyInHz-m_settings.m_inputFrequencyOffset; + ChannelWebAPIUtils::setCenterFrequency(m_dabDemod->getDeviceSetIndex(), centreFreq); clearProgram(); - applySettings(); } +// Ensemble name can sometimes be decoded after program name, so we +// need to update entries in the table where ensemble name is "-" +void DABDemodGUI::updateEnsembleName(const QString& ensemble) +{ + double frequencyInHz = m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset; + for (int i = 0; i < ui->programs->rowCount(); i++) + { + if (ui->programs->item(i, PROGRAMS_COL_ENSEMBLE)->text() == "-") + { + if (ui->programs->item(i, PROGRAMS_COL_FREQUENCY)->data(Qt::UserRole).toDouble() == frequencyInHz) + { + ui->programs->item(i, PROGRAMS_COL_ENSEMBLE)->setText(ensemble); + } + } + } +} + bool DABDemodGUI::handleMessage(const Message& message) { if (DABDemod::MsgConfigureDABDemod::match(message)) @@ -201,6 +242,11 @@ bool DABDemodGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + if (m_deviceCenterFrequency != notif.getCenterFrequency()) + { + // Reset on frequency change, to get new ensemble name + resetService(); + } m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); @@ -223,6 +269,7 @@ bool DABDemodGUI::handleMessage(const Message& message) { DABDemod::MsgDABEnsembleName& report = (DABDemod::MsgDABEnsembleName&) message; ui->ensemble->setText(report.getName()); + updateEnsembleName(report.getName()); return true; } else if (DABDemod::MsgDABProgramName::match(message)) @@ -293,7 +340,14 @@ bool DABDemodGUI::handleMessage(const Message& message) } return true; } - + else if (DABDemod::MsgDABTII::match(message)) + { + DABDemod::MsgDABTII& report = (DABDemod::MsgDABTII&) message; + int tii = report.getTII(); + ui->tiiMainId->setText(QStringLiteral("%1").arg((tii >> 8) & 0xff, 2, 16, QLatin1Char('0')).toUpper()); + ui->tiiSubId->setText(QStringLiteral("%1").arg(tii & 0xff, 2, 16, QLatin1Char('0')).toUpper()); + return true; + } return false; } @@ -607,7 +661,6 @@ void DABDemodGUI::clearProgram() { // Clear current program ui->program->setText("-"); - ui->ensemble->setText("-"); ui->programType->setText("-"); ui->language->setText("-"); ui->audio->setText("-"); @@ -621,10 +674,13 @@ void DABDemodGUI::clearProgram() void DABDemodGUI::resetService() { - // Reset DAB audio service, to avoid unpleasent noise when changing frequency - DABDemod::MsgDABResetService* message = DABDemod::MsgDABResetService::create(); - m_dabDemod->getInputMessageQueue()->push(message); + ui->ensemble->setText("-"); + ui->tiiMainId->setText("-"); + ui->tiiSubId->setText("-"); clearProgram(); + // Reset DAB audio service, so we get new ensemble name + DABDemod::MsgDABReset* message = DABDemod::MsgDABReset::create(); + m_dabDemod->getInputMessageQueue()->push(message); } void DABDemodGUI::on_channel_currentIndexChanged(int index) @@ -634,7 +690,6 @@ void DABDemodGUI::on_channel_currentIndexChanged(int index) QString text = ui->channel->currentText(); if (!text.isEmpty()) { - resetService(); // Tune to requested channel QString freq = text.split(" ")[2]; m_channelFreq = freq.toDouble() * 1e6; @@ -715,9 +770,30 @@ void DABDemodGUI::makeUIConnections() QObject::connect(ui->clearTable, &QPushButton::clicked, this, &DABDemodGUI::on_clearTable_clicked); QObject::connect(ui->programs, &QTableWidget::cellDoubleClicked, this, &DABDemodGUI::on_programs_cellDoubleClicked); QObject::connect(ui->channel, QOverload::of(&QComboBox::currentIndexChanged), this, &DABDemodGUI::on_channel_currentIndexChanged); + QObject::connect(ui->findOnMap, &QToolButton::clicked, this, &DABDemodGUI::on_findOnMap_clicked); } void DABDemodGUI::updateAbsoluteCenterFrequency() { setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); } + +void DABDemodGUI::on_findOnMap_clicked() +{ + QString mainID = ui->tiiMainId->text(); + if (mainID.isEmpty() || (mainID == "-")) { + return; + } + QString subID = ui->tiiSubId->text(); + if (subID.isEmpty() || (subID == "-")) { + return; + } + QString ensemble = ui->ensemble->text().trimmed(); + if (ensemble.isEmpty() || (ensemble == "-")) { + return; + } + QString id = ensemble + " " + mainID + subID; + qDebug() << "Finding " << id; + FeatureWebAPIUtils::mapFind(id); +} + diff --git a/plugins/channelrx/demoddab/dabdemodgui.h b/plugins/channelrx/demoddab/dabdemodgui.h index cad4f4be3..1c63f6885 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.h +++ b/plugins/channelrx/demoddab/dabdemodgui.h @@ -93,6 +93,8 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); + void updateEnsembleName(const QString& ensemble); + int findProgramRowById(int id); void addProgramName(const DABDemod::MsgDABProgramName& program); bool handleMessage(const Message& message); void makeUIConnections(); @@ -115,6 +117,7 @@ private slots: void on_clearTable_clicked(); void on_programs_cellDoubleClicked(int row, int column); void on_channel_currentIndexChanged(int index); + void on_findOnMap_clicked(); void filterRow(int row); void filter(); void programs_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); diff --git a/plugins/channelrx/demoddab/dabdemodgui.ui b/plugins/channelrx/demoddab/dabdemodgui.ui index 5367f6909..90d5ae781 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.ui +++ b/plugins/channelrx/demoddab/dabdemodgui.ui @@ -7,7 +7,7 @@ 0 0 398 - 612 + 657 @@ -673,6 +673,11 @@ Frequency + + + Ensemble + + @@ -1006,6 +1011,106 @@ + + + + 10 + 610 + 361 + 40 + + + + Transmitter + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + + TII Sub ID: + + + + + + + Transmitter Identifier Information + + + - + + + + + + + TII Main ID: + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Transmitter Identification Information + + + - + + + + + + + Find transmitter on map + + + + + + + :/gridpolar.png:/gridpolar.png + + + + + + + + + + diff --git a/plugins/channelrx/demoddab/dabdemodsink.cpp b/plugins/channelrx/demoddab/dabdemodsink.cpp index 5709ed5e4..c5cb95e0c 100644 --- a/plugins/channelrx/demoddab/dabdemodsink.cpp +++ b/plugins/channelrx/demoddab/dabdemodsink.cpp @@ -279,6 +279,14 @@ void motDataHandler(uint8_t *data, int len, const char *filename, int contentsub sink->motData(data, len, QString::fromUtf8(filename), contentsubType); } +// Missing ctx for tiiDataHandler - https://github.com/JvanKatwijk/dab-cmdline/issues/89 +DABDemodSink *tiiSink; + +void tiiDataHandler(int tii) +{ + tiiSink->tii(tii); +} + void DABDemodSink::systemData(bool sync, int16_t snr, int32_t freqOffset) { if (getMessageQueueToChannel()) @@ -287,6 +295,7 @@ void DABDemodSink::systemData(bool sync, int16_t snr, int32_t freqOffset) getMessageQueueToChannel()->push(msg); } } + void DABDemodSink::ensembleName(const QString& name, int id) { if (getMessageQueueToChannel()) @@ -351,6 +360,15 @@ void DABDemodSink::motData(const uint8_t *data, int len, const QString& filename } } +void DABDemodSink::tii(int tii) +{ + if (getMessageQueueToChannel()) + { + DABDemod::MsgDABTII *msg = DABDemod::MsgDABTII::create(tii); + getMessageQueueToChannel()->push(msg); + } +} + static int16_t scale(int16_t sample, float factor) { int32_t prod = (int32_t)(((int32_t)sample) * factor); @@ -473,6 +491,7 @@ DABDemodSink::DABDemodSink(DABDemod *packetDemod) : m_dabAudioSampleRate(10000), // Unused value to begin with m_channelSampleRate(DABDEMOD_CHANNEL_SAMPLE_RATE), m_channelFrequencyOffset(0), + m_programSet(false), m_magsqSum(0.0f), m_magsqPeak(0.0f), m_magsqCount(0), @@ -502,8 +521,9 @@ DABDemodSink::DABDemodSink(DABDemod *packetDemod) : m_api.programdata_Handler = programDataHandler; m_api.program_quality_Handler = programQualityHandler; m_api.motdata_Handler = motDataHandler; - m_api.tii_data_Handler = nullptr; + m_api.tii_data_Handler = tiiDataHandler; m_api.timeHandler = nullptr; + tiiSink = this; m_dab = dabInit(&m_device, &m_api, nullptr, @@ -607,29 +627,49 @@ void DABDemodSink::applySettings(const DABDemodSettings& settings, bool force) if ((settings.m_program != m_settings.m_program) || force) { - if (!settings.m_program.isEmpty()) - { - QByteArray ba = settings.m_program.toUtf8(); - const char *program = ba.data(); - if (!is_audioService (m_dab, program)) - qWarning() << settings.m_program << " is not an audio service"; - else - { - dataforAudioService(m_dab, program, &m_ad, 0); - if (!m_ad.defined) - qWarning() << settings.m_program << " audio data is not defined"; - else - { - dabReset_msc(m_dab); - set_audioChannel(m_dab, &m_ad); - } - } + if (!settings.m_program.isEmpty()) { + setProgram(settings.m_program); + } else { + m_programSet = true; } } m_settings = settings; } +// Can't call setProgram directly from callback, so we get here via a message +void DABDemodSink::programAvailable(const QString& programName) +{ + if (!m_programSet && (programName == m_settings.m_program)) { + setProgram(m_settings.m_program); + } +} + +void DABDemodSink::setProgram(const QString& name) +{ + m_programSet = false; + QByteArray ba = name.toUtf8(); + const char *program = ba.data(); + if (!is_audioService (m_dab, program)) + { + qWarning() << name << " is not an audio service"; + } + else + { + dataforAudioService(m_dab, program, &m_ad, 0); + if (!m_ad.defined) + { + qWarning() << name << " audio data is not defined"; + } + else + { + dabReset_msc(m_dab); + set_audioChannel(m_dab, &m_ad); + m_programSet = true; + } + } +} + // Called when audio device sample rate changes void DABDemodSink::applyAudioSampleRate(int sampleRate) { diff --git a/plugins/channelrx/demoddab/dabdemodsink.h b/plugins/channelrx/demoddab/dabdemodsink.h index 3021875c9..153f96969 100644 --- a/plugins/channelrx/demoddab/dabdemodsink.h +++ b/plugins/channelrx/demoddab/dabdemodsink.h @@ -80,6 +80,7 @@ public: void reset(); void resetService(); + void programAvailable(const QString& programName); // Callbacks void systemData(bool sync, int16_t snr, int32_t freqOffset); @@ -91,6 +92,7 @@ public: void fibQuality(int16_t percent); void data(const QString& data); void motData(const uint8_t *data, int len, const QString& filename, int contentSubType); + void tii(int tii); private: struct MagSqLevelsStore @@ -116,6 +118,7 @@ private: DABDemodDevice m_device; audiodata m_ad; API_struct m_api; + bool m_programSet; NCO m_nco; Interpolator m_interpolator; @@ -145,6 +148,7 @@ private: void processOneSample(Complex &ci); void processOneAudioSample(Complex &ci); MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; } + void setProgram(const QString& name); }; #endif // INCLUDE_DABDEMODSINK_H