From 4d156044e45fd66f369f9301b713041a2f177d70 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 14 Dec 2015 01:36:13 +0100 Subject: [PATCH] BFM demod: RDS GUI part #1: counters, PI and Group 0 --- plugins/channel/bfm/bfmdemodgui.cpp | 87 ++++++- plugins/channel/bfm/bfmdemodgui.h | 2 + plugins/channel/bfm/bfmdemodgui.ui | 364 +++++++++++++++++++++++++++- plugins/channel/bfm/rdsparser.cpp | 156 +++++++----- plugins/channel/bfm/rdsparser.h | 68 ++++-- 5 files changed, 595 insertions(+), 82 deletions(-) diff --git a/plugins/channel/bfm/bfmdemodgui.cpp b/plugins/channel/bfm/bfmdemodgui.cpp index f7c424796..5fe4d5dd8 100644 --- a/plugins/channel/bfm/bfmdemodgui.cpp +++ b/plugins/channel/bfm/bfmdemodgui.cpp @@ -18,6 +18,11 @@ #include #include #include +#include "boost/format.hpp" +#include +#include +#include + #include "dsp/threadedsamplesink.h" #include "dsp/channelizer.h" #include "dsp/dspengine.h" @@ -30,7 +35,6 @@ #include "mainwindow.h" #include "bfmdemodgui.h" - #include "ui_bfmdemodgui.h" #include "bfmdemod.h" @@ -245,6 +249,7 @@ void BFMDemodGUI::on_showPilot_clicked() void BFMDemodGUI::on_rds_clicked() { + m_rdsParser.clearAllFields(); applySettings(); } @@ -268,6 +273,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent) : m_pluginAPI(pluginAPI), m_channelMarker(this), m_basicSettingsShown(false), + m_rdsTimerCount(0), m_channelPowerDbAvg(20,0), m_rate(625000) { @@ -388,6 +394,13 @@ void BFMDemodGUI::tick() } } + if (ui->rds->isChecked() && (m_rdsTimerCount == 0)) + { + rdsUpdate(); + } + + m_rdsTimerCount = (m_rdsTimerCount + 1) % 25; + //qDebug() << "Pilot lock: " << m_bfmDemod->getPilotLock() << ":" << m_bfmDemod->getPilotLevel(); TODO: update a GUI item with status } @@ -398,3 +411,75 @@ void BFMDemodGUI::channelSampleRateChanged() ui->glSpectrum->setSampleRate(m_rate / 2); } +void BFMDemodGUI::rdsUpdate() +{ + // PI group + if (m_rdsParser.m_pi_updated) + { + ui->piLabel->setStyleSheet("QLabel { background-color : green; }"); + ui->piCountText->setNum((int) m_rdsParser.m_pi_count); + QString pistring(str(boost::format("%04X") % m_rdsParser.m_pi_program_identification).c_str()); + ui->piText->setText(pistring); + + if (m_rdsParser.m_pi_traffic_program) { + ui->piTPIndicator->setStyleSheet("QLabel { background-color : green; }"); + } else { + ui->piTPIndicator->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + ui->piType->setText(QString(m_rdsParser.pty_table[m_rdsParser.m_pi_program_type].c_str())); + ui->piCoverage->setText(QString(m_rdsParser.coverage_area_codes[m_rdsParser.m_pi_area_coverage_index].c_str())); + } + else + { + ui->piLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + // G0 group + if (m_rdsParser.m_g0_updated) + { + ui->g00Label->setStyleSheet("QLabel { background-color : green; }"); + ui->g00CountText->setNum((int) m_rdsParser.m_g0_count); + ui->g00ProgServiceName->setText(QString(m_rdsParser.m_g0_program_service_name)); + + if (m_rdsParser.m_g0_traffic_announcement) { + ui->g00TrafficAnnouncement->setStyleSheet("QLabel { background-color : green; }"); + } else { + ui->g00TrafficAnnouncement->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + ui->g00MusicSpeech->setText(QString((m_rdsParser.m_g0_music_speech ? "Music" : "Speech"))); + ui->g00MonoStereo->setText(QString((m_rdsParser.m_g0_mono_stereo ? "Mono" : "Stereo"))); + + if (m_rdsParser.m_g0_af_updated) + { + bool isFirst = true; + std::ostringstream os; + os << std::fixed << std::showpoint << std::setprecision(2); + + for (std::set::iterator it = m_rdsParser.m_g0_alt_freq.begin(); it != m_rdsParser.m_g0_alt_freq.end(); ++it) + { + if (*it > 76.0) + { + if (!isFirst) + { + os << ", "; + } + + os << *it; + isFirst = false; + } + } + + ui->g00AltFrequencies->setText(QString(os.str().c_str())); + } + } + else + { + ui->g00Label->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + // G1 group + + m_rdsParser.clearUpdateFlags(); +} diff --git a/plugins/channel/bfm/bfmdemodgui.h b/plugins/channel/bfm/bfmdemodgui.h index 64b8be417..4bdaa47e6 100644 --- a/plugins/channel/bfm/bfmdemodgui.h +++ b/plugins/channel/bfm/bfmdemodgui.h @@ -75,6 +75,7 @@ private: ChannelMarker m_channelMarker; bool m_basicSettingsShown; bool m_doApplySettings; + int m_rdsTimerCount; ThreadedSampleSink* m_threadedChannelizer; Channelizer* m_channelizer; @@ -92,6 +93,7 @@ private: void blockApplySettings(bool block); void applySettings(); + void rdsUpdate(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channel/bfm/bfmdemodgui.ui b/plugins/channel/bfm/bfmdemodgui.ui index 0c1e2c0ad..514d76153 100644 --- a/plugins/channel/bfm/bfmdemodgui.ui +++ b/plugins/channel/bfm/bfmdemodgui.ui @@ -6,8 +6,8 @@ 0 0 - 335 - 333 + 446 + 729 @@ -399,7 +399,7 @@ 10 - 160 + 560 311 151 @@ -431,6 +431,364 @@ + + + + 10 + 150 + 421 + 91 + + + + RDS data + + + + 3 + + + + + + + PI + + + + + + + 0 + + + + + + + G00 + + + + + + + 0 + + + + + + + G03 + + + + + + + G01 + + + + + + + 0 + + + + + + + G02 + + + + + + + 0 + + + + + + + G08 + + + + + + + G07 + + + + + + + 0 + + + + + + + G04 + + + + + + + 0 + + + + + + + G05 + + + + + + + 0 + + + + + + + G06 + + + + + + + 0 + + + + + + + 0 + + + + + + + 0 + + + + + + + G09 + + + + + + + 0 + + + + + + + G14 + + + + + + + 0 + + + + + + + + + Qt::Horizontal + + + + + + + + + + 30 + 16777215 + + + + PI + + + + + + + Qt::Vertical + + + + + + + + 80 + 16777215 + + + + 0000 + + + + + + + Qt::Vertical + + + + + + + + 20 + 16777215 + + + + TP + + + + + + + None + + + + + + + Local + + + + + + + + + + + + 30 + 16777215 + + + + G00 + + + + + + + Qt::Vertical + + + + + + + + 80 + 16777215 + + + + ........ + + + + + + + Qt::Vertical + + + + + + + + 20 + 16777215 + + + + TA + + + + + + + + 50 + 16777215 + + + + Music + + + + + + + + 50 + 16777215 + + + + Mono + + + + + + + + + + + + + + diff --git a/plugins/channel/bfm/rdsparser.cpp b/plugins/channel/bfm/rdsparser.cpp index 38392460f..c49741695 100644 --- a/plugins/channel/bfm/rdsparser.cpp +++ b/plugins/channel/bfm/rdsparser.cpp @@ -212,14 +212,47 @@ const std::string RDSParser::label_descriptions[16] = { RDSParser::RDSParser() { - std::memset(radiotext, ' ', sizeof(radiotext)); - radiotext[sizeof(radiotext) - 1] = '\0'; + clearAllFields(); } RDSParser::~RDSParser() { } +void RDSParser::clearUpdateFlags() +{ + m_pi_updated = false; + m_g0_updated = false; + m_g0_af_updated = false; +} + +void RDSParser::clearAllFields() +{ + // PI data + m_pi_count = 0; + m_pi_program_identification = 0; + m_pi_program_type = 0; + m_pi_traffic_program = false; + + // Group 00 data + m_g0_count = 0; + std::memset(m_g0_program_service_name, ' ', sizeof(m_g0_program_service_name)); + radiotext[sizeof(m_g0_program_service_name) - 1] = '\0'; + m_g0_traffic_announcement = false; + m_g0_music_speech = false; + m_g0_mono_stereo = false; + m_g0_artificial_head = false; + m_g0_compressed = false; + m_g0_static_pty = false; + m_g0_alt_freq.clear(); + + // Group 02 data + std::memset(radiotext, ' ', sizeof(radiotext)); + radiotext[sizeof(radiotext) - 1] = '\0'; + + clearUpdateFlags(); +} + void RDSParser::parseGroup(unsigned int *group) { unsigned int group_type = (unsigned int)((group[1] >> 12) & 0xf); @@ -229,26 +262,29 @@ void RDSParser::parseGroup(unsigned int *group) << " type: " << group_type << (ab ? 'B' :'A') << " (" << rds_group_acronyms[group_type].c_str() << ")"; - program_identification = group[0]; // "PI" - program_type = (group[1] >> 5) & 0x1f; // "PTY" - int pi_country_identification = (program_identification >> 12) & 0xf; - int pi_area_coverage = (program_identification >> 8) & 0xf; - unsigned char pi_program_reference_number = program_identification & 0xff; + m_pi_count++; + m_pi_updated = true; - std::string pistring = str(boost::format("%04X") % program_identification); - //send_message(0, pistring); - //send_message(2, pty_table[program_type]); + m_pi_program_identification = group[0]; // "PI" + m_pi_traffic_program = (group[1] >> 10) & 0x01; // "TP" + m_pi_program_type = (group[1] >> 5) & 0x1f; // "PTY" + m_pi_country_identification = (m_pi_program_identification >> 12) & 0xf; + m_pi_area_coverage_index = (m_pi_program_identification >> 8) & 0xf; + unsigned char pi_program_reference_number = m_pi_program_identification & 0xff; + + /* + std::string pistring = str(boost::format("%04X") % m_pi_program_identification); qDebug() << "RDSParser::parseGroup:" << " PI:" << pistring.c_str() - << " - " << "PTY:" << pty_table[program_type].c_str() - << " (country:" << (pi_country_codes[pi_country_identification - 1][0]).c_str() - << "/" << (pi_country_codes[pi_country_identification - 1][1]).c_str() - << "/" << (pi_country_codes[pi_country_identification - 1][2]).c_str() - << "/" << (pi_country_codes[pi_country_identification - 1][3]).c_str() - << "/" << (pi_country_codes[pi_country_identification - 1][4]).c_str() - << ", area:" << coverage_area_codes[pi_area_coverage].c_str() - << ", program:" << int(pi_program_reference_number) << ")"; + << " - " << "PTY:" << pty_table[m_pi_program_type].c_str() + << " (country:" << (pi_country_codes[m_pi_country_identification - 1][0]).c_str() + << "/" << (pi_country_codes[m_pi_country_identification - 1][1]).c_str() + << "/" << (pi_country_codes[m_pi_country_identification - 1][2]).c_str() + << "/" << (pi_country_codes[m_pi_country_identification - 1][3]).c_str() + << "/" << (pi_country_codes[m_pi_country_identification - 1][4]).c_str() + << ", area:" << coverage_area_codes[m_pi_area_coverage_index].c_str() + << ", program:" << int(pi_program_reference_number) << ")";*/ switch (group_type) { case 0: @@ -319,40 +355,47 @@ void RDSParser::decode_type0(unsigned int *group, bool B) double af_2 = 0; char flagstring[8] = "0000000"; - traffic_program = (group[1] >> 10) & 0x01; // "TP" - traffic_announcement = (group[1] >> 4) & 0x01; // "TA" - music_speech = (group[1] >> 3) & 0x01; // "MuSp" + m_g0_count++; + m_g0_updated = true; + + m_pi_traffic_program = (group[1] >> 10) & 0x01; // "TP" + m_g0_traffic_announcement = (group[1] >> 4) & 0x01; // "TA" + m_g0_music_speech = (group[1] >> 3) & 0x01; // "MuSp" bool decoder_control_bit = (group[1] >> 2) & 0x01; // "DI" unsigned char segment_address = group[1] & 0x03; // "DI segment" - program_service_name[segment_address * 2] = (group[3] >> 8) & 0xff; - program_service_name[segment_address * 2 + 1] = group[3] & 0xff; + m_g0_program_service_name[segment_address * 2] = (group[3] >> 8) & 0xff; + m_g0_program_service_name[segment_address * 2 + 1] = group[3] & 0xff; /* see page 41, table 9 of the standard */ - switch (segment_address) { + switch (segment_address) + { case 0: - mono_stereo=decoder_control_bit; - break; + m_g0_mono_stereo = decoder_control_bit; + break; case 1: - artificial_head=decoder_control_bit; - break; + m_g0_artificial_head = decoder_control_bit; + break; case 2: - compressed=decoder_control_bit; - break; + m_g0_compressed = decoder_control_bit; + break; case 3: - static_pty=decoder_control_bit; - break; + m_g0_static_pty = decoder_control_bit; + break; default: - break; + break; } - flagstring[0] = traffic_program ? '1' : '0'; - flagstring[1] = traffic_announcement ? '1' : '0'; - flagstring[2] = music_speech ? '1' : '0'; - flagstring[3] = mono_stereo ? '1' : '0'; - flagstring[4] = artificial_head ? '1' : '0'; - flagstring[5] = compressed ? '1' : '0'; - flagstring[6] = static_pty ? '1' : '0'; + + /* unused + flagstring[0] = m_pi_traffic_program ? '1' : '0'; + flagstring[1] = m_g0_traffic_announcement ? '1' : '0'; + flagstring[2] = m_g0_music_speech ? '1' : '0'; + flagstring[3] = m_g0_mono_stereo ? '1' : '0'; + flagstring[4] = m_g0_artificial_head ? '1' : '0'; + flagstring[5] = m_g0_compressed ? '1' : '0'; + flagstring[6] = m_g0_static_pty ? '1' : '0';*/ + static std::string af_string; if (!B) @@ -362,17 +405,25 @@ void RDSParser::decode_type0(unsigned int *group, bool B) af_1 = decode_af(af_code_1); af_2 = decode_af(af_code_2); - if(af_1) { + if (af_1) + { + std::pair, bool> res = m_g0_alt_freq.insert(af_1/1e3); + m_g0_af_updated = m_g0_af_updated || res.second; no_af += 1; } - if(af_2) { + + if (af_2) + { + std::pair, bool> res = m_g0_alt_freq.insert(af_2/1e3); + m_g0_af_updated = m_g0_af_updated || res.second; no_af += 2; } + /* std::string af1_string; std::string af2_string; - /* only AF1 => no_af==1, only AF2 => no_af==2, both AF1 and AF2 => no_af==3 */ + // only AF1 => no_af==1, only AF2 => no_af==2, both AF1 and AF2 => no_af==3 if(no_af) { if(af_1 > 80e3) { @@ -394,20 +445,17 @@ void RDSParser::decode_type0(unsigned int *group, bool B) af_string = af2_string; } else if(no_af == 3) { af_string = str(boost::format("%s, %s") % af1_string %af2_string); - } + }*/ } + /* qDebug() << "RDSParser::decode_type0: " - << "\"" << std::string(program_service_name, 8).c_str() - << "\" -" << (traffic_program ? "TP" : "!TP") - << '-' << (traffic_announcement ? "TA" : "!TA") - << '-' << (music_speech ? "Music" : "Speech") - << '-' << (mono_stereo ? "MONO" : "STEREO") - << " - AF:" << af_string.c_str(); - - //send_message(1, std::string(program_service_name, 8)); - //send_message(3, flagstring); - //send_message(6, af_string); + << "\"" << std::string(m_g0_program_service_name, 8).c_str() + << "\" -" << (m_pi_traffic_program ? "TP" : "!TP") + << '-' << (m_g0_traffic_announcement ? "TA" : "!TA") + << '-' << (m_g0_music_speech ? "Music" : "Speech") + << '-' << (m_g0_mono_stereo ? "MONO" : "STEREO") + << " - AF:" << af_string.c_str();*/ } double RDSParser::decode_af(unsigned int af_code) diff --git a/plugins/channel/bfm/rdsparser.h b/plugins/channel/bfm/rdsparser.h index 815834c71..7e16c412f 100644 --- a/plugins/channel/bfm/rdsparser.h +++ b/plugins/channel/bfm/rdsparser.h @@ -18,6 +18,9 @@ #ifndef PLUGINS_CHANNEL_BFM_RDSPARSER_H_ #define PLUGINS_CHANNEL_BFM_RDSPARSER_H_ +#include +#include + class RDSParser { public: @@ -29,6 +32,45 @@ public: */ void parseGroup(unsigned int *group); + void clearAllFields(); + void clearUpdateFlags(); + + // PI data + bool m_pi_updated; + unsigned int m_pi_count; + unsigned int m_pi_program_identification; + unsigned char m_pi_program_type; + bool m_pi_traffic_program; + int m_pi_country_identification; + int m_pi_area_coverage_index; + + // G0 data + bool m_g0_updated; + bool m_g0_af_updated; + unsigned int m_g0_count; + char m_g0_program_service_name[9]; + bool m_g0_traffic_announcement; + bool m_g0_music_speech; + bool m_g0_mono_stereo; + bool m_g0_artificial_head; + bool m_g0_compressed; + bool m_g0_static_pty; + std::set m_g0_alt_freq; + + // Static tables + static const unsigned int offset_pos[5]; + static const unsigned int offset_word[5]; + static const unsigned int syndrome[5]; + static const char * const offset_name[]; + static const std::string pty_table[32]; + static const std::string pi_country_codes[15][5]; + static const std::string coverage_area_codes[16]; + static const std::string rds_group_acronyms[16]; + static const std::string language_codes[44]; + static const std::string tmc_duration[8][2]; + static const int optional_content_lengths[16]; + static const std::string label_descriptions[16]; + private: double decode_af(unsigned int); void decode_optional_content(int, unsigned long int *); @@ -49,36 +91,14 @@ private: void decode_type14(unsigned int* group, bool B); void decode_type15(unsigned int* group, bool B); - unsigned int program_identification; - unsigned char program_type; unsigned char pi_country_identification; - unsigned char pi_area_coverage; unsigned char pi_program_reference_number; - char radiotext[65+1]; - char program_service_name[9]; + char radiotext[64+1]; + bool radiotext_AB_flag; - bool traffic_program; - bool traffic_announcement; - bool music_speech; - bool mono_stereo; - bool artificial_head; - bool compressed; - bool static_pty; bool debug; bool log; - static const unsigned int offset_pos[5]; - static const unsigned int offset_word[5]; - static const unsigned int syndrome[5]; - static const char * const offset_name[]; - static const std::string pty_table[32]; - static const std::string pi_country_codes[15][5]; - static const std::string coverage_area_codes[16]; - static const std::string rds_group_acronyms[16]; - static const std::string language_codes[44]; - static const std::string tmc_duration[8][2]; - static const int optional_content_lengths[16]; - static const std::string label_descriptions[16]; };