/*
Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
Copyright (C) 2018 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 .
Authors:
Sergio Sagliocco
Matthias P. Braendli
/ | |- ')|) |-|_ _ (|,_ .| _ ,_ \
Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include
#include
#include "etianalyse.hpp"
#include "etiinput.hpp"
#include "figs.hpp"
extern "C" {
#include "lib_crc.h"
}
#include "utils.hpp"
using namespace std;
// Signal handler flag
std::atomic quit(false);
bool
eti_analyse_config_t::is_fig_to_be_printed(int type, int extension) const
{
if (figs_to_display.empty()) {
return true;
}
return std::find(
figs_to_display.begin(),
figs_to_display.end(),
make_pair(type, extension)) != figs_to_display.end();
}
static
string replace_first(const string& source, const string& from, const string& to)
{
string s(source);
size_t i = s.find(from);
if (i != string::npos) {
s.replace(i, from.length(), to);
}
return s;
}
static void print_fig_result(const fig_result_t& fig_result, const display_settings_t& disp)
{
if (disp.print) {
for (const auto& msg : fig_result.msgs) {
std::string s;
for (int i = 0; i < msg.level; i++) {
s += " ";
}
s += replace_first(msg.msg, "=", ": ");
for (int i = 0; i < disp.indent; i++) {
printf(" ");
}
printf("%s\n", s.c_str());
}
if (not fig_result.errors.empty()) {
for (const auto& err : fig_result.errors) {
fprintf(stderr, "ERRORS: %s\n" , err.c_str());
}
}
}
}
void ETI_Analyser::analyse()
{
if (config.etifd != nullptr) {
return eti_analyse();
}
else if (config.ficfd != nullptr) {
return fic_analyse();
}
}
void ETI_Analyser::eti_analyse()
{
uint8_t p[ETINIPACKETSIZE];
string desc;
char prevsync[3]={0x00,0x00,0x00};
uint8_t ficf,nst,fp,mid,ficl;
uint16_t fl,crch;
uint16_t crc;
uint8_t scid,tpl;
uint16_t sad[64],stl[64];
char sdesc[256];
uint32_t frame_nb = 0, frame_sec = 0, frame_ms = 0;
static int last_fct = -1;
bool running = true;
size_t num_frames = 0;
int stream_type = ETI_STREAM_TYPE_NONE;
if (identify_eti_format(config.etifd, &stream_type) == -1) {
fprintf(stderr, "Could not identify stream type\n");
running = false;
}
else {
fprintf(stderr, "Identified ETI type ");
if (stream_type == ETI_STREAM_TYPE_RAW)
fprintf(stderr, "RAW\n");
else if (stream_type == ETI_STREAM_TYPE_STREAMED)
fprintf(stderr, "STREAMED\n");
else if (stream_type == ETI_STREAM_TYPE_FRAMED)
fprintf(stderr, "FRAMED\n");
else
fprintf(stderr, "?\n");
}
if (config.analyse_fig_rates) {
rate_display_header(config.analyse_fig_rates_per_second);
}
FILE *stat_fd = nullptr;
if (not config.statistics_filename.empty()) {
stat_fd = fopen(config.statistics_filename.c_str(), "w");
if (stat_fd == nullptr) {
fprintf(stderr, "Could not open statistics file: %s\n",
strerror(errno));
return;
}
}
while (running) {
int ret = get_eti_frame(config.etifd, stream_type, p);
if (ret == -1) {
fprintf(stderr, "ETI file read error\n");
break;
}
else if (ret == 0) {
fprintf(stderr, "End of ETI\n");
break;
}
// Timestamp and Frame Number
uint32_t frame_h = (frame_sec / 3600);
uint32_t frame_m = (frame_sec - (frame_h * 3600)) / 60;
uint32_t frame_s = (frame_sec - (frame_h * 3600) - (frame_m * 60));
printf("---\n");
printf("Frame: %d\n", frame_nb);
printf("Time: %02d:%02d:%02d.%03d\n", frame_h, frame_m, frame_s, frame_ms);
frame_ms += 24; // + 24 ms
if (frame_ms >= 1000) {
frame_ms -= 1000;
frame_sec++;
}
frame_nb++;
// SYNC
printbuf("SYNC", 0, p, 4);
// SYNC - ERR
if (p[0] == 0xFF) {
printbuf("ERR", 1, p, 1, "", "No Error");
}
else {
printbuf("ERR", 1, p, 1, "", "Error");
if (!config.ignore_error) {
fprintf(stderr, "Aborting because of SYNC error\n");
break;
}
}
// SYNC - FSYNC
if (memcmp(prevsync, "\x00\x00\x00", 3) == 0) {
if ( (memcmp(p + 1, "\x07\x3a\xb6", 3) == 0) ||
(memcmp(p + 1, "\xf8\xc5\x49", 3) == 0) ) {
desc = "OK";
memcpy(prevsync, p+1, 3);
}
else {
desc = "Wrong FSYNC";
memcpy(prevsync, "\x00\x00\x00", 3);
}
}
else if (memcmp(prevsync, "\x07\x3a\xb6", 3) == 0) {
if (memcmp(p + 1, "\xf8\xc5\x49", 3) != 0) {
desc = "Wrong FSYNC";
memcpy(prevsync, "\x00\x00\x00", 3);
} else {
desc = "OK";
memcpy(prevsync, p + 1, 3);
}
}
else if (memcmp(prevsync, "\xf8\xc5\x49", 3) == 0) {
if (memcmp(p + 1, "\x07\x3a\xb6", 3) != 0) {
desc = "Wrong FSYNC";
memcpy(prevsync, "\x00\x00\x00", 3);
} else {
desc = "OK";
memcpy(prevsync, p + 1, 3);
}
}
printbuf("FSYNC", 1, p + 1, 3, "", desc);
// LIDATA
printbuf("LIDATA");
// LIDATA - FC
printbuf("FC", 1, p+4, 4, "Frame Characterization field");
// LIDATA - FC - FCT
int fct = p[4];
printbuf("FCT", 2, p+4, 1, "Frame Count", to_string(fct));
if (last_fct != -1) {
if ((last_fct + 1) % 250 != fct) {
fprintf(stderr, "Error: FCT not contiguous\n");
}
}
last_fct = fct;
// LIDATA - FC - FICF
ficf = (p[5] & 0x80) >> 7;
{
stringstream ss;
if (ficf == 1) {
ss << "FIC Information are present";
}
else {
ss << "FIC Information are not present";
}
printbuf("FICF", 2, nullptr, 0, ss.str(), to_string(ficf));
}
// LIDATA - FC - NST
nst = p[5] & 0x7F;
{
printbuf("NST", 2, nullptr, 0, "Number of streams", to_string(nst));
}
// LIDATA - FC - FP
fp = (p[6] & 0xE0) >> 5;
{
stringstream ss;
ss << (int)fp;
printbuf("FP", 2, &fp, 1, "Frame Phase", to_string(fp));
}
// LIDATA - FC - MID
mid = (p[6] & 0x18) >> 3;
{
string modestr;
if (mid != 0) {
modestr = to_string(mid);
}
else {
modestr = "4";
}
printbuf("MID", 2, &mid, 1, "Mode Identity", modestr);
set_mode_identity(mid);
}
// LIDATA - FC - FL
fl = (p[6] & 0x07) * 256 + p[7];
{
printbuf("FL", 2, nullptr, 0, "Frame Length in words", to_string(fl));
}
if (ficf == 0) {
ficl = 0;
}
else if (mid == 3) {
ficl = 32;
}
else {
ficl = 24;
}
// STC
printvalue("STC", 1);
for (int i=0; i < nst; i++) {
printsequencestart(2);
printbuf("Stream Number", 3, p + 8 + 4*i, 4, "", to_string(i));
scid = (p[8 + 4*i] & 0xFC) >> 2;
printvalue("SCID", 3, "Sub-channel Identifier", to_string(scid));
sad[i] = (p[8+4*i] & 0x03) * 256 + p[9+4*i];
printvalue("SAD", 3, "Sub-channel Start Address", to_string(sad[i]));
tpl = (p[10+4*i] & 0xFC) >> 2;
if ((tpl & 0x20) >> 5 == 1) {
uint8_t opt, plevel;
string plevelstr;
string rate;
int num_cu = 0;
opt = (tpl & 0x1c) >> 2;
plevel = (tpl & 0x03);
if (opt == 0x00) {
if (plevel == 0) {
plevelstr = "1-A";
rate = "1/4";
num_cu = 16;
}
else if (plevel == 1) {
plevelstr = "2-A";
rate = "3/8";
num_cu = 8;
}
else if (plevel == 2) {
plevelstr = "3-A";
rate = "1/2";
num_cu = 6;
}
else if (plevel == 3) {
plevelstr = "4-A";
rate = "3/4";
num_cu = 4;
}
}
else if (opt == 0x01) {
if (plevel == 0) {
plevelstr = "1-B";
rate = "4/9";
num_cu = 27;
}
else if (plevel == 1) {
plevelstr = "2-B";
rate = "4/7";
num_cu = 21;
}
else if (plevel == 2) {
plevelstr = "3-B";
rate = "4/6";
num_cu = 18;
}
else if (plevel == 3) {
plevelstr = "4-B";
rate = "4/5";
num_cu = 15;
}
}
else {
plevelstr = "Unknown option " + to_string(opt);
}
printvalue("TPL", 3, "Sub-channel Type and Protection Level");
printvalue("EEP", 4, "Equal Error Protection", to_string(tpl));
printvalue("Level", 5, "", plevelstr);
if (not rate.empty()) {
printvalue("Rate", 5, "", rate);
}
if (num_cu) {
printvalue("CUs", 5, "", to_string(num_cu));
}
}
else {
uint8_t tsw, uepidx;
tsw = (tpl & 0x08);
uepidx = tpl & 0x07;
printvalue("TPL", 3, "Sub-channel Type and Protection Level");
printvalue("UEP", 4, "Unequal Error Protection", to_string(tpl));
printvalue("Table switch", 5, "", to_string(tsw));
printvalue("Index", 5, "", to_string(uepidx));
}
stl[i] = (p[10+4*i] & 0x03) * 256 + \
p[11+4*i];
printvalue("STL", 3, "Sub-channel Stream Length", to_string(stl[i]));
printvalue("bitrate", 3, "kbit/s", to_string(stl[i]*8/3));
if (config.statistics and config.streams_to_decode.count(i) == 0) {
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;
}
}
// EOH
printbuf("EOH", 1, p + 8 + 4*nst, 4, "End Of Header");
uint16_t mnsc = p[8 + 4*nst] * 256 + p[8 + 4*nst + 1];
printbuf("MNSC", 2, p+8+4*nst, 2, "Multiplex Network Signalling Channel", strprintf("%04x", mnsc));
crch = p[8 + 4*nst + 2]*256 + p[8 + 4*nst + 3];
crc = 0xffff;
for (int i=4; i < 8 + 4*nst + 2; i++) {
crc = update_crc_ccitt(crc, p[i]);
}
crc =~ crc;
if (crc == crch) {
sprintf(sdesc, "OK");
}
else {
sprintf(sdesc, "Mismatch: %02x",crc);
}
printbuf("Header CRC", 2, p + 8 + 4*nst + 2, 2, "", sdesc);
// MST - FIC
if (ficf == 1) {
uint8_t *fib, *fig;
FIGalyser figs;
uint8_t ficdata[32*4];
memcpy(ficdata, p + 12 + 4*nst, ficl*4);
printvalue("FIG Length", 1, "FIC length in bytes", to_string(ficl*4));
printvalue("FIC", 1);
fib = p + 12 + 4*nst;
for (int i = 0; i < ficl*4/32; i++) {
printsequencestart(2);
printvalue("FIB", 3, "", to_string(i));
fig=fib;
figs.set_fib(i);
rate_new_fib(i);
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)
printvalue("CRC", 3, "", "OK");
else {
printvalue("CRC", 3, "",
strprintf("Mismatch: %04x %04x", crc, figcrc));
}
printvalue("FIGs", 3);
bool endmarker = false;
int figcount = 0;
while (!endmarker) {
uint8_t figtype, figlen;
figtype = (fig[0] & 0xE0) >> 5;
if (figtype != 7) {
figlen = fig[0] & 0x1F;
printsequencestart(4);
decodeFIG(config, figs, fig+1, figlen, figtype, 5, crccorrect);
fig += figlen + 1;
figcount += figlen + 1;
if (figcount >= 29)
endmarker = true;
}
else {
endmarker = true;
}
}
fib += 32;
}
if (config.analyse_fic_carousel) {
figs.analyse(get_mode_identity());
}
}
printvalue("Stream Data", 1);
int offset = 0;
for (int i=0; i < nst; i++) {
uint8_t streamdata[684*8];
memcpy(streamdata, p + 12 + 4*nst + ficf*ficl*4 + offset, stl[i]*8);
offset += stl[i] * 8;
printsequencestart(2);
printvalue("Id", 3, "", to_string(i));
printvalue("Length", 3, "", to_string(stl[i]*8));
printvalue("Selected for decoding", 3, "",
(config.streams_to_decode.count(i) > 0 ? "true" : "false"));
if (get_verbosity() > 1) {
printbuf("Data", 3, streamdata, stl[i]*8);
}
if (config.streams_to_decode.count(i) > 0) {
config.streams_to_decode.at(i).push(streamdata, stl[i]*8);
}
}
//* EOF (4 Bytes)
printbuf("EOF", 1, p + 12 + 4*nst + ficf*ficl*4 + offset, 4);
// CRC (2 Bytes)
crch = p[12 + 4*nst + ficf*ficl*4 + offset] * 256 + \
p[12 + 4*nst + ficf*ficl*4 + offset + 1];
crc = 0xffff;
for (int i = 12 + 4*nst; i < 12 + 4*nst + ficf*ficl*4 + offset; i++) {
crc = update_crc_ccitt(crc, p[i]);
}
crc =~ crc;
if (crc == crch)
sprintf(sdesc, "OK");
else
sprintf(sdesc, "Mismatch: %02x", crc);
printbuf("CRC", 2, p + 12 + 4*nst + ficf*ficl*4 + offset, 2, "", sdesc);
// RFU (2 Bytes)
printbuf("RFU", 2, p + 12 + 4*nst + ficf*ficl*4 + offset + 2, 2);
//* TIST (4 Bytes)
const size_t tist_ix = 12 + 4*nst + ficf*ficl*4 + offset + 4;
uint32_t TIST = (uint32_t)(p[tist_ix]) << 24 |
(uint32_t)(p[tist_ix+1]) << 16 |
(uint32_t)(p[tist_ix+2]) << 8 |
(uint32_t)(p[tist_ix+3]);
sprintf(sdesc, "%f", (TIST & 0xFFFFFF) / 16384.0);
printbuf("TIST", 1, p + tist_ix, 4, "Time Stamp (ms)", sdesc);
if (config.analyse_fig_rates and (fct % 250) == 0) {
rate_display_analysis(false, config.analyse_fig_rates_per_second);
}
num_frames++;
if (config.num_frames_to_decode > 0 and
num_frames >= config.num_frames_to_decode) {
fprintf(stderr, "Decoded %zu ETI frames\n", num_frames);
break;
}
if (quit.load()) running = false;
}
if (config.statistics) {
assert(stat_fd != nullptr);
fprintf(stat_fd, "# Statistics from ETISnoop. This file should be valid YAML\n");
fprintf(stat_fd, "---\n");
fprintf(stat_fd, "ensemble:\n");
fprintf(stat_fd, " id: 0x%x\n", ensemble.EId);
fprintf(stat_fd, " label: %s\n", ensemble.label.c_str());
fprintf(stat_fd, " shortlabel: %s\n",
flag_to_shortlabel(ensemble.label, ensemble.shortlabel_flag).c_str());
fprintf(stat_fd, "audio:\n");
for (const auto& snoop : config.streams_to_decode) {
bool corresponding_service_found = false;
for (const auto& service : ensemble.services) {
for (const auto& component : service.components) {
if (component.subchId == snoop.second.subchid and
component.primary) {
corresponding_service_found = true;
fprintf(stat_fd, " - service_id: 0x%x\n", service.id);
fprintf(stat_fd, " subchannel_id: 0x%x\n", component.subchId);
fprintf(stat_fd, " label: %s\n", service.label.c_str());
fprintf(stat_fd, " shortlabel: %s\n",
flag_to_shortlabel(service.label, service.shortlabel_flag).c_str());
if (not component.label.empty()) {
fprintf(stat_fd, " component_label: %s\n", component.label.c_str());
}
try {
const auto& subch = ensemble.get_subchannel(component.subchId);
fprintf(stat_fd, " subchannel:\n");
fprintf(stat_fd, " id: %d\n", subch.id);
fprintf(stat_fd, " SAd: %d\n", subch.start_addr);
using ensemble_database::subchannel_t;
switch (subch.protection_type) {
case subchannel_t::protection_type_t::EEP:
switch (subch.protection_option) {
case subchannel_t::protection_eep_option_t::EEP_A:
fprintf(stat_fd, " protection: EEP %d-A\n",
subch.protection_level + 1);
break;
case subchannel_t::protection_eep_option_t::EEP_B:
fprintf(stat_fd, " protection: EEP %d-B\n",
subch.protection_level + 1);
break;
default:
fprintf(stat_fd, " protection: unknown\n");
break;
}
fprintf(stat_fd, " size: %d\n", subch.size);
break;
case ensemble_database::subchannel_t::protection_type_t::UEP:
fprintf(stat_fd, " table_switch: %d\n", subch.table_switch);
fprintf(stat_fd, " table_index: %d\n", subch.table_index);
break;
}
}
catch (ensemble_database::not_found &e)
{
fprintf(stat_fd, " subchannel: not found\n");
}
}
}
}
if (not corresponding_service_found) {
fprintf(stat_fd, " - service_id: unknown\n");
}
const auto& stat = snoop.second.get_audio_statistics();
fprintf(stat_fd, " audio:\n");
fprintf(stat_fd, " average: %d %d\n",
absolute_to_dB(stat.average_level_left),
absolute_to_dB(stat.average_level_right));
fprintf(stat_fd, " peak: %d %d\n",
absolute_to_dB(stat.peak_level_left),
absolute_to_dB(stat.peak_level_right));
}
fclose(stat_fd);
}
if (config.decode_watermark) {
std::string watermark(wm_decoder.calculate_watermark());
printf("Watermark:\n %s\n", watermark.c_str());
}
if (config.analyse_fig_rates) {
rate_display_analysis(false, config.analyse_fig_rates_per_second);
}
figs_cleardb();
}
void ETI_Analyser::fic_analyse()
{
if (config.analyse_fig_rates) {
rate_display_header(config.analyse_fig_rates_per_second);
}
FILE *stat_fd = nullptr;
if (not config.statistics_filename.empty()) {
stat_fd = fopen(config.statistics_filename.c_str(), "w");
if (stat_fd == nullptr) {
fprintf(stderr, "Could not open statistics file: %s\n",
strerror(errno));
return;
}
}
bool running = true;
int i = 0;
while (running) {
FIGalyser figs;
uint8_t fib[32];
if (fread(fib, 32, 1, config.ficfd) == 0) {
break;
}
printsequencestart(2);
printvalue("FIB", 3, "", to_string(i));
figs.set_fib(i);
rate_new_fib(i);
const uint16_t figcrc = fib[30]*256 + fib[31];
uint16_t 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)
printvalue("CRC", 3, "", "OK");
else {
printvalue("CRC", 3, "",
strprintf("Mismatch: %04x %04x", crc, figcrc));
}
printvalue("FIGs", 3);
uint8_t *fig = fib;
bool endmarker = false;
int figcount = 0;
while (!endmarker) {
uint8_t figtype, figlen;
figtype = (fig[0] & 0xE0) >> 5;
if (figtype != 7) {
figlen = fig[0] & 0x1F;
printsequencestart(4);
decodeFIG(config, figs, fig+1, figlen, figtype, 5, crccorrect);
fig += figlen + 1;
figcount += figlen + 1;
if (figcount >= 29)
endmarker = true;
}
else {
endmarker = true;
}
}
if (quit.load()) running = false;
if (config.analyse_fic_carousel) {
figs.analyse(1);
}
i = (i+1) % 3;
}
}
void ETI_Analyser::decodeFIG(
const eti_analyse_config_t &config,
FIGalyser &figs,
uint8_t* f,
uint8_t figlen,
uint16_t figtype,
int indent,
bool fibcrccorrect)
{
switch (figtype) {
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);
printvalue("FIG", disp, "", strprintf("0/%d", fig0.ext()));
if (get_verbosity() > 0) {
printbuf("Data", disp, f, figlen);
}
if (disp.print) {
printvalue("Length", disp, "", to_string(figlen));
printvalue("OE", disp, "", to_string(fig0.oe()));
printvalue("C/N", disp, "", to_string(fig0.cn()));
printvalue("P/D", disp, "", to_string(fig0.pd()));
}
figs.push_back(figtype, fig0.ext(), figlen);
auto fig_result = fig0_select(fig0, disp);
fig_result.figtype = figtype;
fig_result.figext = fig0.ext();
printvalue("Decoding", disp);
print_fig_result(fig_result, disp+1);
rate_announce_fig(figtype, fig0.ext(), fig_result.complete);
}
break;
case 1:
{// SHORT LABELS
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);
printvalue("FIG", disp, "", strprintf("1/%d", fig1.ext()));
if (get_verbosity() > 0) {
printbuf("Data", disp, f, figlen);
}
if (disp.print) {
printvalue("Length", disp, "", to_string(figlen));
printvalue("OE", disp, "", to_string(fig1.oe()));
}
figs.push_back(figtype, fig1.ext(), figlen);
auto fig_result = fig1_select(fig1, disp);
fig_result.figtype = figtype;
fig_result.figext = fig1.ext();
printvalue("Decoding", disp);
print_fig_result(fig_result, disp+1);
rate_announce_fig(figtype, fig1.ext(), fig_result.complete);
}
break;
case 2:
{// EXTENDED LABELS
const uint16_t ext = f[0] & 0x07;
const display_settings_t disp(config.is_fig_to_be_printed(figtype, ext), indent);
fig2_common_t fig2(ensemble, f, figlen);
auto fig_result = fig2_select(fig2, disp);
printvalue("FIG", disp, "", strprintf("2/%d", ext));
if (get_verbosity() > 0) {
printbuf("Data", disp, f, figlen);
}
if (disp.print) {
printvalue("Length", disp, "", to_string(figlen));
printvalue("RFU", disp, "", to_string(fig2.rfu()));
printvalue("Toggle flag", disp, "", to_string(fig2.toggle_flag()));
printvalue("Segment index", disp, "", to_string(fig2.segment_index()));
}
figs.push_back(figtype, fig2.ext(), figlen);
bool complete = true;
rate_announce_fig(figtype, ext, complete);
}
break;
case 5:
{// FIDC
uint16_t ext;
uint8_t d1 = (f[0] & 0x80) >> 7;
uint8_t d2 = (f[0] & 0x40) >> 6;
uint8_t tcid = (f[0] & 0x38) >> 5;
ext = f[0] & 0x07;
const display_settings_t disp(config.is_fig_to_be_printed(figtype, ext), indent);
printvalue("FIG", disp, "", strprintf("5/%d", ext));
if (get_verbosity() > 0) {
printbuf("Data", disp, f, figlen);
}
if (disp.print) {
printvalue("Length", disp, "", to_string(figlen));
printvalue("D1", disp, "", to_string(d1));
printvalue("D2", disp, "", to_string(d2));
printvalue("TCId", disp, "", to_string(tcid));
}
figs.push_back(figtype, ext, figlen);
bool complete = true; // TODO verify
rate_announce_fig(figtype, ext, complete);
}
break;
case 6:
{// Conditional access
fprintf(stderr, "ERROR: ETI contains unsupported FIG 6\n");
printvalue("FIG", indent, "", "6 - unsupported");
}
break;
default:
{
fprintf(stderr, "ERROR: ETI contains unknown FIG %d\n", figtype);
printvalue("FIG", indent, "", strprintf("%d - unsupported", figtype));
}
break;
}
}