From 1d92e548f75b9fa6b5f3322ffa0658e0006c324b Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 7 Jul 2017 12:02:48 +0200 Subject: Add ensemble database, show service label in audio level statistics --- src/dabplussnoop.hpp | 2 + src/ensembledatabase.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++ src/ensembledatabase.hpp | 70 +++++++++++++++++++++++----- src/etianalyse.cpp | 61 ++++++++++++++++--------- src/etianalyse.hpp | 9 ++-- src/fig0_0.cpp | 3 ++ src/fig0_1.cpp | 23 ++++++++++ src/fig0_2.cpp | 14 ++++++ src/fig1.cpp | 17 +++++++ src/figs.cpp | 49 ++++++++++---------- src/figs.hpp | 14 +++++- 11 files changed, 317 insertions(+), 61 deletions(-) create mode 100644 src/ensembledatabase.cpp (limited to 'src') diff --git a/src/dabplussnoop.hpp b/src/dabplussnoop.hpp index 6322c9b..cb7d9ec 100644 --- a/src/dabplussnoop.hpp +++ b/src/dabplussnoop.hpp @@ -151,6 +151,8 @@ class StreamSnoop audio_statistics_t get_audio_statistics(void) const; + uint32_t subchid; + private: DabPlusSnoop dps; int m_index; diff --git a/src/ensembledatabase.cpp b/src/ensembledatabase.cpp new file mode 100644 index 0000000..671eb40 --- /dev/null +++ b/src/ensembledatabase.cpp @@ -0,0 +1,116 @@ +/* + Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/) + Copyright (C) 2017 Matthias P. Braendli (http://www.opendigitalradio.org) + Copyright (C) 2015 Data Path + + 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, either version 3 of the License, or + (at your option) any later version. + + 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Ensemble Database, gather data from the ensemble for the + statistics. + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "ensembledatabase.hpp" + +namespace ensemble_database { + +using namespace std; + +component_t& service_t::get_component(uint32_t subchannel_id) +{ + for (auto& component : components) { + if (component.subchId == subchannel_id) { + return component; + } + } + + throw not_found("component referring to subchannel " + + to_string(subchannel_id) + " not found"); +} + +component_t& service_t::get_or_create_component(uint32_t subchannel_id) +{ + for (auto& component : components) { + if (component.subchId == subchannel_id) { + return component; + } + } + + // not found + component_t new_component; + new_component.subchId = subchannel_id; + components.push_back(new_component); + return components.back(); +} + + +service_t& ensemble_t::get_service(uint32_t service_id) +{ + for (auto& service : services) { + if (service.id == service_id) { + return service; + } + } + + throw not_found("Service " + to_string(service_id) + " not found"); +} + +service_t& ensemble_t::get_or_create_service(uint32_t service_id) +{ + for (auto& service : services) { + if (service.id == service_id) { + return service; + } + } + + // not found + service_t new_service; + new_service.id = service_id; + services.push_back(new_service); + return services.back(); +} + +subchannel_t& ensemble_t::get_subchannel(uint8_t subchannel_id) +{ + for (auto& subchannel : subchannels) { + if (subchannel.id == subchannel_id) { + return subchannel; + } + } + + throw not_found("Subchannel " + to_string(subchannel_id) + " not found"); +} + +subchannel_t& ensemble_t::get_or_create_subchannel(uint8_t subchannel_id) +{ + for (auto& subchannel : subchannels) { + if (subchannel.id == subchannel_id) { + return subchannel; + } + } + + // not found + subchannel_t new_subchannel; + new_subchannel.id = subchannel_id; + subchannels.push_back(new_subchannel); + return subchannels.back(); +} + +} diff --git a/src/ensembledatabase.hpp b/src/ensembledatabase.hpp index ebe7a66..eb83785 100644 --- a/src/ensembledatabase.hpp +++ b/src/ensembledatabase.hpp @@ -33,36 +33,84 @@ # include "config.h" #endif +#include #include #include -struct service_t { - uint32_t id; - std::string label; - std::string shortlabel; - // TODO PTy language announcement -}; +namespace ensemble_database { struct subchannel_t { - uint8_t address; - uint8_t num_cu; - // TODO type bitrate protection + uint8_t id; + uint8_t start_addr; + + enum class protection_type_t { UEP, EEP }; + + protection_type_t protection_type; + + // Long form FIG0/1, i.e. EEP + int protection_option; + int protection_level; + int size; + + // Short form FIG0/1, i.e. UEP + int table_switch; + int table_index; + + // TODO type bitrate }; struct component_t { uint32_t service_id; uint8_t subchId; + + bool primary; + + std::string label; + uint16_t shortlabel_flag; /* TODO uint8_t type; - uint8_t SCIdS; uaType for audio */ }; +struct service_t { + uint32_t id; + std::string label; + uint16_t shortlabel_flag; + bool programme_not_data; + + std::list components; + + component_t& get_component(uint32_t subchannel_id); + component_t& get_or_create_component(uint32_t subchannel_id); + + // TODO PTy language announcement +}; + + +class not_found : public std::runtime_error +{ + public: + not_found(const std::string& msg) : std::runtime_error(msg) {} +}; + struct ensemble_t { + uint16_t EId; + std::string label; + uint16_t shortlabel_flag; + std::list services; std::list subchannels; - std::list components; + + // TODO ecc + + service_t& get_service(uint32_t service_id); + service_t& get_or_create_service(uint32_t service_id); + + subchannel_t& get_subchannel(uint8_t subchannel_id); + subchannel_t& get_or_create_subchannel(uint8_t subchannel_id); }; +} + diff --git a/src/etianalyse.cpp b/src/etianalyse.cpp index 9799fb5..322e45a 100644 --- a/src/etianalyse.cpp +++ b/src/etianalyse.cpp @@ -338,11 +338,13 @@ void ETI_Analyser::eti_analyse() config.streams_to_decode.emplace(std::piecewise_construct, std::make_tuple(i), std::make_tuple(false)); // do not dump to file + config.streams_to_decode.at(i).subchid = scid; } if (config.streams_to_decode.count(i) > 0) { config.streams_to_decode.at(i).set_subchannel_index(stl[i]/3); config.streams_to_decode.at(i).set_index(i); + config.streams_to_decode.at(i).subchid = scid; } } @@ -375,10 +377,7 @@ void ETI_Analyser::eti_analyse() // MST - FIC if (ficf == 1) { - int endmarker = 0; - int figcount = 0; uint8_t *fib, *fig; - uint16_t figcrc; FIGalyser figs; @@ -388,14 +387,28 @@ void ETI_Analyser::eti_analyse() //printbuf(sdesc, 1, ficdata, ficl*4); printbuf(sdesc, 1, NULL, 0); fib = p + 12 + 4*nst; - for(int i = 0; i < ficl*4/32; i++) { + for (int i = 0; i < ficl*4/32; i++) { sprintf(sdesc, "FIB %d", i); printbuf(sdesc, 1, NULL, 0); fig=fib; figs.set_fib(i); rate_new_fib(i); - endmarker=0; - figcount=0; + + const uint16_t figcrc = fib[30]*256 + fib[31]; + crc = 0xffff; + for (int j = 0; j < 30; j++) { + crc = update_crc_ccitt(crc, fib[j]); + } + crc =~ crc; + const bool crccorrect = (crc == figcrc); + if (crccorrect) + sprintf(sdesc, "FIB %d CRC OK", i); + else + sprintf(sdesc, "FIB %d CRC Mismatch: %02x", i, crc); + + + bool endmarker = false; + int figcount = 0; while (!endmarker) { uint8_t figtype, figlen; figtype = (fig[0] & 0xE0) >> 5; @@ -403,28 +416,19 @@ void ETI_Analyser::eti_analyse() figlen = fig[0] & 0x1F; sprintf(sdesc, "FIG %d [%d bytes]", figtype, figlen); printbuf(sdesc, 3, fig+1, figlen); - decodeFIG(config, figs, fig+1, figlen, figtype, 4); + decodeFIG(config, figs, fig+1, figlen, figtype, 4, crccorrect); fig += figlen + 1; figcount += figlen + 1; if (figcount >= 29) - endmarker = 1; + endmarker = true; } else { - endmarker = 1; + endmarker = true; } } - figcrc = fib[30]*256 + fib[31]; - crc = 0xffff; - for (int j = 0; j < 30; j++) { - crc = update_crc_ccitt(crc, fib[j]); - } - crc =~ crc; - if (crc == figcrc) - sprintf(sdesc, "FIB %d CRC OK", i); - else - sprintf(sdesc, "FIB %d CRC Mismatch: %02x", i, crc); printbuf(sdesc,3,fib+30,2); + fib += 32; } @@ -500,6 +504,18 @@ void ETI_Analyser::eti_analyse() for (const auto& snoop : config.streams_to_decode) { printf("Statistics for %d:\n", snoop.first); + for (const auto& service : ensemble.services) { + for (const auto& component : service.components) { + if (component.subchId == snoop.first and + component.primary) { + printf("Label %s\n", service.label.c_str()); + if (not component.label.empty()) { + printf("Component label %s\n", component.label.c_str()); + } + } + } + } + const auto& stat = snoop.second.get_audio_statistics(); printf(" Avg L: %d dB\n", absolute_to_dB(stat.average_level_left)); printf(" Avg R: %d dB\n", absolute_to_dB(stat.average_level_right)); @@ -529,7 +545,8 @@ void ETI_Analyser::decodeFIG( uint8_t* f, uint8_t figlen, uint16_t figtype, - int indent) + int indent, + bool fibcrccorrect) { char desc[512]; @@ -537,6 +554,7 @@ void ETI_Analyser::decodeFIG( case 0: { fig0_common_t fig0(f, figlen, ensemble, wm_decoder); + fig0.fibcrccorrect = fibcrccorrect; const display_settings_t disp(config.is_fig_to_be_printed(figtype, fig0.ext()), indent); @@ -559,7 +577,8 @@ void ETI_Analyser::decodeFIG( case 1: {// SHORT LABELS - fig1_common_t fig1(f, figlen); + fig1_common_t fig1(ensemble, f, figlen); + fig1.fibcrccorrect = fibcrccorrect; const display_settings_t disp(config.is_fig_to_be_printed(figtype, fig1.ext()), indent); if (disp.print) { diff --git a/src/etianalyse.hpp b/src/etianalyse.hpp index 7d02071..fb17c8f 100644 --- a/src/etianalyse.hpp +++ b/src/etianalyse.hpp @@ -71,7 +71,9 @@ struct eti_analyse_config_t { class ETI_Analyser { public: ETI_Analyser(eti_analyse_config_t &config) : - config(config) {} + config(config), + ensemble(), + wm_decoder() {} void eti_analyse(void); @@ -82,11 +84,12 @@ class ETI_Analyser { uint8_t* f, uint8_t figlen, uint16_t figtype, - int indent); + int indent, + bool fibcrccorrect); eti_analyse_config_t &config; - ensemble_t ensemble; + ensemble_database::ensemble_t ensemble; WatermarkDecoder wm_decoder; }; diff --git a/src/fig0_0.cpp b/src/fig0_0.cpp index 162110b..b643185 100644 --- a/src/fig0_0.cpp +++ b/src/fig0_0.cpp @@ -37,6 +37,9 @@ fig_result_t fig0_0(fig0_common_t& fig0, const display_settings_t &disp) const uint16_t eid = f[1]*256+f[2]; r.msgs.push_back(strprintf("Ensemble ID=0x%02x", eid)); + if (fig0.fibcrccorrect) { + fig0.ensemble.EId = eid; + } const uint8_t cid = (f[1] & 0xF0) >> 4; r.msgs.emplace_back(1, strprintf("Country ID=%d", cid)); diff --git a/src/fig0_1.cpp b/src/fig0_1.cpp index 76cda36..4f35b75 100644 --- a/src/fig0_1.cpp +++ b/src/fig0_1.cpp @@ -61,6 +61,16 @@ fig_result_t fig0_1(fig0_common_t& fig0, const display_settings_t &disp) (f[i+1]); int long_flag = (f[i+2] >> 7); + if (fig0.fibcrccorrect) { + auto& subch = fig0.ensemble.get_or_create_subchannel(subch_id); + + subch.id = subch_id; + subch.start_addr = start_addr; + subch.protection_type = long_flag ? + ensemble_database::subchannel_t::protection_type_t::EEP : + ensemble_database::subchannel_t::protection_type_t::UEP; + } + if (long_flag) { int option = (f[i+2] >> 4) & 0x07; int protection_level = (f[i+2] >> 2) & 0x03; @@ -84,6 +94,13 @@ fig_result_t fig0_1(fig0_common_t& fig0, const display_settings_t &disp) } r.msgs.push_back(strprintf("subch size %d", subchannel_size)); + + if (fig0.fibcrccorrect) { + auto& subch = fig0.ensemble.get_subchannel(subch_id); + subch.protection_option = option; + subch.protection_level = protection_level; + subch.size = subchannel_size; + } } else { int table_switch = (f[i+2] >> 6) & 0x01; @@ -97,6 +114,12 @@ fig_result_t fig0_1(fig0_common_t& fig0, const display_settings_t &disp) } r.msgs.push_back(strprintf("table index %d", table_index)); + if (fig0.fibcrccorrect) { + auto& subch = fig0.ensemble.get_subchannel(subch_id); + subch.table_switch = table_switch; + subch.table_index = table_index; + } + i += 3; } } diff --git a/src/fig0_2.cpp b/src/fig0_2.cpp index 85de44c..48e9adf 100644 --- a/src/fig0_2.cpp +++ b/src/fig0_2.cpp @@ -101,6 +101,12 @@ fig_result_t fig0_2(fig0_common_t& fig0, const display_settings_t &disp) r.msgs.emplace_back(1, strprintf("CAID=%d", caid)); } + if (fig0.fibcrccorrect) { + auto& service = fig0.ensemble.get_or_create_service(sid); + service.programme_not_data = (fig0.pd() == 0); + } + + k++; for (int i = 0; i < ncomp; i++) { uint8_t scomp[2]; @@ -128,6 +134,13 @@ fig_result_t fig0_2(fig0_common_t& fig0, const display_settings_t &disp) psdesc = "Primary service"; } + if (fig0.fibcrccorrect) { + // TODO is subchid unique, or do we also need to take timd into the key for identifying + // our components? + auto& service = fig0.ensemble.get_service(sid); + auto& component = service.get_or_create_component(subchid); + component.primary = (ps != 0); + } if (timd == 0) { //MSC stream audio @@ -171,6 +184,7 @@ fig_result_t fig0_2(fig0_common_t& fig0, const display_settings_t &disp) r.msgs.emplace_back(2, strprintf("SubChannel ID=%02X", subchid)); r.msgs.emplace_back(2, strprintf("CA=%d", ca)); } + k += 2; } } diff --git a/src/fig1.cpp b/src/fig1.cpp index bb2591c..5ae92c5 100644 --- a/src/fig1.cpp +++ b/src/fig1.cpp @@ -58,6 +58,12 @@ fig_result_t fig1_select(fig1_common_t& fig1, const display_settings_t &disp) r.msgs.push_back(strprintf("Ensemble ID: 0x%04X", eid)); r.msgs.push_back(strprintf("Label: \"%s\"", label)); r.msgs.push_back(strprintf("Short label mask: 0x%04X", flag)); + + if (fig1.fibcrccorrect) { + fig1.ensemble.EId = eid; + fig1.ensemble.label = label; + fig1.ensemble.shortlabel_flag = flag; + } } break; @@ -68,6 +74,17 @@ fig_result_t fig1_select(fig1_common_t& fig1, const display_settings_t &disp) r.msgs.push_back(strprintf("Service ID: 0x%04X", sid)); r.msgs.push_back(strprintf("Label: \"%s\"", label)); r.msgs.push_back(strprintf("Short label mask: 0x%04X", flag)); + + if (fig1.fibcrccorrect) { + try { + auto& service = fig1.ensemble.get_service(sid); + service.label = label; + service.shortlabel_flag = flag; + } + catch (ensemble_database::not_found &e) { + r.errors.push_back("Not yet in DB"); + } + } } break; diff --git a/src/figs.cpp b/src/figs.cpp index cd557bf..4a6729a 100644 --- a/src/figs.cpp +++ b/src/figs.cpp @@ -77,30 +77,31 @@ void figs_cleardb() fig_result_t fig0_select(fig0_common_t& fig0, const display_settings_t &disp) { switch (fig0.ext()) { - case 0: return fig0_0(fig0, disp); break; - case 1: return fig0_1(fig0, disp); break; - case 2: return fig0_2(fig0, disp); break; - case 3: return fig0_3(fig0, disp); break; - case 5: return fig0_5(fig0, disp); break; - case 6: return fig0_6(fig0, disp); break; - case 8: return fig0_8(fig0, disp); break; - case 9: return fig0_9(fig0, disp); break; - case 10: return fig0_10(fig0, disp); break; - case 11: return fig0_11(fig0, disp); break; - case 13: return fig0_13(fig0, disp); break; - case 14: return fig0_14(fig0, disp); break; - case 16: return fig0_16(fig0, disp); break; - case 17: return fig0_17(fig0, disp); break; - case 18: return fig0_18(fig0, disp); break; - case 19: return fig0_19(fig0, disp); break; - case 21: return fig0_21(fig0, disp); break; - case 22: return fig0_22(fig0, disp); break; - case 24: return fig0_24(fig0, disp); break; - case 25: return fig0_25(fig0, disp); break; - case 26: return fig0_26(fig0, disp); break; - case 27: return fig0_27(fig0, disp); break; - case 28: return fig0_28(fig0, disp); break; - case 31: return fig0_31(fig0, disp); break; + case 0: return fig0_0(fig0, disp); break; // EId + case 1: return fig0_1(fig0, disp); break; // SubCh Id SAd protection size + case 2: return fig0_2(fig0, disp); break; // Service SId and components (SCId) + case 3: return fig0_3(fig0, disp); break; // Component in packet mode + // 4 not implemented // Component conditional access + case 5: return fig0_5(fig0, disp); break; // Component language + case 6: return fig0_6(fig0, disp); break; // Service linking + case 8: return fig0_8(fig0, disp); break; // More component stuff + case 9: return fig0_9(fig0, disp); break; // Country, LTO, ECC, subfield with per-service ECC and LTO + case 10: return fig0_10(fig0, disp); break; // Date and Time + case 11: return fig0_11(fig0, disp); break; // Region definition + case 13: return fig0_13(fig0, disp); break; // User application + case 14: return fig0_14(fig0, disp); break; // Subchannel FEC scheme + case 16: return fig0_16(fig0, disp); break; // Service Programme Number PNum + case 17: return fig0_17(fig0, disp); break; // Service PTy + case 18: return fig0_18(fig0, disp); break; // Service: Announcement cluster definition + case 19: return fig0_19(fig0, disp); break; // Cluster: Announcement switching + case 21: return fig0_21(fig0, disp); break; // Frequency Information + case 22: return fig0_22(fig0, disp); break; // TII database + case 24: return fig0_24(fig0, disp); break; // OE Services + case 25: return fig0_25(fig0, disp); break; // OE Announcement + case 26: return fig0_26(fig0, disp); break; // OE Announcement switching + case 27: return fig0_27(fig0, disp); break; // FM Announcement + case 28: return fig0_28(fig0, disp); break; // FM Announcement switching + case 31: return fig0_31(fig0, disp); break; // FIC Redirection default: break; } diff --git a/src/figs.hpp b/src/figs.hpp index 8decea3..64cfa48 100644 --- a/src/figs.hpp +++ b/src/figs.hpp @@ -59,16 +59,19 @@ struct fig0_common_t { fig0_common_t( uint8_t* fig_data, uint16_t fig_len, - ensemble_t &ens, + ensemble_database::ensemble_t &ens, WatermarkDecoder &wm_dec) : f(fig_data), figlen(fig_len), ensemble(ens), + fibcrccorrect(true), wm_decoder(wm_dec) { } uint8_t* f; uint16_t figlen; - ensemble_t& ensemble; + ensemble_database::ensemble_t& ensemble; + // The ensemble only gets updated when the fib crc is ok + bool fibcrccorrect; WatermarkDecoder &wm_decoder; uint16_t cn(void) { return (f[0] & 0x80) >> 7; } @@ -79,11 +82,18 @@ struct fig0_common_t { struct fig1_common_t { fig1_common_t( + ensemble_database::ensemble_t &ens, uint8_t* fig_data, uint16_t fig_len) : + fibcrccorrect(true), + ensemble(ens), f(fig_data), figlen(fig_len) {} + // The ensemble only gets updated when the fib crc is ok + bool fibcrccorrect; + ensemble_database::ensemble_t& ensemble; + uint8_t* f; uint16_t figlen; -- cgit v1.2.3