aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2017-07-07 12:02:48 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2017-07-07 12:02:48 +0200
commit1d92e548f75b9fa6b5f3322ffa0658e0006c324b (patch)
treeb0f7006d4f9c9a43591f32e2feb8349d7b25b89f
parent4870d0c148eb49ffeb5482ae55d9cc4c9cc89786 (diff)
downloadetisnoop-1d92e548f75b9fa6b5f3322ffa0658e0006c324b.tar.gz
etisnoop-1d92e548f75b9fa6b5f3322ffa0658e0006c324b.tar.bz2
etisnoop-1d92e548f75b9fa6b5f3322ffa0658e0006c324b.zip
Add ensemble database, show service label in audio level statistics
-rw-r--r--Makefile.am2
-rw-r--r--src/dabplussnoop.hpp2
-rw-r--r--src/ensembledatabase.cpp116
-rw-r--r--src/ensembledatabase.hpp70
-rw-r--r--src/etianalyse.cpp61
-rw-r--r--src/etianalyse.hpp9
-rw-r--r--src/fig0_0.cpp3
-rw-r--r--src/fig0_1.cpp23
-rw-r--r--src/fig0_2.cpp14
-rw-r--r--src/fig1.cpp17
-rw-r--r--src/figs.cpp49
-rw-r--r--src/figs.hpp14
12 files changed, 318 insertions, 62 deletions
diff --git a/Makefile.am b/Makefile.am
index f66e7e5..da24f2f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,7 +16,7 @@ etisnoop_SOURCES = src/dabplussnoop.cpp src/dabplussnoop.hpp \
src/etianalyse.cpp src/etianalyse.hpp \
src/etisnoop.cpp \
src/faad_decoder.cpp src/faad_decoder.hpp \
- src/ensembledatabase.hpp \
+ src/ensembledatabase.hpp src/ensembledatabase.cpp \
src/fig0_0.cpp \
src/fig0_10.cpp \
src/fig0_11.cpp \
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 <http://www.gnu.org/licenses/>.
+
+ Ensemble Database, gather data from the ensemble for the
+ statistics.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ 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 <stdexcept>
#include <string>
#include <list>
-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<component_t> 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<service_t> services;
std::list<subchannel_t> subchannels;
- std::list<component_t> 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;