aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2016-01-15 15:03:38 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2016-01-15 15:03:38 +0100
commit3576bfcd68ff58e022efc07ef8e1f1b757ccf6fb (patch)
treedd8b4ae93ce1b87260794022f56cf8892958692a
parent275b55e39a84a3877e7e68b7c800bd0de28c128c (diff)
downloadetisnoop-3576bfcd68ff58e022efc07ef8e1f1b757ccf6fb.tar.gz
etisnoop-3576bfcd68ff58e022efc07ef8e1f1b757ccf6fb.tar.bz2
etisnoop-3576bfcd68ff58e022efc07ef8e1f1b757ccf6fb.zip
Refactor all FIG0/x into separate files
-rw-r--r--CMakeLists.txt35
-rw-r--r--Makefile24
-rw-r--r--README.md18
-rw-r--r--etisnoop.cpp2331
-rw-r--r--fig0_0.cpp60
-rw-r--r--fig0_1.cpp91
-rw-r--r--fig0_10.cpp72
-rw-r--r--fig0_11.cpp185
-rw-r--r--fig0_13.cpp101
-rw-r--r--fig0_14.cpp59
-rw-r--r--fig0_16.cpp100
-rw-r--r--fig0_17.cpp117
-rw-r--r--fig0_18.cpp91
-rw-r--r--fig0_19.cpp85
-rw-r--r--fig0_2.cpp146
-rw-r--r--fig0_21.cpp302
-rw-r--r--fig0_22.cpp163
-rw-r--r--fig0_24.cpp88
-rw-r--r--fig0_25.cpp91
-rw-r--r--fig0_26.cpp88
-rw-r--r--fig0_27.cpp83
-rw-r--r--fig0_28.cpp69
-rw-r--r--fig0_3.cpp88
-rw-r--r--fig0_31.cpp110
-rw-r--r--fig0_5.cpp89
-rw-r--r--fig0_6.cpp170
-rw-r--r--fig0_8.cpp117
-rw-r--r--fig0_9.cpp131
-rw-r--r--figs.cpp112
-rw-r--r--figs.hpp96
-rw-r--r--tables.cpp311
-rw-r--r--tables.hpp43
-rw-r--r--utils.cpp154
-rw-r--r--utils.hpp57
-rw-r--r--watermarkdecoder.hpp98
35 files changed, 3610 insertions, 2365 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a0c139..1e730dc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -46,13 +46,40 @@ include_directories(${FEC_INCLUDE_DIRS})
list(APPEND etisnoop_sources
dabplussnoop.cpp
- lib_crc.c
- firecode.c
- faad_decoder.cpp
- wavfile.c
etiinput.cpp
etisnoop.cpp
+ faad_decoder.cpp
+ fig0_0.cpp
+ fig0_1.cpp
+ fig0_2.cpp
+ fig0_3.cpp
+ fig0_5.cpp
+ fig0_6.cpp
+ fig0_8.cpp
+ fig0_9.cpp
+ fig0_10.cpp
+ fig0_11.cpp
+ fig0_13.cpp
+ fig0_14.cpp
+ fig0_16.cpp
+ fig0_17.cpp
+ fig0_18.cpp
+ fig0_19.cpp
+ fig0_21.cpp
+ fig0_22.cpp
+ fig0_24.cpp
+ fig0_25.cpp
+ fig0_26.cpp
+ fig0_27.cpp
+ fig0_28.cpp
+ fig0_31.cpp
+ figs.cpp
+ firecode.c
+ lib_crc.c
rsdecoder.cpp
+ tables.cpp
+ utils.cpp
+ wavfile.c
)
set_source_files_properties(${etisnoop_sources} PROPERTIES LANGUAGE "CXX")
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 84f4cf1..0000000
--- a/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-
-CXX=g++
-
-SOURCES=etisnoop.cpp dabplussnoop.cpp lib_crc.c firecode.c faad_decoder.cpp wavfile.c etiinput.cpp rsdecoder.cpp
-HEADERS=dabplussnoop.h lib_crc.h firecode.h faad_decoder.h wavfile.h etiinput.h rsdecoder.h
-
-all: etisnoop
-
-etisnoop: $(SOURCES) $(HEADERS)
- $(CXX) -std=c++11 -Wall -ggdb $(SOURCES) -lfaad -lfec -o etisnoop
-
-etisnoop-static: libfaad $(SOURCES) $(HEADERS)
- $(CXX) -std=c++11 -Wall -ggdb $(SOURCES) -lfec -Ifaad2-2.7/include faad2-2.7/libfaad/.libs/libfaad.a -o etisnoop
-
-libfaad:
- make -C ./faad2-2.7
-
-.PHONY: tags
-tags:
- ctags -R .
-
-
-clean:
- rm -f etisnoop *.o
diff --git a/README.md b/README.md
index f91196a..1a58e50 100644
--- a/README.md
+++ b/README.md
@@ -17,24 +17,6 @@ Make sure you have cmake installed and do
cmake ..
make
-
-Build with old makefile
------------------------
-
-Alternatively, use the old Makefile. There are two
-variants:
-
- make etisnoop
-
-compiles the tool, dynamically linked against libfaad
-
-Or you can extract the sources for libfaad2-2.7 into a subfolder of the
-same name, configure it, and if required modify it. Then you can do
-
- make etisnoop-static
-
-to compile a version of etisnoop compiled against your own copy of FAAD.
-
About
-----
diff --git a/etisnoop.cpp b/etisnoop.cpp
index 049790d..09f8280 100644
--- a/etisnoop.cpp
+++ b/etisnoop.cpp
@@ -44,9 +44,11 @@
#include <sstream>
#include <time.h>
#include "lib_crc.h"
-
+#include "utils.hpp"
#include "dabplussnoop.h"
#include "etiinput.h"
+#include "figs.hpp"
+#include "watermarkdecoder.hpp"
struct FIG
{
@@ -55,68 +57,6 @@ struct FIG
int len;
};
-class WatermarkDecoder
-{
- public:
- void push_confind_bit(bool confind)
- {
- // The ConfInd of FIG 0/10 contains the CRC-DABMUX and ODR-DabMux watermark
- m_confind_bits.push_back(confind);
- }
-
- std::string calculate_watermark()
- {
- // First try to find the 0x55 0x55 sync in the waternark data
- size_t bit_ix;
- int alternance_count = 0;
- bool last_bit = 1;
- for (bit_ix = 0; bit_ix < m_confind_bits.size(); bit_ix++) {
- if (alternance_count == 16) {
- break;
- }
- else {
- if (last_bit != m_confind_bits[bit_ix]) {
- last_bit = m_confind_bits[bit_ix];
- alternance_count++;
- }
- else {
- alternance_count = 0;
- last_bit = 1;
- }
- }
-
- }
-
- printf("Found SYNC at offset %zu out of %zu\n", bit_ix - alternance_count, m_confind_bits.size());
-
- std::stringstream watermark_ss;
-
- uint8_t b = 0;
- size_t i = 0;
- while (bit_ix < m_confind_bits.size()) {
-
- b |= m_confind_bits[bit_ix] << (7 - i);
-
- if (i == 7) {
- watermark_ss << (char)b;
-
- b = 0;
- i = 0;
- }
- else {
- i++;
- }
-
- bit_ix += 2;
- }
-
- return watermark_ss.str();
- }
-
- private:
- std::vector<bool> m_confind_bits;
-};
-
class FIGalyser
{
public:
@@ -204,167 +144,6 @@ struct FIG0_13_shortAppInfo
uint8_t SCIdS:4;
} PACKED;
-class FIG0_5 {
- public:
- // Constructor
- FIG0_5() {
- // Initialization of Language_code_to_char_map:
- // ETSI TS 101 756 V1.6.1 Table 9: European languages
- Language_code_to_char_map[0x00] = "Unknown/not applicable";
- Language_code_to_char_map[0x01] = "Albanian";
- Language_code_to_char_map[0x02] = "Breton";
- Language_code_to_char_map[0x03] = "Catalan";
- Language_code_to_char_map[0x04] = "Croatian";
- Language_code_to_char_map[0x05] = "Welsh";
- Language_code_to_char_map[0x06] = "Czech";
- Language_code_to_char_map[0x07] = "Danish";
- Language_code_to_char_map[0x08] = "German";
- Language_code_to_char_map[0x09] = "English";
- Language_code_to_char_map[0x0A] = "Spanish";
- Language_code_to_char_map[0x0B] = "Esperanto";
- Language_code_to_char_map[0x0C] = "Estonian";
- Language_code_to_char_map[0x0D] = "Basque";
- Language_code_to_char_map[0x0E] = "Faroese";
- Language_code_to_char_map[0x0F] = "French";
- Language_code_to_char_map[0x10] = "Frisian";
- Language_code_to_char_map[0x11] = "Irish";
- Language_code_to_char_map[0x12] = "Gaelic";
- Language_code_to_char_map[0x13] = "Galician";
- Language_code_to_char_map[0x14] = "Icelandic";
- Language_code_to_char_map[0x15] = "Italian";
- Language_code_to_char_map[0x16] = "Lappish";
- Language_code_to_char_map[0x17] = "Latin";
- Language_code_to_char_map[0x18] = "Latvian";
- Language_code_to_char_map[0x19] = "Luxembourgian";
- Language_code_to_char_map[0x1A] = "Lithuanian";
- Language_code_to_char_map[0x1B] = "Hungarian";
- Language_code_to_char_map[0x1C] = "Maltese";
- Language_code_to_char_map[0x1D] = "Dutch";
- Language_code_to_char_map[0x1E] = "Norwegian";
- Language_code_to_char_map[0x1F] = "Occitan";
- Language_code_to_char_map[0x20] = "Polish";
- Language_code_to_char_map[0x21] = "Portuguese";
- Language_code_to_char_map[0x22] = "Romanian";
- Language_code_to_char_map[0x23] = "Romansh";
- Language_code_to_char_map[0x24] = "Serbian";
- Language_code_to_char_map[0x25] = "Slovak";
- Language_code_to_char_map[0x26] = "Slovene";
- Language_code_to_char_map[0x27] = "Finnish";
- Language_code_to_char_map[0x28] = "Swedish";
- Language_code_to_char_map[0x29] = "Turkish";
- Language_code_to_char_map[0x2A] = "Flemish";
- Language_code_to_char_map[0x2B] = "Walloon";
- Language_code_to_char_map[0x2C] = "rfu";
- Language_code_to_char_map[0x2D] = "rfu";
- Language_code_to_char_map[0x2E] = "rfu";
- Language_code_to_char_map[0x2F] = "rfu";
- Language_code_to_char_map[0x30] = "Reserved for national assignment";
- Language_code_to_char_map[0x31] = "Reserved for national assignment";
- Language_code_to_char_map[0x32] = "Reserved for national assignment";
- Language_code_to_char_map[0x33] = "Reserved for national assignment";
- Language_code_to_char_map[0x34] = "Reserved for national assignment";
- Language_code_to_char_map[0x35] = "Reserved for national assignment";
- Language_code_to_char_map[0x36] = "Reserved for national assignment";
- Language_code_to_char_map[0x37] = "Reserved for national assignment";
- Language_code_to_char_map[0x38] = "Reserved for national assignment";
- Language_code_to_char_map[0x39] = "Reserved for national assignment";
- Language_code_to_char_map[0x3A] = "Reserved for national assignment";
- Language_code_to_char_map[0x3B] = "Reserved for national assignment";
- Language_code_to_char_map[0x3C] = "Reserved for national assignment";
- Language_code_to_char_map[0x3D] = "Reserved for national assignment";
- Language_code_to_char_map[0x3E] = "Reserved for national assignment";
- Language_code_to_char_map[0x3F] = "Reserved for national assignment";
-
- // ETSI TS 101 756 V1.6.1 Table 10: Other languages
- Language_code_to_char_map[0x40] = "Background sound/clean feed";
- Language_code_to_char_map[0x41] = "rfu";
- Language_code_to_char_map[0x42] = "rfu";
- Language_code_to_char_map[0x43] = "rfu";
- Language_code_to_char_map[0x44] = "rfu";
- Language_code_to_char_map[0x45] = "Zulu";
- Language_code_to_char_map[0x46] = "Vietnamese";
- Language_code_to_char_map[0x47] = "Uzbek";
- Language_code_to_char_map[0x48] = "Urdu";
- Language_code_to_char_map[0x49] = "Ukranian";
- Language_code_to_char_map[0x4A] = "Thai";
- Language_code_to_char_map[0x4B] = "Telugu";
- Language_code_to_char_map[0x4C] = "Tatar";
- Language_code_to_char_map[0x4D] = "Tamil";
- Language_code_to_char_map[0x4E] = "Tadzhik";
- Language_code_to_char_map[0x4F] = "Swahili";
- Language_code_to_char_map[0x50] = "Sranan Tongo";
- Language_code_to_char_map[0x51] = "Somali";
- Language_code_to_char_map[0x52] = "Sinhalese";
- Language_code_to_char_map[0x53] = "Shona";
- Language_code_to_char_map[0x54] = "Serbo-Croat";
- Language_code_to_char_map[0x55] = "Rusyn";
- Language_code_to_char_map[0x56] = "Russian";
- Language_code_to_char_map[0x57] = "Quechua";
- Language_code_to_char_map[0x58] = "Pushtu";
- Language_code_to_char_map[0x59] = "Punjabi";
- Language_code_to_char_map[0x5A] = "Persian";
- Language_code_to_char_map[0x5B] = "Papiamento";
- Language_code_to_char_map[0x5C] = "Oriya";
- Language_code_to_char_map[0x5D] = "Nepali";
- Language_code_to_char_map[0x5E] = "Ndebele";
- Language_code_to_char_map[0x5F] = "Marathi";
- Language_code_to_char_map[0x60] = "Moldavian";
- Language_code_to_char_map[0x61] = "Malaysian";
- Language_code_to_char_map[0x62] = "Malagasay";
- Language_code_to_char_map[0x63] = "Macedonian";
- Language_code_to_char_map[0x64] = "Laotian";
- Language_code_to_char_map[0x65] = "Korean";
- Language_code_to_char_map[0x66] = "Khmer";
- Language_code_to_char_map[0x67] = "Kazakh";
- Language_code_to_char_map[0x68] = "Kannada";
- Language_code_to_char_map[0x69] = "Japanese";
- Language_code_to_char_map[0x6A] = "Indonesian";
- Language_code_to_char_map[0x6B] = "Hindi";
- Language_code_to_char_map[0x6C] = "Hebrew";
- Language_code_to_char_map[0x6D] = "Hausa";
- Language_code_to_char_map[0x6E] = "Gurani";
- Language_code_to_char_map[0x6F] = "Gujurati";
- Language_code_to_char_map[0x70] = "Greek";
- Language_code_to_char_map[0x71] = "Georgian";
- Language_code_to_char_map[0x72] = "Fulani";
- Language_code_to_char_map[0x73] = "Dari";
- Language_code_to_char_map[0x74] = "Chuvash";
- Language_code_to_char_map[0x75] = "Chinese";
- Language_code_to_char_map[0x76] = "Burmese";
- Language_code_to_char_map[0x77] = "Bulgarian";
- Language_code_to_char_map[0x78] = "Bengali";
- Language_code_to_char_map[0x79] = "Belorussian";
- Language_code_to_char_map[0x7A] = "Bambora";
- Language_code_to_char_map[0x7B] = "Azerbaijani";
- Language_code_to_char_map[0x7C] = "Assamese";
- Language_code_to_char_map[0x7D] = "Armenian";
- Language_code_to_char_map[0x7E] = "Arabic";
- Language_code_to_char_map[0x7F] = "Amharic";
- }
-
- // Destructor
- ~FIG0_5() {
- // Remove elements from Language_code_to_char_map map container
- Language_code_to_char_map.clear();
- }
-
- // Language_to_char decode fig 0/5 language code in string
- // Input : Language code number
- // Return: Language char *
- const char * Language_to_char(uint8_t language) {
- if (Language_code_to_char_map.count(language) > 0) {
- return Language_code_to_char_map[language];
- }
- else {
- return "";
- }
- }
-
- private:
- // Map between fig 0/5 Language code and Language char
- std::map<uint8_t, const char *> Language_code_to_char_map;
-};
-
#define ETINIPACKETSIZE 6144
@@ -379,152 +158,7 @@ struct eti_analyse_config_t {
};
-// Globals
-static int verbosity;
-static uint8_t Mode_Identity = 0;
-
-// fig 0/2 fig 0/3 DSCTy types string:
-const char *DSCTy_types_str[64] = {
- // ETSI TS 101 756 V1.6.1 (2014-05) table 2
- "Unspecified data", "Traffic Message Channel (TMC)",
- "Emergency Warning System (EWS)", "Interactive Text Transmission System (ITTS)",
- "Paging", "Transparent Data Channel (TDC)",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "MPEG-2 Transport Stream, see ETSI TS 102 427", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Rfu",
- "Rfu", "Embedded IP packets",
- "Multimedia Object Transfer (MOT)", "Proprietary service: no DSCTy signalled",
- "Not used", "Not used"
-};
-
-// map between fig 0/6 database key and LA to detect activation and deactivation of links
-std::map<uint16_t, bool> fig0_6_key_la;
-
-// fig 0/9 global variables
-uint8_t Ensemble_ECC=0, International_Table_Id=0;
-int8_t Ensemble_LTO=0;
-bool LTO_uniq;
-
-// fig 0/14 FEC Scheme: this 2-bit field shall indicate the Forward Error Correction scheme in use, as follows:
-const char *FEC_schemes_str[4] = {
- "no FEC scheme applied",
- "FEC scheme applied according to ETSI EN 300 401 clause 5.3.5",
- "reserved for future definition",
- "reserved for future definition"
-};
-
-// fig 0/17 Programme type codes
-#define INTERNATIONAL_TABLE_SIZE 2
-#define PROGRAMME_TYPE_CODES_SIZE 32
-const char *Programme_type_codes_str[INTERNATIONAL_TABLE_SIZE][PROGRAMME_TYPE_CODES_SIZE] = {
- { // ETSI TS 101 756 V1.6.1 (2014-05) table 12
- "No programme type", "News",
- "Current Affairs", "Information",
- "Sport", "Education",
- "Drama", "Culture",
- "Science", "Varied",
- "Pop Music", "Rock Music",
- "Easy Listening Music", "Light Classical",
- "Serious Classical", "Other Music",
- "Weather/meteorology", "Finance/Business",
- "Children's programmes", "Social Affairs",
- "Religion", "Phone In",
- "Travel", "Leisure",
- "Jazz Music", "Country Music",
- "National Music", "Oldies Music",
- "Folk Music", "Documentary",
- "Not used", "Not used"
- },
- { // ETSI TS 101 756 V1.6.1 (2014-05) table 13
- "No program type", "News",
- "Information", "Sports",
- "Talk", "Rock",
- "Classic Rock", "Adult Hits",
- "Soft Rock", "Top 40",
- "Country", "Oldies",
- "Soft", "Nostalgia",
- "Jazz", "Classical",
- "Rhythm and Blues", "Soft Rhythm and Blues",
- "Foreign Language", "Religious Music",
- "Religious Talk", "Personality",
- "Public", "College",
- "rfu", "rfu",
- "rfu", "rfu",
- "rfu", "Weather",
- "Not used", "Not used"
- }
-};
-
-// fig 0/18 0/19 announcement types (ETSI TS 101 756 V1.6.1 (2014-05) table 14 & 15)
-const char *announcement_types_str[16] = {
- "Alarm",
- "Road Traffic flash",
- "Transport flash",
- "Warning/Service",
- "News flash",
- "Area weather flash",
- "Event announcement",
- "Special event",
- "Programme Information",
- "Sport report",
- "Financial report",
- "Reserved for future definition",
- "Reserved for future definition",
- "Reserved for future definition",
- "Reserved for future definition",
- "Reserved for future definition"
-};
-
-// fig 0/22 struct
-typedef struct lat_lng {
- double latitude, longitude;
-}Lat_Lng;
-// map for fig 0/22 database
-std::map<uint16_t, Lat_Lng> fig0_22_key_Lat_Lng;
-
-// ETSI TS 102 367 V1.2.1 (2006-01) 5.4.1 Conditional Access Mode (CAMode)
-const char *CAMode_str[8] = {
- "Sub-channel CA", "Data Group CA",
- "MOT CA", "proprietary CA",
- "reserved", "reserved",
- "reserved", "reserved"
-};
-
-
// Function prototypes
-void printinfo(string header,
- int indent_level,
- int min_verb=0);
-
-void printbuf(string header,
- int indent_level,
- uint8_t* buffer,
- size_t size,
- string desc="");
-
void decodeFIG(FIGalyser &figs,
WatermarkDecoder &wm_decoder,
uint8_t* figdata,
@@ -535,26 +169,8 @@ void decodeFIG(FIGalyser &figs,
int eti_analyse(eti_analyse_config_t& config);
const char *get_programme_type_str(size_t int_table_Id, size_t pty);
-char *strcatPNum(char *dest_str, uint16_t Programme_Number);
int sprintfMJD(char *dst, int mjd);
-std::string get_fig_0_13_userapp(int user_app_type)
-{
- switch (user_app_type) {
- case 0x000: return "Reserved for future definition";
- case 0x001: return "Not used";
- case 0x002: return "MOT Slideshow";
- case 0x003: return "MOT Broadacst Web Site";
- case 0x004: return "TPEG";
- case 0x005: return "DGPS";
- case 0x006: return "TMC";
- case 0x007: return "EPG";
- case 0x008: return "DAB Java";
- case 0x44a: return "Journaline";
- default: return "Reserved for future applications";
- }
-}
-
#define no_argument 0
#define required_argument 1
#define optional_argument 2
@@ -587,7 +203,6 @@ int main(int argc, char *argv[])
string file_name("-");
map<int, DabPlusSnoop> streams_to_decode;
- verbosity = 0;
bool ignore_error = false;
bool analyse_fic_carousel = false;
bool decode_watermark = false;
@@ -612,7 +227,7 @@ int main(int argc, char *argv[])
analyse_fic_carousel = true;
break;
case 'v':
- verbosity++;
+ set_verbosity(get_verbosity() + 1);
break;
case 'w':
decode_watermark = true;
@@ -809,7 +424,7 @@ int eti_analyse(eti_analyse_config_t& config)
ss << "4";
}
printbuf("MID - Mode Identity", 2, &mid, 1, ss.str());
- Mode_Identity = mid;
+ set_mode_identity(mid);
}
// LIDATA - FC - FL
@@ -991,7 +606,7 @@ int eti_analyse(eti_analyse_config_t& config)
else {
sprintf(sdesc, "id %d, len %d, not selected for decoding", i, stl[i]*8);
}
- if (verbosity > 1) {
+ if (get_verbosity() > 1) {
printbuf("Stream Data", 1, streamdata, stl[i]*8, sdesc);
}
else {
@@ -1030,7 +645,7 @@ int eti_analyse(eti_analyse_config_t& config)
printbuf("TIST - Time Stamp", 1, p+12+4*nst+ficf*ficl*4+offset+4, 4, sdesc);
- if (verbosity) {
+ if (get_verbosity()) {
printf("-------------------------------------------------------------------------------------------------------------\n");
}
}
@@ -1048,9 +663,7 @@ int eti_analyse(eti_analyse_config_t& config)
printf("Watermark:\n %s\n", watermark.c_str());
}
- // remove elements from fig0_6_key_la and fig0_22_key_Lat_Lng map containers
- fig0_6_key_la.clear();
- fig0_22_key_Lat_Lng.clear();
+ figs_cleardb();
return 0;
}
@@ -1063,1812 +676,19 @@ void decodeFIG(FIGalyser &figs,
int indent)
{
char desc[512];
- static FIG0_5 fig0_5;
switch (figtype) {
case 0:
{
- uint16_t ext,cn,oe,pd;
+ fig0_common_t fig0(f, figlen, wm_decoder);
- cn = (f[0] & 0x80) >> 7;
- oe = (f[0] & 0x40) >> 6;
- pd = (f[0] & 0x20) >> 5;
- ext = f[0] & 0x1F;
sprintf(desc, "FIG %d/%d: C/N=%d OE=%d P/D=%d",
- figtype, ext, cn, oe, pd);
+ figtype, fig0.ext(), fig0.cn(), fig0.oe(), fig0.pd());
printbuf(desc, indent, f+1, figlen-1);
- figs.push_back(figtype, ext, figlen);
-
- switch (ext) {
-
- case 0: // FIG 0/0 Ensemble information
- { // ETSI EN 300 401 6.4
- uint8_t cid, al, ch, hic, lowc, occ;
- uint16_t eid, eref;
-
- eid = f[1]*256+f[2];
- cid = (f[1] & 0xF0) >> 4;
- eref = (f[1] & 0x0F)*256 + \
- f[2];
- ch = (f[3] & 0xC0) >> 6;
- al = (f[3] & 0x20) >> 5;
- hic = f[3] & 0x1F;
- lowc = f[4];
- if (ch != 0) {
- occ = f[5];
- sprintf(desc,
- "Ensemble ID=0x%02x (Country id=%d, Ensemble reference=%d), Change flag=%d, Alarm flag=%d, CIF Count=%d/%d, Occurance change=%d",
- eid, cid, eref, ch, al, hic, lowc, occ);
- }
- else {
- sprintf(desc,
- "Ensemble ID=0x%02x (Country id=%d, Ensemble reference=%d), Change flag=%d, Alarm flag=%d, CIF Count=%d/%d",
- eid, cid, eref, ch, al, hic, lowc);
- }
- printbuf(desc, indent+1, NULL, 0);
-
- }
- break;
- case 1: // FIG 0/1 Basic sub-channel organization
- { // ETSI EN 300 401 6.2.1
- int i = 1;
-
- while (i < figlen-3) {
- // iterate over subchannels
- int subch_id = f[i] >> 2;
- int start_addr = ((f[i] & 0x03) << 8) |
- (f[i+1]);
- int long_flag = (f[i+2] >> 7);
-
- if (long_flag) {
- int option = (f[i+2] >> 4) & 0x07;
- int protection_level = (f[i+2] >> 2) & 0x03;
- int subchannel_size = ((f[i+2] & 0x03) << 8 ) |
- f[i+3];
-
- i += 4;
-
- if (option == 0x00) {
- sprintf(desc,
- "Subch 0x%x, start_addr %d, long, EEP %d-A, subch size %d",
- subch_id, start_addr, protection_level, subchannel_size);
- }
- else if (option == 0x01) {
- sprintf(desc,
- "Subch 0x%x, start_addr %d, long, EEP %d-B, subch size %d",
- subch_id, start_addr, protection_level, subchannel_size);
- }
- else {
- sprintf(desc,
- "Subch 0x%x, start_addr %d, long, invalid option %d, protection %d, subch size %d",
- subch_id, start_addr, option, protection_level, subchannel_size);
- }
- }
- else {
- int table_switch = (f[i+2] >> 6) & 0x01;
- uint32_t table_index = (f[i+2] & 0x3F);
-
-
- if (table_switch == 0) {
- sprintf(desc,
- "Subch 0x%x, start_addr %d, short, table index %d",
- subch_id, start_addr, table_index);
- }
- else {
- sprintf(desc,
- "Subch 0x%x, start_addr %d, short, invalid table_switch(=1), table index %d",
- subch_id, start_addr, table_index);
- }
-
- i += 3;
- }
- printbuf(desc, indent+1, NULL, 0);
- }
-
- }
- break;
- case 2: // FIG 0/2 Basic service and service component definition
- { // ETSI EN 300 401 6.3.1
- uint16_t sref, sid;
- uint8_t cid, ecc, local, caid, ncomp, timd, ps, ca, subchid, scty;
- int k=1;
- string psdesc;
- char sctydesc[32];
-
- while (k<figlen) {
- if (pd == 0) {
- sid = f[k] * 256 + f[k+1];
- cid = (f[k] & 0xF0) >> 4;
- sref = (f[k] & 0x0F) * 256 + f[k+1];
- k += 2;
- }
- else {
- sid = f[k] * 256 * 256 * 256 + \
- f[k+1] * 256 * 256 + \
- f[k+2] * 256 + \
- f[k+3];
-
- ecc = f[k];
- cid = (f[k+1] & 0xF0) >> 4;
- sref = (f[k+1] & 0x0F) * 256 * 256 + \
- f[k+2] * 256 + \
- f[k+3];
-
- k += 4;
- }
-
- local = (f[k] & 0x80) >> 7;
- caid = (f[k] & 0x70) >> 4;
- ncomp = f[k] & 0x0F;
-
- if (pd == 0)
- sprintf(desc,
- "Service ID=0x%X (Country id=%d, Service reference=%d), Number of components=%d, Local flag=%d, CAID=%d",
- sid, cid, sref, ncomp, local, caid);
- else
- sprintf(desc,
- "Service ID=0x%X (ECC=%d, Country id=%d, Service reference=%d), Number of components=%d, Local flag=%d, CAID=%d",
- sid, ecc, cid, sref, ncomp, local, caid);
- printbuf(desc, indent+1, NULL, 0);
-
- k++;
- for (int i=0; i<ncomp; i++) {
- uint8_t scomp[2];
-
- memcpy(scomp, f+k, 2);
- sprintf(desc, "Component[%d]", i);
- printbuf(desc, indent+2, scomp, 2, "");
- timd = (scomp[0] & 0xC0) >> 6;
- ps = (scomp[1] & 0x02) >> 1;
- ca = scomp[1] & 0x01;
- scty = scomp[0] & 0x3F;
- subchid = (scomp[1] & 0xFC) >> 2;
-
- /* useless, kept as reference
- if (timd == 3) {
- uint16_t scid;
- scid = scty*64 + subchid;
- }
- */
-
- if (ps == 0) {
- psdesc = "Secondary service";
- }
- else {
- psdesc = "Primary service";
- }
-
-
- if (timd == 0) {
- //MSC stream audio
- if (scty == 0)
- sprintf(sctydesc, "MPEG Foreground sound (%d)", scty);
- else if (scty == 1)
- sprintf(sctydesc, "MPEG Background sound (%d)", scty);
- else if (scty == 2)
- sprintf(sctydesc, "Multi Channel sound (%d)", scty);
- else if (scty == 63)
- sprintf(sctydesc, "AAC sound (%d)", scty);
- else
- sprintf(sctydesc, "Unknown ASCTy (%d)", scty);
-
- sprintf(desc, "Stream audio mode, %s, %s, SubChannel ID=%02X, CA=%d", psdesc.c_str(), sctydesc, subchid, ca);
- printbuf(desc, indent+3, NULL, 0);
- }
- else if (timd == 1) {
- // MSC stream data
- sprintf(sctydesc, "DSCTy=%d %s", scty, DSCTy_types_str[scty]);
- sprintf(desc, "Stream data mode, %s, %s, SubChannel ID=%02X, CA=%d", psdesc.c_str(), sctydesc, subchid, ca);
- printbuf(desc, indent+3, NULL, 0);
- }
- else if (timd == 2) {
- // FIDC
- sprintf(sctydesc, "DSCTy=%d %s", scty, DSCTy_types_str[scty]);
- sprintf(desc, "FIDC mode, %s, %s, Fast Information Data Channel ID=%02X, CA=%d", psdesc.c_str(), sctydesc, subchid, ca);
- printbuf(desc, indent+3, NULL, 0);
- }
- else if (timd == 3) {
- // MSC Packet mode
- sprintf(desc, "MSC Packet Mode, %s, Service Component ID=%02X, CA=%d", psdesc.c_str(), subchid, ca);
- printbuf(desc, indent+3, NULL, 0);
- }
- k += 2;
- }
- }
- }
- break;
- case 3: // FIG 0/3 Service component in packet mode with or without Conditional Access
- { // ETSI EN 300 401 6.3.2
- uint16_t SCId, Packet_address, CAOrg;
- uint8_t i = 1, Rfa, DSCTy, SubChId, CAMode, SharedFlag;
- char tmpbuf[256];
- bool CAOrg_flag, DG_flag, Rfu;
-
- while (i < (figlen - 4)) {
- // iterate over service component in packet mode
- SCId = ((uint16_t)f[i] << 4) | ((uint16_t)(f[i+1] >> 4) & 0x0F);
- Rfa = (f[i+1] >> 1) & 0x07;
- CAOrg_flag = f[i+1] & 0x01;
- DG_flag = (f[i+2] >> 7) & 0x01;
- Rfu = (f[i+2] >> 6) & 0x01;
- DSCTy = f[i+2] & 0x3F;
- SubChId = (f[i+3] >> 2);
- Packet_address = ((uint16_t)(f[i+3] & 0x03) << 8) | ((uint16_t)f[i+4]);
- sprintf(desc, "SCId=0x%X, CAOrg flag=%d CAOrg field %s, DG flag=%d data groups are %sused to transport the service component, DSCTy=%d %s, SubChId=0x%X, Packet address=0x%X",
- SCId, CAOrg_flag, CAOrg_flag?"present":"absent", DG_flag, DG_flag?"not ":"", DSCTy, DSCTy_types_str[DSCTy], SubChId, Packet_address);
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- if (Rfu != 0) {
- sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
- strcat(desc, tmpbuf);
- }
- i += 5;
- if (CAOrg_flag) {
- if (i < (figlen - 1)) {
- CAOrg = ((uint16_t)f[i] << 8) | ((uint16_t)f[i+1]);
- CAMode = (f[i] >> 5);
- SharedFlag = f[i+1];
- sprintf(tmpbuf, ", CAOrg=0x%X CAMode=%d \"%s\" SharedFlag=0x%X%s",
- CAOrg, CAMode, CAMode_str[CAMode], SharedFlag, (SharedFlag == 0)?" invalid":"");
- strcat(desc, tmpbuf);
- }
- else {
- sprintf(tmpbuf, ", invalid figlen");
- strcat(desc, tmpbuf);
- }
- i += 2;
- }
- printbuf(desc, indent+1, NULL, 0);
- }
- }
- break;
- case 5: // FIG 0/5 Service component language
- { // ETSI EN 300 401 8.1.2
- uint16_t SCId;
- uint8_t i = 1, SubChId, FIDCId, Language, Rfa;
- char tmpbuf[256];
- bool LS_flag, MSC_FIC_flag;
-
- while (i < (figlen - 1)) {
- // iterate over service component language
- LS_flag = f[i] >> 7;
- if (LS_flag == 0) {
- // Short form (L/S = 0)
- MSC_FIC_flag = (f[i] >> 6) & 0x01;
- Language = f[i+1];
- if (MSC_FIC_flag == 0) {
- // 0: MSC in Stream mode and SubChId identifies the sub-channel
- SubChId = f[i] & 0x3F;
- sprintf(desc, "L/S flag=%d short form, MSC/FIC flag=%d MSC, SubChId=0x%X, Language=0x%X %s",
- LS_flag, MSC_FIC_flag, SubChId, Language, fig0_5.Language_to_char(Language));
- }
- else {
- // 1: FIC and FIDCId identifies the component
- FIDCId = f[i] & 0x3F;
- sprintf(desc, "L/S flag=%d short form, MSC/FIC flag=%d FIC, FIDCId=0x%X, Language=0x%X %s",
- LS_flag, MSC_FIC_flag, FIDCId, Language, fig0_5.Language_to_char(Language));
- }
- printbuf(desc, indent+1, NULL, 0);
- i += 2;
- }
- else {
- // Long form (L/S = 1)
- if (i < (figlen - 2)) {
- Rfa = (f[i] >> 4) & 0x07;
- SCId = (((uint16_t)f[i] & 0x0F) << 8) | (uint16_t)f[i+1];
- Language = f[i+2];
- sprintf(desc, "L/S flag=%d long form", LS_flag);
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", SCId=0x%X, Language=0x%X %s",
- SCId, Language, fig0_5.Language_to_char(Language));
- strcat(desc, tmpbuf);
- printbuf(desc, indent+1, NULL, 0);
- }
- i += 3;
- }
- }
- }
- break;
- case 6: // FIG 0/6 Service linking information
- { // ETSI EN 300 401 8.1.15
- uint32_t j;
- uint16_t LSN, key;
- uint8_t i = 1, Number_of_Ids, IdLQ;
- char signal_link[256];
- bool Id_list_flag, LA, SH, ILS, Shd;
-
- while (i < (figlen - 1)) {
- // iterate over service linking
- Id_list_flag = (f[i] >> 7) & 0x01;
- LA = (f[i] >> 6) & 0x01;
- SH = (f[i] >> 5) & 0x01;
- ILS = (f[i] >> 4) & 0x01;
- LSN = ((f[i] & 0x0F) << 8) | f[i+1];
- key = (oe << 15) | (pd << 14) | (SH << 13) | (ILS << 12) | LSN;
- strcpy(signal_link, "");
- // check activation / deactivation
- if ((fig0_6_key_la.count(key) > 0) && (fig0_6_key_la[key] != LA)) {
- if (LA == 0) {
- strcat(signal_link, " deactivated");
- }
- else {
- strcat(signal_link, " activated");
- }
- }
- fig0_6_key_la[key] = LA;
- i += 2;
- if (Id_list_flag == 0) {
- if (cn == 0) { // Id_list_flag=0 && cn=0: CEI Change Event Indication
- strcat(signal_link, " CEI");
- }
- sprintf(desc, "Id list flag=%d, LA=%d %s, S/H=%d %s, ILS=%d %s, LSN=%d, database key=0x%04x%s",
- Id_list_flag, LA, (LA)?"active":"inactive", SH, (SH)?"Hard":"Soft", ILS, (ILS)?"international":"national", LSN, key, signal_link);
- printbuf(desc, indent+1, NULL, 0);
- }
- else { // Id_list_flag == 1
- if (i < figlen) {
- Number_of_Ids = (f[i] & 0x0F);
- if (pd == 0) {
- IdLQ = (f[i] >> 5) & 0x03;
- Shd = (f[i] >> 4) & 0x01;
- sprintf(desc, "Id list flag=%d, LA=%d %s, S/H=%d %s, ILS=%d %s, LSN=%d, database key=0x%04x, IdLQ=%d, Shd=%d %s, Number of Ids=%d%s",
- Id_list_flag, LA, (LA)?"active":"inactive", SH, (SH)?"Hard":"Soft", ILS, (ILS)?"international":"national", LSN, key, IdLQ, Shd, (Shd)?"b11-8 in 4-F are different services":"single service", Number_of_Ids, signal_link);
- printbuf(desc, indent+1, NULL, 0);
- if (ILS == 0) {
- // read Id list
- for(j = 0; ((j < Number_of_Ids) && ((i+2+(j*2)) < figlen)); j++) {
- // ETSI EN 300 401 8.1.15:
- // The IdLQ shall not apply to the first entry in the Id list when OE = "0" and
- // when the version number of the type 0 field is set to "0" (using the C/N flag, see clause 5.2.2.1)
- // ... , the first entry in the Id list of each Service linking field shall be
- // the SId that applies to the service in the ensemble.
- if (((j == 0) && (oe == 0) && (cn == 0)) ||
- (IdLQ == 0)) {
- sprintf(desc, "DAB SId 0x%X", ((f[i+1+(j*2)] << 8) | f[i+2+(j*2)]));
- }
- else if (IdLQ == 1) {
- sprintf(desc, "RDS PI 0x%X", ((f[i+1+(j*2)] << 8) | f[i+2+(j*2)]));
- }
- else if (IdLQ == 2) {
- sprintf(desc, "AM-FM service 0x%X", ((f[i+1+(j*2)] << 8) | f[i+2+(j*2)]));
- }
- else { // IdLQ == 3
- sprintf(desc, "invalid ILS IdLQ configuration");
- }
- printbuf(desc, indent+2, NULL, 0);
- }
- // check deadlink
- if ((Number_of_Ids == 0) && (IdLQ == 1)) {
- sprintf(desc, "deadlink");
- printbuf(desc, indent+2, NULL, 0);
- }
- i += (Number_of_Ids * 2) + 1;
- }
- else { // pd == 0 && ILS == 1
- // read Id list
- for(j = 0; ((j < Number_of_Ids) && ((i+3+(j*3)) < figlen)); j++) {
- // ETSI EN 300 401 8.1.15:
- // The IdLQ shall not apply to the first entry in the Id list when OE = "0" and
- // when the version number of the type 0 field is set to "0" (using the C/N flag, see clause 5.2.2.1)
- // ... , the first entry in the Id list of each Service linking field shall be
- // the SId that applies to the service in the ensemble.
- if (((j == 0) && (oe == 0) && (cn == 0)) ||
- (IdLQ == 0)) {
- sprintf(desc, "DAB SId ecc 0x%02X Id 0x%04X", f[i+1+(j*3)], ((f[i+2+(j*3)] << 8) | f[i+3+(j*3)]));
- }
- else if (IdLQ == 1) {
- sprintf(desc, "RDS PI ecc 0x%02X Id 0x%04X", f[i+1+(j*3)], ((f[i+2+(j*3)] << 8) | f[i+3+(j*3)]));
- }
- else if (IdLQ == 2) {
- sprintf(desc, "AM-FM service ecc 0x%02X Id 0x%04X", f[i+1+(j*3)], ((f[i+2+(j*3)] << 8) | f[i+3+(j*3)]));
- }
- else { // IdLQ == 3
- sprintf(desc, "DRM/AMSS service ecc 0x%02X Id 0x%04X", f[i+1+(j*3)], ((f[i+2+(j*3)] << 8) | f[i+3+(j*3)]));
- }
- printbuf(desc, indent+2, NULL, 0);
- }
- // check deadlink
- if ((Number_of_Ids == 0) && (IdLQ == 1)) {
- sprintf(desc, "deadlink");
- printbuf(desc, indent+2, NULL, 0);
- }
- i += (Number_of_Ids * 3) + 1;
- }
- }
- else { // pd == 1
- sprintf(desc, "Id list flag=%d, LA=%d %s, S/H=%d %s, ILS=%d %s, LSN=%d, database key=0x%04x, Number of Ids=%d%s",
- Id_list_flag, LA, (LA)?"active":"inactive", SH, (SH)?"Hard":"Soft", ILS, (ILS)?"international":"national", LSN, key, Number_of_Ids, signal_link);
- printbuf(desc, indent+1, NULL, 0);
- if (Number_of_Ids > 0) {
- // read Id list
- for(j = 0; ((j < Number_of_Ids) && ((i+4+(j*4)) < figlen)); j++) {
- sprintf(desc, "SId 0x%X",
- ((f[i+1+(j*4)] << 24) | (f[i+2+(j*4)] << 16) | (f[i+3+(j*4)] << 8) | f[i+4+(j*4)]));
- printbuf(desc, indent+2, NULL, 0);
- }
- }
- i += (Number_of_Ids * 4) + 1;
- }
- }
- }
- }
- }
- break;
- case 8: // FIG 0/8 Service component global definition
- { // ETSI EN 300 401 6.3.5
- uint32_t SId;
- uint16_t SCId;
- uint8_t i = 1, Rfa, SCIdS, SubChId, FIDCId;
- char tmpbuf[256];
- bool Ext_flag, LS_flag, MSC_FIC_flag;
-
- while (i < (figlen - (2 + (2 * pd)))) {
- // iterate over service component global definition
- if (pd == 0) {
- // Programme services, 16 bit SId
- SId = (f[i] << 8) | f[i+1];
- i += 2;
- }
- else {
- // Data services, 32 bit SId
- SId = ((uint32_t)f[i] << 24) | ((uint32_t)f[i+1] << 16) |
- ((uint32_t)f[i+2] << 8) | (uint32_t)f[i+3];
- i += 4;
- }
- Ext_flag = f[i] >> 7;
- Rfa = (f[i] >> 4) & 0x7;
- SCIdS = f[i] & 0x0F;
- sprintf(desc, "SId=0x%X, Ext flag=%d 8-bit Rfa %s", SId, Ext_flag, (Ext_flag)?"present":"absent");
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", SCIdS=0x%X", SCIdS);
- strcat(desc, tmpbuf);
- i++;
- if (i < figlen) {
- LS_flag = f[i] >> 7;
- sprintf(tmpbuf, ", L/S flag=%d %s", LS_flag, (LS_flag)?"Long form":"Short form");
- strcat(desc, tmpbuf);
- if (LS_flag == 0) {
- // Short form
- if (i < (figlen - Ext_flag)) {
- MSC_FIC_flag = (f[i] >> 6) & 0x01;
- if (MSC_FIC_flag == 0) {
- // MSC in stream mode and SubChId identifies the sub-channel
- SubChId = f[i] & 0x3F;
- sprintf(tmpbuf, ", MSC/FIC flag=%d MSC, SubChId=0x%X", MSC_FIC_flag, SubChId);
- strcat(desc, tmpbuf);
- }
- else {
- // FIC and FIDCId identifies the component
- FIDCId = f[i] & 0x3F;
- sprintf(tmpbuf, ", MSC/FIC flag=%d FIC, FIDCId=0x%X", MSC_FIC_flag, FIDCId);
- strcat(desc, tmpbuf);
- }
- if (Ext_flag == 1) {
- // Rfa field present
- Rfa = f[i+1];
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=0x%X invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- }
- }
- i += (1 + Ext_flag);
- }
- else {
- // Long form
- if (i < (figlen - 1)) {
- Rfa = (f[i] >> 4) & 0x07;
- SCId = (((uint16_t)f[i] & 0x0F) << 8) | (uint16_t)f[i+1];
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", SCId=0x%X", SCId);
- strcat(desc, tmpbuf);
- }
- i += 2;
- }
- }
- printbuf(desc, indent+1, NULL, 0);
- }
- }
- break;
- case 9: // FIG 0/9 Country, LTO and International table
- { // ETSI EN 300 401 8.1.3.2
- uint32_t SId;
- uint8_t i = 1, j, key, Number_of_services, ECC;
- int8_t LTO;
- char tmpbuf[256];
- bool Ext_flag;
-
- if (i < (figlen - 2)) {
- // get Ensemble LTO, ECC and International Table Id
- key = ((uint8_t)oe << 1) | (uint8_t)pd;
- Ext_flag = f[i] >> 7;
- LTO_uniq = (f[i]>> 6) & 0x01;
- Ensemble_LTO = f[i] & 0x3F;
- if (Ensemble_LTO & 0x20) {
- // negative Ensemble LTO
- Ensemble_LTO |= 0xC0;
- }
- sprintf(desc, "Ext flag=%d extended field %s, LTO uniq=%d %s, Ensemble LTO=0x%X %s%d:%02d",
- Ext_flag, Ext_flag?"present":"absent", LTO_uniq,
- LTO_uniq?"several time zones":"one time zone (time specified by Ensemble LTO)",
- (Ensemble_LTO & 0x3F), (Ensemble_LTO >= 0)?"":"-" , abs(Ensemble_LTO) >> 1, (Ensemble_LTO & 0x01) * 30);
- if (abs(Ensemble_LTO) > 24) {
- sprintf(tmpbuf, " out of range -12 hours to +12 hours");
- strcat(desc, tmpbuf);
- }
- Ensemble_ECC = f[i+1];
- International_Table_Id = f[i+2];
- sprintf(tmpbuf, ", Ensemble ECC=0x%X, International Table Id=0x%X, database key=0x%x",
- Ensemble_ECC, International_Table_Id, key);
- strcat(desc, tmpbuf);
- printbuf(desc, indent+1, NULL, 0);
- i += 3;
- if (Ext_flag == 1) {
- // extended field present
- while (i < figlen) {
- // iterate over extended sub-field
- Number_of_services = f[i] >> 6;
- LTO = f[i] & 0x3F;
- if (LTO & 0x20) {
- // negative LTO
- LTO |= 0xC0;
- }
- sprintf(desc, "Number of services=%d, LTO=0x%X %s%d:%02d",
- Number_of_services, (LTO & 0x3F), (LTO >= 0)?"":"-" , abs(LTO) >> 1, (LTO & 0x01) * 30);
- if (abs(LTO) > 24) {
- sprintf(tmpbuf, " out of range -12 hours to +12 hours");
- strcat(desc, tmpbuf);
- }
- // CEI Change Event Indication
- if ((Number_of_services == 0) && (LTO == 0) /* && (Ext_flag == 1) */) {
- sprintf(tmpbuf, ", CEI");
- strcat(desc, tmpbuf);
- }
- i++;
- if (pd == 0) {
- // Programme services, 16 bit SId
- if (i < figlen) {
- ECC = f[i];
- sprintf(tmpbuf, ", ECC=0x%X", ECC);
- strcat(desc, tmpbuf);
- printbuf(desc, indent+2, NULL, 0);
- i++;
- for(j = i; ((j < (i + (Number_of_services * 2))) && (j < figlen)); j += 2) {
- // iterate over SId
- SId = ((uint32_t)f[j] << 8) | (uint32_t)f[j+1];
- sprintf(desc, "SId 0x%X", SId);
- printbuf(desc, indent+3, NULL, 0);
- }
- i += (Number_of_services * 2);
- }
- }
- else {
- // Data services, 32 bit SId
- printbuf(desc, indent+2, NULL, 0);
- for(j = i; ((j < (i + (Number_of_services * 4))) && (j < figlen)); j += 4) {
- // iterate over SId
- SId = ((uint32_t)f[j] << 24) | ((uint32_t)f[j+1] << 16) |
- ((uint32_t)f[j+2] << 8) | (uint32_t)f[j+3];
- sprintf(desc, "SId 0x%X", SId);
- printbuf(desc, indent+3, NULL, 0);
- }
- i += (Number_of_services * 4);
- }
- }
- }
- }
- }
- break;
- case 10: // FIG 0/10 Date and time
- { // ETSI EN 300 401 8.1.3.1
- char dateStr[256];
- dateStr[0] = 0;
-
- //bool RFU = f[1] >> 7;
-
- uint32_t MJD = (((uint32_t)f[1] & 0x7F) << 10) |
- ((uint32_t)(f[2]) << 2) |
- (f[3] >> 6);
- sprintfMJD(dateStr, MJD);
-
- bool LSI = f[3] & 0x20;
- bool ConfInd = f[3] & 0x10;
- wm_decoder.push_confind_bit(ConfInd);
- bool UTC = f[3] & 0x8;
-
- uint8_t hours = ((f[3] & 0x7) << 2) |
- ( f[4] >> 6);
+ figs.push_back(figtype, fig0.ext(), figlen);
- uint8_t minutes = f[4] & 0x3f;
-
- if (UTC) {
- uint8_t seconds = f[5] >> 2;
- uint16_t milliseconds = ((uint16_t)(f[5] & 0x2) << 8) | f[6];
-
- sprintf(desc, "FIG %d/%d(long): MJD=0x%X %s, LSI %u, ConfInd %u, UTC Time: %02d:%02d:%02d.%d",
- figtype, ext, MJD, dateStr, LSI, ConfInd, hours, minutes, seconds, milliseconds);
- printbuf(desc, indent+1, NULL, 0);
- }
- else {
- sprintf(desc, "FIG %d/%d(short): MJD=0x%X %s, LSI %u, ConfInd %u, UTC Time: %02d:%02d",
- figtype, ext, MJD, dateStr, LSI, ConfInd, hours, minutes);
- printbuf(desc, indent+1, NULL, 0);
- }
- }
- break;
- case 11: // FIG 0/11 Region definition
- { // ETSI EN 300 401 8.1.16.1
- Lat_Lng gps_pos = {0, 0};
- int16_t Latitude_coarse, Longitude_coarse;
- uint16_t Region_Id, Extent_Latitude, Extent_Longitude, key;
- uint8_t i = 1, j, k, GATy, Rfu, Length_TII_list, Rfa, MainId, Length_SubId_list, SubId;
- int8_t bit_pos;
- char tmpbuf[256];
- bool GE_flag;
-
- while (i < (figlen - 1)) {
- // iterate over Region definition
- GATy = f[i] >> 4;
- GE_flag = (f[i] >> 3) & 0x01;
- Region_Id = ((uint16_t)(f[i] & 0x07) << 8) | ((uint16_t)f[i+1]);
- key = ((uint16_t)oe << 12) | ((uint16_t)pd << 11) | Region_Id;
- i += 2;
- if (GATy == 0) {
- // TII list
- sprintf(desc, "GATy=%d Geographical area defined by a TII list, G/E flag=%d %s coverage area, RegionId=0x%X, database key=0x%X",
- GATy, GE_flag, GE_flag?"Global":"Ensemble", Region_Id, key);
- if (i < figlen) {
- Rfu = f[i] >> 5;
- if (Rfu != 0) {
- sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
- strcat(desc, tmpbuf);
- }
- Length_TII_list = f[i] & 0x1F;
- sprintf(tmpbuf, ", Length of TII list=%d", Length_TII_list);
- strcat(desc, tmpbuf);
- if (Length_TII_list == 0) {
- strcat(desc, ", CEI");
- }
- printbuf(desc, indent+1, NULL, 0);
- i++;
-
- for(j = 0;(i < (figlen - 1)) && (j < Length_TII_list); j++) {
- // iterate over Transmitter group
- Rfa = f[i] >> 7;
- MainId = f[i] & 0x7F;
- if (Rfa != 0) {
- sprintf(desc, "Rfa=%d invalid value, MainId=0x%X",
- Rfa, MainId);
- }
- else {
- sprintf(desc, "MainId=0x%X", MainId);
- }
- // check MainId value
- if ((Mode_Identity == 1) || (Mode_Identity == 2) || (Mode_Identity == 4)) {
- if (MainId > 69) {
- // The coding range shall be 0 to 69 for transmission modes I, II and IV
- sprintf(tmpbuf, " invalid value for transmission mode %d", Mode_Identity);
- strcat(desc, tmpbuf);
- }
- }
- else if (Mode_Identity == 3) {
- if (MainId > 5) {
- // The coding range shall be 0 to 5 for transmission modes I, II and IV
- sprintf(tmpbuf, " invalid value for transmission mode %d", Mode_Identity);
- strcat(desc, tmpbuf);
- }
- }
- Rfa = f[i+1] >> 5;
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- Length_SubId_list = f[i+1] & 0x1F;
- sprintf(tmpbuf, ", Length of SubId=%d", Length_SubId_list);
- strcat(desc, tmpbuf);
- printbuf(desc, indent+2, NULL, 0);
- i += 2;
-
- bit_pos = 3;
- SubId = 0;
- for(k = 0;(i < figlen) && (k < Length_SubId_list); k++) {
- // iterate SubId
- if (bit_pos >= 0) {
- SubId |= (f[i] >> bit_pos) & 0x1F;
- sprintf(desc, "SubId=0x%X", SubId);
- // check SubId value
- if ((SubId == 0) || (SubId > 23)) {
- strcat(desc, " invalid value");
- }
- printbuf(desc, indent+3, NULL, 0);
- bit_pos -= 5;
- SubId = 0;
- }
- if (bit_pos < 0) {
- SubId = (f[i] << abs(bit_pos)) & 0x1F;
- bit_pos += 8;
- i++;
- }
- }
- if (bit_pos > 3) {
- // jump padding
- i++;
- }
- if (k < Length_SubId_list) {
- sprintf(desc, "%d SubId missing, fig length too short !", (Length_SubId_list - k));
- printbuf(desc, indent+3, NULL, 0);
- fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, ext, figlen);
- }
- }
- if (j < Length_TII_list) {
- sprintf(desc, "%d Transmitter group missing, fig length too short !", (Length_TII_list - j));
- printbuf(desc, indent+2, NULL, 0);
- fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, ext, figlen);
- }
- }
- }
- else if (GATy == 1) {
- // Coordinates
- sprintf(desc, "GATy=%d Geographical area defined as a spherical rectangle by the geographical co-ordinates of one corner and its latitude and longitude extents, G/E flag=%d %s coverage area, RegionId=0x%X, database key=0x%X",
- GATy, GE_flag, GE_flag?"Global":"Ensemble", Region_Id, key);
- if (i < (figlen - 6)) {
- Latitude_coarse = ((int16_t)f[i] << 8) | ((uint16_t)f[i+1]);
- Longitude_coarse = ((int16_t)f[i+2] << 8) | ((uint16_t)f[i+3]);
- gps_pos.latitude = ((double)Latitude_coarse) * 90 / 32768;
- gps_pos.longitude = ((double)Latitude_coarse) * 180 / 32768;
- sprintf(tmpbuf, ", Lat Lng coarse=0x%X 0x%X => %f, %f",
- Latitude_coarse, Longitude_coarse, gps_pos.latitude, gps_pos.longitude);
- strcat(desc, tmpbuf);
- Extent_Latitude = ((uint16_t)f[i+4] << 4) | ((uint16_t)(f[i+5] >> 4));
- Extent_Longitude = ((uint16_t)(f[i+5] & 0x0F) << 8) | ((uint16_t)f[i+6]);
- gps_pos.latitude += ((double)Extent_Latitude) * 90 / 32768;
- gps_pos.longitude += ((double)Extent_Longitude) * 180 / 32768;
- sprintf(tmpbuf, ", Extent Lat Lng=0x%X 0x%X => %f, %f",
- Extent_Latitude, Extent_Longitude, gps_pos.latitude, gps_pos.longitude);
- strcat(desc, tmpbuf);
- }
- else {
- sprintf(tmpbuf, ", Coordinates missing, fig length too short !");
- strcat(desc, tmpbuf);
- fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, ext, figlen);
- }
- printbuf(desc, indent+1, NULL, 0);
- i += 7;
- }
- else {
- // Rfu
- sprintf(desc, "GATy=%d reserved for future use of the geographical, G/E flag=%d %s coverage area, RegionId=0x%X, database key=0x%X, stop Region definition iteration %d/%d",
- GATy, GE_flag, GE_flag?"Global":"Ensemble", Region_Id, key, i, figlen);
- printbuf(desc, indent+1, NULL, 0);
- // stop Region definition iteration
- i = figlen;
- }
- }
- }
- break;
- case 13: // FIG 0/13 User application information
- { // ETSI EN 300 401 8.1.20
- uint32_t SId;
- uint8_t SCIdS;
- uint8_t No;
-
- int k = 1;
-
- if (pd == 0) { // Programme services, 16 bit SId
- SId = (f[k] << 8) |
- f[k+1];
- k+=2;
-
- SCIdS = f[k] >> 4;
- No = f[k] & 0x0F;
- k++;
- }
- else { // Data services, 32 bit SId
- SId = (f[k] << 24) |
- (f[k+1] << 16) |
- (f[k+2] << 8) |
- f[k+3];
- k+=4;
-
- SCIdS = f[k] >> 4;
- No = f[k] & 0x0F;
- k++;
-
- }
-
- sprintf(desc, "FIG %d/%d: SId=0x%X SCIdS=%u No=%u",
- figtype, ext, SId, SCIdS, No);
- printbuf(desc, indent+1, NULL, 0);
-
- for (int numapp = 0; numapp < No; numapp++) {
- uint16_t user_app_type = ((f[k] << 8) |
- (f[k+1] & 0xE0)) >> 5;
- uint8_t user_app_len = f[k+1] & 0x1F;
- k+=2;
-
- sprintf(desc, "User Application %d '%s'; length %u",
- user_app_type,
- get_fig_0_13_userapp(user_app_type).c_str(),
- user_app_len);
- printbuf(desc, indent+2, NULL, 0);
- }
- }
- break;
- case 14: // FIG 0/14 FEC sub-channel organization
- { // ETSI EN 300 401 6.2.2
- uint8_t i = 1, SubChId, FEC_scheme;
-
- while (i < figlen) {
- // iterate over Sub-channel
- SubChId = f[i] >> 2;
- FEC_scheme = f[i] & 0x3;
- sprintf(desc, "SubChId=0x%X, FEC scheme=%d %s",
- SubChId, FEC_scheme, FEC_schemes_str[FEC_scheme]);
- printbuf(desc, indent+1, NULL, 0);
- i++;
- }
- }
- break;
- case 16: // FIG 0/16 Programme Number & OE Programme Number
- { // ETSI EN 300 401 8.1.4 & 8.1.10.3
- uint16_t SId, PNum, New_SId, New_PNum;
- uint8_t i = 1, Rfa, Rfu;
- char tmpbuf[256];
- bool Continuation_flag, Update_flag;
-
- while (i < (figlen - 4)) {
- // iterate over Programme Number
- SId = ((uint16_t)f[i] << 8) | ((uint16_t)f[i+1]);
- PNum = ((uint16_t)f[i+2] << 8) | ((uint16_t)f[i+3]);
- Rfa = f[i+4] >> 6;
- Rfu = (f[i+4] >> 2) & 0x0F;
- Continuation_flag = (f[i+4] >> 1) & 0x01;
- Update_flag = f[i+4] & 0x01;
-
- sprintf(desc, "SId=0x%X, PNum=0x%X ", SId, PNum);
- // Append PNum decoded string
- strcatPNum(desc, PNum);
-
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
-
- if (Rfu != 0) {
- sprintf(tmpbuf, ", Rfu=0x%X invalid value", Rfu);
- strcat(desc, tmpbuf);
- }
-
- sprintf(tmpbuf, ", Continuation flag=%d the programme will %s, Update flag=%d %sre-direction",
- Continuation_flag, Continuation_flag?"be interrupted but continued later":"not be subject to a planned interruption",
- Update_flag, Update_flag?"":"no ");
- strcat(desc, tmpbuf);
- i += 5;
-
- if (Update_flag != 0) {
- // In the case of a re-direction, the New SId and New PNum shall be appended
- if (i < (figlen - 1)) {
- New_SId = ((uint16_t)f[i] << 8) | ((uint16_t)f[i+1]);
- sprintf(tmpbuf, ", New SId=0x%X", New_SId);
- strcat(desc, tmpbuf);
- if (i < (figlen - 3)) {
- New_PNum = ((uint16_t)f[i+2] << 8) | ((uint16_t)f[i+3]);
- sprintf(tmpbuf, ", New PNum=0x%X ", New_PNum);
- strcat(desc, tmpbuf);
- // Append New_PNum decoded string
- strcatPNum(desc, New_PNum);
- }
- else {
- sprintf(tmpbuf, ", missing New PNum !");
- strcat(desc, tmpbuf);
- }
- }
- else {
- sprintf(tmpbuf, ", missing New SId and New PNum !");
- strcat(desc, tmpbuf);
- }
- i += 4;
- }
-
- printbuf(desc, indent+1, NULL, 0);
- }
- }
- break;
- case 17: // FIG 0/17 Programme Type
- { // ETSI EN 300 401 8.1.5
- uint16_t SId;
- uint8_t i = 1, Rfa, Language, Int_code, Comp_code;
- char tmpbuf[256];
- bool SD_flag, PS_flag, L_flag, CC_flag, Rfu;
- while (i < (figlen - 3)) {
- // iterate over announcement support
- SId = (f[i] << 8) | f[i+1];
- SD_flag = (f[i+2] >> 7);
- PS_flag = ((f[i+2] >> 6) & 0x01);
- L_flag = ((f[i+2] >> 5) & 0x01);
- CC_flag = ((f[i+2] >> 4) & 0x01);
- Rfa = (f[i+2] & 0x0F);
- sprintf(desc, "SId=0x%X, S/D=%d Programme Type codes and language (when present), %srepresent the current programme contents, P/S=%d %s service component, L flag=%d language field %s, CC flag=%d complementary code and preceding Rfa and Rfu fields %s",
- SId, SD_flag, SD_flag?"":"may not ", PS_flag, PS_flag?"secondary":"primary",
- L_flag, L_flag?"present":"absent", CC_flag, CC_flag?"present":"absent");
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=0x%X invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- i += 3;
- if (L_flag != 0) {
- if (i < figlen) {
- Language = f[i];
- sprintf(tmpbuf, ", Language=0x%X %s", Language,
- fig0_5.Language_to_char(Language));
- strcat(desc, tmpbuf);
- }
- else {
- sprintf(tmpbuf, ", Language= invalid FIG length");
- strcat(desc, tmpbuf);
- }
- i++;
- }
- if (i < figlen) {
- Rfa = f[i] >> 6;
- Rfu = (f[i] >> 5) & 0x01;
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=0x%X invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- if (Rfu != 0) {
- sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
- strcat(desc, tmpbuf);
- }
- Int_code = f[i] & 0x1F;
- sprintf(tmpbuf, ", Int code=0x%X %s", Int_code, get_programme_type_str(International_Table_Id , Int_code));
- strcat(desc, tmpbuf);
- i++;
- }
- else {
- sprintf(tmpbuf, ", Int code= invalid FIG length");
- strcat(desc, tmpbuf);
- }
- if (CC_flag != 0) {
- if (i < figlen) {
- Rfa = f[i] >> 6;
- Rfu = (f[i] >> 5) & 0x01;
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=0x%X invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- if (Rfu != 0) {
- sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
- strcat(desc, tmpbuf);
- }
- Comp_code = f[i] & 0x1F;
- sprintf(tmpbuf, ", Comp code=0x%X %s", Comp_code, get_programme_type_str(International_Table_Id , Comp_code));
- strcat(desc, tmpbuf);
- i++;
- }
- else {
- sprintf(tmpbuf, ", Comp code= invalid FIG length");
- strcat(desc, tmpbuf);
- }
- }
- printbuf(desc, indent+1, NULL, 0);
- }
- }
- break;
- case 18: // FIG 0/18 Announcement support
- { // ETSI EN 300 401 8.1.6.1
- uint32_t key;
- uint16_t SId, Asu_flags;
- uint8_t i = 1, j, Rfa, Number_clusters;
- char tmpbuf[256];
-
- while (i < (figlen - 4)) {
- // iterate over announcement support
- // SId, Asu flags, Rfa, Number of clusters
- SId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
- Asu_flags = ((uint16_t)f[i+2] << 8) | (uint16_t)f[i+3];
- Rfa = (f[i+4] >> 5);
- Number_clusters = (f[i+4] & 0x1F);
- sprintf(desc, "SId=0x%X, Asu flags=0x%04x", SId, Asu_flags);
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", Number of clusters=%d", Number_clusters);
- strcat(desc, tmpbuf);
- key = ((uint32_t)oe << 17) | ((uint32_t)pd << 16) | (uint32_t)SId;
- sprintf(tmpbuf, ", database key=0x%05x", key);
- strcat(desc, tmpbuf);
- // CEI Change Event Indication
- if ((Number_clusters == 0) && (Asu_flags == 0)) {
- sprintf(tmpbuf, ", CEI");
- strcat(desc, tmpbuf);
- }
- printbuf(desc, indent+1, NULL, 0);
- i += 5;
-
- for(j = 0; (j < Number_clusters) && (i < figlen); j++) {
- // iterate over Cluster Id
- sprintf(desc, "Cluster Id=0x%X", f[i]);
- printbuf(desc, indent+2, NULL, 0);
- i++;
- }
- if (j < Number_clusters) {
- sprintf(desc, "missing Cluster Id, fig length too short !");
- printbuf(desc, indent+1, NULL, 0);
- fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, ext, figlen);
- }
-
- // decode announcement support types
- for(j = 0; j < 16; j++) {
- if (Asu_flags & (1 << j)) {
- sprintf(desc, "Announcement support=%s", announcement_types_str[j]);
- printbuf(desc, indent+2, NULL, 0);
- }
- }
- }
- }
- break;
- case 19: // FIG 0/19 Announcement switching
- { // ETSI EN 300 401 8.1.6.2
- uint16_t Asw_flags;
- uint8_t i = 1, j, Cluster_Id, SubChId, Rfa, RegionId_LP;
- char tmpbuf[256];
- bool New_flag, Region_flag;
-
- while (i < (figlen - 3)) {
- // iterate over announcement switching
- // Cluster Id, Asw flags, New flag, Region flag,
- // SubChId, Rfa, Region Id Lower Part
- Cluster_Id = f[i];
- Asw_flags = ((uint16_t)f[i+1] << 8) | (uint16_t)f[i+2];
- New_flag = (f[i+3] >> 7);
- Region_flag = (f[i+3] >> 6) & 0x1;
- SubChId = (f[i+3] & 0x3F);
- sprintf(desc, "Cluster Id=0x%02x, Asw flags=0x%04x, New flag=%d %s, Region flag=%d last byte %s, SubChId=%d",
- Cluster_Id, Asw_flags, New_flag, (New_flag)?"new":"repeat", Region_flag, (Region_flag)?"present":"absent", SubChId);
- if (Region_flag) {
- if (i < (figlen - 4)) {
- // read region lower part
- Rfa = (f[i+4] >> 6);
- RegionId_LP = (f[i+4] & 0x3F);
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", Region Lower Part=0x%02x", RegionId_LP);
- strcat(desc, tmpbuf);
- }
- else {
- sprintf(tmpbuf, "missing Region Lower Part, fig length too short !");
- strcat(desc, tmpbuf);
- fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, ext, figlen);
- }
- }
- printbuf(desc, indent+1, NULL, 0);
- // decode announcement switching types
- for(j = 0; j < 16; j++) {
- if (Asw_flags & (1 << j)) {
- sprintf(desc, "Announcement switching=%s", announcement_types_str[j]);
- printbuf(desc, indent+2, NULL, 0);
- }
- }
- i += (4 + Region_flag);
- }
- }
- break;
- case 21: // FIG 0/21 Frequency Information
- { // ETSI EN 300 401 8.1.8
- float freq;
- uint32_t ifreq;
- uint64_t key;
- uint16_t RegionId, Id_field;
- uint8_t i = 1, j, k, Length_FI_list, RandM, Length_Freq_list, Control_field;
- uint8_t Control_field_trans_mode, Id_field2;
- char tmpbuf[256];
- bool Continuity_flag;
-
- while (i < (figlen - 1)) {
- // iterate over frequency information
- // decode RegionId, Length of FI list
- RegionId = (f[i] << 3) | (f[i+1] >> 5);
- Length_FI_list = (f[i+1] & 0x1F);
- sprintf(desc, "RegionId=0x%03x", RegionId);
- printbuf(desc, indent+1, NULL, 0);
- i += 2;
- if ((i + Length_FI_list) <= figlen) {
- j = i;
- while ((j + 2) < (i + Length_FI_list)) {
- // iterate over FI list x
- // decode Id field, R&M, Continuity flag, Length of Freq list
- Id_field = (f[j] << 8) | f[j+1];
- RandM = f[j+2] >> 4;
- Continuity_flag = (f[j+2] >> 3) & 0x01;
- Length_Freq_list = f[j+2] & 0x07;
- sprintf(desc, "Id_field=");
- switch (RandM) {
- case 0x0:
- case 0x1:
- strcat(desc, "EId");
- break;
- case 0x6:
- strcat(desc, "DRM Service Id");
- break;
- case 0x8:
- strcat(desc, "RDS PI");
- break;
- case 0x9:
- case 0xa:
- case 0xc:
- strcat(desc, "Dummy");
- break;
- case 0xe:
- strcat(desc, "AMSS Service Id");
- break;
- default:
- strcat(desc, "invalid");
- break;
- }
- sprintf(tmpbuf, "=0x%X, R&M=0x%1x", Id_field, RandM);
- strcat(desc, tmpbuf);
- switch (RandM) {
- case 0x0:
- strcat(desc, "=DAB ensemble, no local windows");
- break;
- case 0x6:
- strcat(desc, "=DRM");
- break;
- case 0x8:
- strcat(desc, "=FM with RDS");
- break;
- case 0x9:
- strcat(desc, "=FM without RDS");
- break;
- case 0xa:
- strcat(desc, "=AM (MW in 9 kHz steps & LW)");
- break;
- case 0xc:
- strcat(desc, "=AM (MW in 5 kHz steps & SW)");
- break;
- case 0xe:
- strcat(desc, "=AMSS");
- break;
- default:
- strcat(desc, "=Rfu");
- break;
- }
- sprintf(tmpbuf, ", Continuity flag=%d", Continuity_flag);
- strcat(desc, tmpbuf);
- if ((oe == 0) || ((oe == 1) && (RandM != 0x6) && \
- ((RandM < 0x8) || (RandM > 0xa)) && (RandM != 0xc) && (RandM != 0xe))) {
- if (Continuity_flag == 0) {
- switch (RandM) {
- case 0x0:
- case 0x1:
- strcat(desc, "=continuous output not expected");
- break;
- case 0x6:
- strcat(desc, "=no compensating time delay on DRM audio signal");
- break;
- case 0x8:
- case 0x9:
- strcat(desc, "=no compensating time delay on FM audio signal");
- break;
- case 0xa:
- case 0xc:
- case 0xe:
- strcat(desc, "=no compensating time delay on AM audio signal");
- break;
- default:
- strcat(desc, "=Rfu");
- break;
- }
- }
- else { // Continuity_flag == 1
- switch (RandM) {
- case 0x0:
- case 0x1:
- strcat(desc, "=continuous output possible");
- break;
- case 0x6:
- strcat(desc, "=compensating time delay on DRM audio signal");
- break;
- case 0x8:
- case 0x9:
- strcat(desc, "=compensating time delay on FM audio signal");
- break;
- case 0xa:
- case 0xc:
- case 0xe:
- strcat(desc, "=compensating time delay on AM audio signal");
- break;
- default:
- strcat(desc, "=Rfu");
- break;
- }
- }
- }
- else { // oe == 1
- strcat(desc, "=reserved for future addition");
- }
- key = ((uint64_t)oe << 32) | ((uint64_t)pd << 31) | \
- ((uint64_t)RegionId << 20) | ((uint64_t)Id_field << 4) | \
- (uint64_t)RandM;
- sprintf(tmpbuf, ", database key=0x%09" PRId64, key);
- // CEI Change Event Indication
- if (Length_Freq_list == 0) {
- strcat(tmpbuf, ", CEI");
- }
- strcat(desc, tmpbuf);
- printbuf(desc, indent+2, NULL, 0);
- j += 3; // add header
-
- k = j;
- switch (RandM) {
- case 0x0:
- case 0x1:
- while((k + 2) < (j + Length_Freq_list)) {
- // iteration over Freq list
- ifreq = (((uint32_t)(f[k] & 0x07) << 16) | ((uint32_t)f[k+1] << 8) | (uint32_t)f[k+2]) * 16;
- if (ifreq != 0) {
- Control_field = (f[k] >> 3);
- Control_field_trans_mode = (Control_field >> 1) & 0x07;
- if ((Control_field & 0x10) == 0) {
- sprintf(desc, "%d KHz, ", ifreq);
- if ((Control_field & 0x01) == 0) {
- strcat(desc, "geographically adjacent area, ");
- }
- else { // (Control_field & 0x01) == 1
- strcat(desc, "no geographically adjacent area, ");
- }
- if (Control_field_trans_mode == 0) {
- strcat(desc, "no transmission mode signalled");
- }
- else if (Control_field_trans_mode <= 4) {
- sprintf(tmpbuf, "transmission mode %d", Control_field_trans_mode);
- strcat(desc, tmpbuf);
- }
- else { // Control_field_trans_mode > 4
- sprintf(tmpbuf, "invalid transmission mode 0x%x", Control_field_trans_mode);
- strcat(desc, tmpbuf);
- }
- }
- else { // (Control_field & 0x10) == 0x10
- sprintf(desc, "%d KHz, invalid Control field b23 0x%x", ifreq, Control_field);
- }
- }
- else {
- sprintf(desc, "Frequency not to be used (0)");
- }
- printbuf(desc, indent+3, NULL, 0);
- k += 3;
- }
- break;
- case 0x8:
- case 0x9:
- case 0xa:
- while(k < (j + Length_Freq_list)) {
- // iteration over Freq list
- if (f[k] != 0) { // freq != 0
- if (RandM == 0xa) {
- if (f[k] < 16) {
- ifreq = (144 + ((uint32_t)f[k] * 9));
- }
- else { // f[k] >= 16
- ifreq = (387 + ((uint32_t)f[k] * 9));
- }
- sprintf(desc, "%d KHz", ifreq);
- }
- else { // RandM == 8 or 9
- freq = (87.5 + ((float)f[k] * 0.1));
- sprintf(desc, "%.1f MHz", freq);
- }
- }
- else {
- sprintf(desc, "Frequency not to be used (0)");
- }
- printbuf(desc, indent+3, NULL, 0);
- k++;
- }
- break;
- case 0xc:
- while((k + 1) < (j + Length_Freq_list)) {
- // iteration over Freq list
- ifreq = (((uint32_t)f[k] << 8) | (uint32_t)f[k+1]) * 5;
- if (ifreq != 0) {
- sprintf(desc, "%d KHz", ifreq);
- }
- else {
- sprintf(desc, "Frequency not to be used (0)");
- }
- printbuf(desc, indent+3, NULL, 0);
- k += 2;
- }
- break;
- case 0x6:
- case 0xe:
- while((k + 2) < (j + Length_Freq_list)) {
- // iteration over Freq list
- Id_field2 = f[k];
- ifreq = ((((uint32_t)f[k+1] & 0x7f) << 8) | (uint32_t)f[k+2]);
- if (ifreq != 0) {
- sprintf(desc, "%d KHz", ifreq);
- }
- else {
- sprintf(desc, "Frequency not to be used (0)");
- }
- if (RandM == 0x6) {
- sprintf(tmpbuf, ", DRM Service Id 0x%X", Id_field2);
- strcat(desc, tmpbuf);
- }
- else if (RandM == 0xe) {
- sprintf(tmpbuf, ", AMSS Service Id 0x%X", Id_field2);
- strcat(desc, tmpbuf);
- }
- if ((f[k+1] & 0x80) == 0x80) {
- sprintf(tmpbuf, ", invalid Rfu b15 set to 1 instead of 0");
- strcat(desc, tmpbuf);
- }
- printbuf(desc, indent+3, NULL, 0);
- k += 3;
- }
- break;
- default:
- break;
- }
- j += Length_Freq_list;
- }
- i += Length_FI_list;
- }
- }
- }
- break;
- case 22: // FIG 0/22 Transmitter Identification Information (TII) database
- { // ETSI EN 300 401 8.1.9
- Lat_Lng gps_pos = {0, 0};
- double latitude_sub, longitude_sub;
- int16_t Latitude_coarse, Longitude_coarse;
- uint16_t key, TD;
- int16_t Latitude_offset, Longitude_offset;
- uint8_t i = 1, j, MainId = 0, Rfu, Nb_SubId_fields, SubId;
- uint8_t Latitude_fine, Longitude_fine;
- char tmpbuf[256];
- bool MS;
-
- while (i < figlen) {
- // iterate over Transmitter Identification Information (TII) fields
- MS = f[i] >> 7;
- MainId = f[i] & 0x7F;
- key = (oe << 8) | (pd << 7) | MainId;
- sprintf(desc, "M/S=%d %sidentifier, MainId=0x%X",
- MS, MS?"Sub-":"Main ", MainId);
- // check MainId value
- if ((Mode_Identity == 1) || (Mode_Identity == 2) || (Mode_Identity == 4)) {
- if (MainId > 69) {
- // The coding range shall be 0 to 69 for transmission modes I, II and IV
- sprintf(tmpbuf, " invalid value for transmission mode %d", Mode_Identity);
- strcat(desc, tmpbuf);
- }
- }
- else if (Mode_Identity == 3) {
- if (MainId > 5) {
- // The coding range shall be 0 to 5 for transmission modes I, II and IV
- sprintf(tmpbuf, " invalid value for transmission mode %d", Mode_Identity);
- strcat(desc, tmpbuf);
- }
- }
- // print database key
- sprintf(tmpbuf, ", database key=0x%X", key);
- strcat(desc, tmpbuf);
- i++;
- if (MS == 0) {
- // Main identifier
-
- if (i < (figlen - 4)) {
- Latitude_coarse = (f[i] << 8) | f[i+1];
- Longitude_coarse = (f[i+2] << 8) | f[i+3];
- Latitude_fine = f[i+4] >> 4;
- Longitude_fine = f[i+4] & 0x0F;
- gps_pos.latitude = (double)((int32_t)((((int32_t)Latitude_coarse) << 4) | (uint32_t)Latitude_fine)) * 90 / 524288;
- gps_pos.longitude = (double)((int32_t)((((int32_t)Longitude_coarse) << 4) | (uint32_t)Longitude_fine)) * 180 / 524288;
- fig0_22_key_Lat_Lng[key] = gps_pos;
- sprintf(tmpbuf, ", Lat Lng coarse=0x%X 0x%X, Lat Lng fine=0x%X 0x%X => Lat Lng=%f, %f",
- Latitude_coarse, Longitude_coarse, Latitude_fine, Longitude_fine,
- gps_pos.latitude, gps_pos.longitude);
- strcat(desc, tmpbuf);
- i += 5;
- }
- else {
- strcat(desc, ", invalid length of Latitude Longitude coarse fine");
- }
- printbuf(desc, indent+1, NULL, 0);
- }
- else { // MS == 1
- // Sub-identifier
-
- if (i < figlen) {
- Rfu = f[i] >> 3;
- Nb_SubId_fields = f[i] & 0x07;
- if (Rfu != 0) {
- sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", Number of SubId fields=%d%s",
- Nb_SubId_fields, (Nb_SubId_fields == 0)?", CEI":"");
- strcat(desc, tmpbuf);
- printbuf(desc, indent+1, NULL, 0);
- i++;
-
- for(j = i; ((j < (i + (Nb_SubId_fields * 6))) && (j < (figlen - 5))); j += 6) {
- // iterate over SubId fields
- SubId = f[j] >> 3;
- sprintf(desc, "SubId=0x%X", SubId);
- // check SubId value
- if ((SubId == 0) || (SubId > 23)) {
- strcat(desc, " invalid value");
- }
-
- TD = ((f[j] & 0x03) << 8) | f[j+1];
- Latitude_offset = (f[j+2] << 8) | f[j+3];
- Longitude_offset = (f[j+4] << 8) | f[j+5];
- sprintf(tmpbuf, ", TD=%d us, Lat Lng offset=0x%X 0x%X",
- TD, Latitude_offset, Longitude_offset);
- strcat(desc, tmpbuf);
-
- if (fig0_22_key_Lat_Lng.count(key) > 0) {
- // latitude longitude available in database for Main Identifier
- latitude_sub = (90 * (double)Latitude_offset / 524288) + fig0_22_key_Lat_Lng[key].latitude;
- longitude_sub = (180 * (double)Longitude_offset / 524288) + fig0_22_key_Lat_Lng[key].longitude;
- sprintf(tmpbuf, " => Lat Lng=%f, %f", latitude_sub, longitude_sub);
- strcat(desc, tmpbuf);
- }
- else {
- // latitude longitude not available in database for Main Identifier
- latitude_sub = 90 * (double)Latitude_offset / 524288;
- longitude_sub = 180 * (double)Longitude_offset / 524288;
- sprintf(tmpbuf, " => Lat Lng=%f, %f wrong value because Main identifier latitude/longitude not available in database", latitude_sub, longitude_sub);
- strcat(desc, tmpbuf);
- }
- printbuf(desc, indent+2, NULL, 0);
- }
- i += (Nb_SubId_fields * 6);
- }
- else {
- strcat(desc, ", invalid fig length or Number of SubId fields length");
- printbuf(desc, indent+1, NULL, 0);
- }
- }
- }
- }
- break;
- case 24: // FIG 0/24 OE Services
- { // ETSI EN 300 401 8.1.10.2
- uint64_t key;
- uint32_t SId;
- uint16_t EId;
- uint8_t i = 1, j, Number_of_EIds, CAId;
- char tmpbuf[256];
- bool Rfa;
-
- while (i < (figlen - (((uint8_t)pd + 1) * 2))) {
- // iterate over other ensembles services
- if (pd == 0) {
- SId = ((uint32_t)f[i] << 8) | (uint32_t)f[i+1];
- i += 2;
- }
- else { // pd == 1
- SId = ((uint32_t)f[i] << 24) | ((uint32_t)f[i+1] << 16) |
- ((uint32_t)f[i+2] << 8) | (uint32_t)f[i+3];
- i += 4;
- }
- Rfa = (f[i] >> 7);
- CAId = (f[i] >> 4);
- Number_of_EIds = (f[i] & 0x0f);
- key = ((uint64_t)oe << 33) | ((uint64_t)pd << 32) | \
- (uint64_t)SId;
- if (pd == 0) {
- sprintf(desc, "SId=0x%X, CAId=%d, Number of EId=%d, database key=%09" PRId64, SId, CAId, Number_of_EIds, key);
- }
- else { // pd == 1
- sprintf(desc, "SId=0x%X, CAId=%d, Number of EId=%d, database key=%09" PRId64, SId, CAId, Number_of_EIds, key);
- }
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- // CEI Change Event Indication
- if (Number_of_EIds == 0) {
- sprintf(tmpbuf, ", CEI");
- strcat(desc, tmpbuf);
- }
- printbuf(desc, indent+1, NULL, 0);
- i++;
-
- for(j = i; ((j < (i + (Number_of_EIds * 2))) && (j < figlen)); j += 2) {
- // iterate over EIds
- EId = ((uint16_t)f[j] <<8) | (uint16_t)f[j+1];
- sprintf(desc, "EId 0x%04x", EId);
- printbuf(desc, indent+2, NULL, 0);
- }
- i += (Number_of_EIds * 2);
- }
- }
- break;
- case 25: // FIG 0/25 OE Announcement support
- { // ETSI EN 300 401 8.1.10.5.1
- uint32_t key;
- uint16_t SId, Asu_flags, EId;
- uint8_t i = 1, j, Rfu, Number_EIds;
- char tmpbuf[256];
-
- while (i < (figlen - 4)) {
- // iterate over other ensembles announcement support
- // SId, Asu flags, Rfu, Number of EIds
- SId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
- Asu_flags = ((uint16_t)f[i+2] << 8) | (uint16_t)f[i+3];
- Rfu = (f[i+4] >> 4);
- Number_EIds = (f[i+4] & 0x0F);
- sprintf(desc, "SId=0x%X, Asu flags=0x%X", SId, Asu_flags);
- if (Rfu != 0) {
- sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", Number of EIds=%d", Number_EIds);
- strcat(desc, tmpbuf);
- key = ((uint32_t)oe << 17) | ((uint32_t)pd << 16) | (uint32_t)SId;
- sprintf(tmpbuf, ", database key=0x%05x", key);
- strcat(desc, tmpbuf);
- // CEI Change Event Indication
- if (Number_EIds == 0) {
- sprintf(tmpbuf, ", CEI");
- strcat(desc, tmpbuf);
- }
- printbuf(desc, indent+1, NULL, 0);
- i += 5;
-
- for(j = 0; (j < Number_EIds) && (i < (figlen - 1)); j++) {
- // iterate over EIds
- EId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
- sprintf(desc, "EId=0x%X", EId);
- printbuf(desc, indent+2, NULL, 0);
- i += 2;
- }
- if (j < Number_EIds) {
- sprintf(desc, "missing EId, fig length too short !");
- printbuf(desc, indent+1, NULL, 0);
- fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, ext, figlen);
- }
-
- // decode OE announcement support types
- for(j = 0; j < 16; j++) {
- if (Asu_flags & (1 << j)) {
- sprintf(desc, "OE Announcement support=%s", announcement_types_str[j]);
- printbuf(desc, indent+2, NULL, 0);
- }
- }
- }
- }
- break;
- case 26: // FIG 0/26 OE Announcement switching
- { // ETSI EN 300 401 8.1.10.5.2
- uint16_t Asw_flags, EId_Other_Ensemble;
- uint8_t i = 1, j, Rfa, Cluster_Id_Current_Ensemble, Region_Id_Current_Ensemble;
- uint8_t Cluster_Id_Other_Ensemble, Region_Id_Other_Ensemble;
- bool New_flag, Region_flag;
- char tmpbuf[256];
-
- while (i < (figlen - 6)) {
- // iterate over other ensembles announcement switching
- Cluster_Id_Current_Ensemble = f[i];
- Asw_flags = ((uint16_t)f[i+1] << 8) | (uint16_t)f[i+2];
- New_flag = f[i+3] >> 7;
- Region_flag = (f[i+3] >> 6) & 0x01;
- Region_Id_Current_Ensemble = f[i+3] & 0x3F;
- EId_Other_Ensemble = ((uint16_t)f[i+4] << 8) | (uint16_t)f[i+5];
- Cluster_Id_Other_Ensemble = f[i+6];
- sprintf(desc, "Cluster Id Current Ensemble=0x%X, Asw flags=0x%X, New flag=%d %s announcement, Region flag=%d last byte %s, Region Id Current Ensemble=0x%X, EId Other Ensemble=0x%X, Cluster Id Other Ensemble=0x%X",
- Cluster_Id_Current_Ensemble, Asw_flags, New_flag, New_flag?"newly introduced":"repeated",
- Region_flag, Region_flag?"present":"absent. The announcement concerns the whole service area",
- Region_Id_Current_Ensemble, EId_Other_Ensemble, Cluster_Id_Other_Ensemble);
- i += 7;
- if (Region_flag != 0) {
- if (i < figlen) {
- // get Region Id Other Ensemble
- Rfa = (f[i] >> 6);
- Region_Id_Other_Ensemble = f[i] & 0x3F;
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", Region Id Other Ensemble=0x%X", Region_Id_Other_Ensemble);
- strcat(desc, tmpbuf);
- }
- else {
- sprintf(tmpbuf, "missing Region Id Other Ensemble, fig length too short !");
- strcat(desc, tmpbuf);
- fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, ext, figlen);
- }
- i++;
- }
- printbuf(desc, indent+1, NULL, 0);
- // decode announcement switching types
- for(j = 0; j < 16; j++) {
- if (Asw_flags & (1 << j)) {
- sprintf(desc, "Announcement switching=%s", announcement_types_str[j]);
- printbuf(desc, indent+2, NULL, 0);
- }
- }
- }
- }
- break;
- case 27: // FIG 0/27 FM Announcement support
- { // ETSI EN 300 401 8.1.11.2.1
- uint16_t SId, PI;
- uint8_t i = 1, j, Rfu, Number_PI_codes, key;
- char tmpbuf[256];
-
- while (i < (figlen - 2)) {
- // iterate over FM announcement support
- SId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
- Rfu = f[i+2] >> 4;
- Number_PI_codes = f[i+2] & 0x0F;
- key = (oe << 5) | (pd << 4) | Number_PI_codes;
- sprintf(desc, "SId=0x%X", SId);
- if (Rfu != 0) {
- sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", Number of PI codes=%d", Number_PI_codes);
- strcat(desc, tmpbuf);
- if (Number_PI_codes > 12) {
- strcat(desc, " above maximum value of 12");
- fprintf(stderr, "WARNING: FIG %d/%d Number of PI codes=%d > 12 (maximum value)\n", figtype, ext, Number_PI_codes);
- }
- sprintf(tmpbuf, ", database key=0x%02X", key);
- strcat(desc, tmpbuf);
- // CEI Change Event Indication
- if (Number_PI_codes == 0) {
- // The Change Event Indication (CEI) is signalled by the Number of PI codes field = 0
- strcat(desc, ", CEI");
- }
- printbuf(desc, indent+1, NULL, 0);
- i += 3;
- for(j = 0; (j < Number_PI_codes) && (i < (figlen - 1)); j++) {
- // iterate over PI
- PI = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
- sprintf(desc, "PI=0x%X", PI);
- printbuf(desc, indent+2, NULL, 0);
- i += 2;
- }
- if (j != Number_PI_codes) {
- sprintf(desc, "fig length too short !");
- printbuf(desc, indent+2, NULL, 0);
- fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, ext, figlen);
- }
- }
- }
- break;
- case 28: // FIG 0/28 FM Announcement switching
- { // ETSI EN 300 401 8.1.11.2.2
- uint16_t PI;
- uint8_t i = 1, Cluster_Id_Current_Ensemble, Region_Id_Current_Ensemble;
- bool New_flag, Rfa;
- char tmpbuf[256];
-
- while (i < (figlen - 3)) {
- // iterate over FM announcement switching
- Cluster_Id_Current_Ensemble = f[i];
- New_flag = f[i+1] >> 7;
- Rfa = (f[i+1] >> 6) & 0x01;
- Region_Id_Current_Ensemble = f[i+1] & 0x3F;
- PI = ((uint16_t)f[i+2] << 8) | (uint16_t)f[i+3];
- sprintf(desc, "Cluster Id Current Ensemble=0x%X", Cluster_Id_Current_Ensemble);
- if (Cluster_Id_Current_Ensemble == 0) {
- strcat(desc, " invalid value");
- fprintf(stderr, "WARNING: FIG %d/%d Cluster Id Current Ensemble invalid value 0\n", figtype, ext);
- }
- sprintf(tmpbuf, ", New flag=%d %s announcement",
- New_flag, New_flag?"newly introduced":"repeated");
- strcat(desc, tmpbuf);
- if (Rfa != 0) {
- sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
- strcat(desc, tmpbuf);
- }
- sprintf(tmpbuf, ", Region Id Current Ensemble=0x%X, PI=0x%X", Region_Id_Current_Ensemble, PI);
- strcat(desc, tmpbuf);
- printbuf(desc, indent+1, NULL, 0);
- i += 4;
- }
- }
- break;
- case 31: // FIG 0/31 FIC re-direction
- { // ETSI EN 300 401 8.1.12
- uint32_t FIG_type0_flag_field = 0, flag_field;
- uint8_t i = 1, j, FIG_type1_flag_field = 0, FIG_type2_flag_field = 0;
-
- if (i < (figlen - 5)) {
- // Read FIC re-direction
- FIG_type0_flag_field = ((uint32_t)f[i] << 24) | ((uint32_t)f[i+1] << 16) |
- ((uint32_t)f[i+2] << 8) | (uint32_t)f[i+3];
- FIG_type1_flag_field = f[i+4];
- FIG_type2_flag_field = f[i+5];
-
- sprintf(desc, "FIG type 0 flag field=0x%X, FIG type 1 flag field=0x%X, FIG type 2 flag field=0x%X",
- FIG_type0_flag_field, FIG_type1_flag_field, FIG_type2_flag_field);
- printbuf(desc, indent+1, NULL, 0);
-
- for(j = 0; j < 32; j++) {
- // iterate over FIG type 0 re-direction
- flag_field = FIG_type0_flag_field & ((uint32_t)1 << j);
- if ((flag_field != 0) && ((j <= 5) || (j == 8) ||
- (j == 10) || (j == 13) || (j == 14) ||
- (j == 19) || (j == 26) || (j == 28))) {
- sprintf(desc, "oe=%d FIG 0/%d carried in AIC, invalid configuration, shall always be carried entirely in the FIC",
- oe, j);
- printbuf(desc, indent+2, NULL, 0);
- fprintf(stderr, "WARNING: FIG %d/%d FIG re-direction of oe=%d FIG0/%d not allowed\n", figtype, ext, oe, j);
- }
- else if ((flag_field != 0) && ((j == 21) || (j == 24))) {
- sprintf(desc, "oe=%d FIG 0/%d carried in AIC, same shall be carried in FIC", oe, j);
- printbuf(desc, indent+2, NULL, 0);
- }
- else if (flag_field != 0) {
- if (oe == 0) {
- sprintf(desc, "oe=%d FIG 0/%d carried in AIC, same shall be carried in FIC", oe, j);
- }
- else { // oe == 1
- sprintf(desc, "oe=%d FIG 0/%d carried in AIC, may be carried entirely in AIC", oe, j);
- }
- printbuf(desc, indent+2, NULL, 0);
- }
- }
-
- for(j = 0; j < 8; j++) {
- // iterate over FIG type 1 re-direction
- flag_field = FIG_type1_flag_field & ((uint32_t)1 << j);
- if (flag_field != 0) {
- if (oe == 0) {
- sprintf(desc, "oe=%d FIG 1/%d carried in AIC, same shall be carried in FIC", oe, j);
- }
- else { // oe == 1
- sprintf(desc, "oe=%d FIG 1/%d carried in AIC, may be carried entirely in AIC", oe, j);
- }
- printbuf(desc, indent+2, NULL, 0);
- }
- }
-
- for(j = 0; j < 8; j++) {
- // iterate over FIG type 2 re-direction
- flag_field = FIG_type2_flag_field & ((uint32_t)1 << j);
- if (flag_field != 0) {
- if (oe == 0) {
- sprintf(desc, "oe=%d FIG 2/%d carried in AIC, same shall be carried in FIC", oe, j);
- }
- else { // oe == 1
- sprintf(desc, "oe=%d FIG 2/%d carried in AIC, may be carried entirely in AIC", oe, j);
- }
- printbuf(desc, indent+2, NULL, 0);
- }
- }
- }
- if (figlen != 7) {
- fprintf(stderr, "WARNING: FIG %d/%d invalid length %d, expecting 7\n", figtype, ext, figlen);
- }
- }
- break;
- }
+ fig0_select(fig0, indent);
}
break;
@@ -3033,131 +853,4 @@ void decodeFIG(FIGalyser &figs,
}
}
-// get_programme_type_str return the programme type string from international table Id and programme type
-const char *get_programme_type_str(size_t int_table_Id, size_t pty) {
- if ((int_table_Id - 1) < INTERNATIONAL_TABLE_SIZE) {
- if (pty < PROGRAMME_TYPE_CODES_SIZE) {
- return Programme_type_codes_str[int_table_Id - 1][pty];
- }
- else {
- return "invalid programme type";
- }
- }
- else {
- return "unknown international table Id";
- }
-}
-
-// strcatPNum decode Programme_Number into string and append it to dest_str
-// Programme_Number: this 16-bit field shall define the date and time at which
-// a programme begins or will be continued. This field is coded in the same way
-// as the RDS "Programme Item Number (PIN)" feature (EN 62106).
-char *strcatPNum(char *dest_str, uint16_t Programme_Number) {
- uint8_t day, hour, minute;
- char tempbuf[256];
-
- minute = (uint8_t)(Programme_Number & 0x003F);
- hour = (uint8_t)((Programme_Number >> 6) & 0x001F);
- day = (uint8_t)((Programme_Number >> 11) & 0x001F);
- if (day != 0) {
- sprintf(tempbuf, "day of month=%d time=%02d:%02d", day, hour, minute);
- }
- else { // day == 0
- // Special codes are allowed when the date part of the PNum field
- // signals date = "0". In this case, the hours and minutes part of
- // the field shall contain a special code, as follows
- if ((hour == 0) && (minute == 0)) {
- sprintf(tempbuf, "Status code: no meaningful PNum is currently provided");
- }
- else if ((hour == 0) && (minute == 1)) {
- sprintf(tempbuf, "Blank code: the current programme is not worth recording");
- }
- else if ((hour == 0) && (minute == 2)) {
- sprintf(tempbuf, "Interrupt code: the interrupt is unplanned (for example a traffic announcement)");
- }
- else {
- sprintf(tempbuf, "invalid value");
- }
- }
- return strcat(dest_str, tempbuf);
-}
-
-// sprintfMJD: convert MJD (Modified Julian Date) into date string
-int sprintfMJD(char *dst, int mjd) {
- // EN 62106 Annex G
- // These formulas are applicable between the inclusive dates: 1st March 1900 to 28th February 2100
- int y, m, k;
- struct tm timeDate;
-
- memset(&timeDate, 0, sizeof(struct tm));
-
- // find Y, M, D from MJD
- y = (int)(((double)mjd - 15078.2) / 365.25);
- m = (int)(((double)mjd - 14956.1 - (int)((double)y * 365.25)) / 30.6001);
- timeDate.tm_mday = mjd - 14956 - (int)((double)y * 365.25) - (int)((double)m * 30.6001);
- if ((m == 14) || (m == 15)) {
- k = 1;
- }
- else {
- k = 0;
- }
- timeDate.tm_year = y + k;
- timeDate.tm_mon = (m - 1 - (k * 12)) - 1;
-
- // find WD from MJD
- timeDate.tm_wday = (((mjd + 2) % 7) + 1) % 7;
-
- //timeDate.tm_yday = 0; // Number of days since the first day of January not calculated
- timeDate.tm_isdst = -1; // No time print then information not available
-
- // print date string
- if ((timeDate.tm_mday < 0) || (timeDate.tm_mon < 0) || (timeDate.tm_year < 0)) {
- return sprintf(dst, "invalid MJD mday=%d mon=%d year=%d", timeDate.tm_mday, timeDate.tm_mon, timeDate.tm_year);
- }
- return strftime(dst, 256, "%a %b %d %Y", &timeDate);
-}
-
-void printinfo(string header,
- int indent_level,
- int min_verb)
-{
- if (verbosity >= min_verb) {
- for (int i = 0; i < indent_level; i++) {
- printf("\t");
- }
- printf("%s\n", header.c_str());
- }
-}
-
-void printbuf(string header,
- int indent_level,
- uint8_t* buffer,
- size_t size,
- string desc)
-{
- if (verbosity > 0) {
- for (int i = 0; i < indent_level; i++) {
- printf("\t");
- }
-
- printf("%s", header.c_str());
-
- if (verbosity > 1) {
- if (size != 0) {
- printf(": ");
- }
-
- for (size_t i = 0; i < size; i++) {
- printf("%02x ", buffer[i]);
- }
- }
-
- if (desc != "") {
- printf(" [%s] ", desc.c_str());
- }
-
- printf("\n");
- }
-}
-
diff --git a/fig0_0.cpp b/fig0_0.cpp
new file mode 100644
index 0000000..2b2b6a2
--- /dev/null
+++ b/fig0_0.cpp
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+
+// FIG 0/0 Ensemble information
+// ETSI EN 300 401 6.4
+void fig0_0(fig0_common_t& fig0, int indent)
+{
+ uint8_t cid, al, ch, hic, lowc, occ;
+ uint16_t eid, eref;
+ char desc[128];
+ uint8_t* f = fig0.f;
+
+ eid = f[1]*256+f[2];
+ cid = (f[1] & 0xF0) >> 4;
+ eref = (f[1] & 0x0F)*256 + \
+ f[2];
+ ch = (f[3] & 0xC0) >> 6;
+ al = (f[3] & 0x20) >> 5;
+ hic = f[3] & 0x1F;
+ lowc = f[4];
+ if (ch != 0) {
+ occ = f[5];
+ sprintf(desc,
+ "Ensemble ID=0x%02x (Country id=%d, Ensemble reference=%d), Change flag=%d, Alarm flag=%d, CIF Count=%d/%d, Occurance change=%d",
+ eid, cid, eref, ch, al, hic, lowc, occ);
+ }
+ else {
+ sprintf(desc,
+ "Ensemble ID=0x%02x (Country id=%d, Ensemble reference=%d), Change flag=%d, Alarm flag=%d, CIF Count=%d/%d",
+ eid, cid, eref, ch, al, hic, lowc);
+ }
+ printbuf(desc, indent+1, NULL, 0);
+}
+
diff --git a/fig0_1.cpp b/fig0_1.cpp
new file mode 100644
index 0000000..791c0ec
--- /dev/null
+++ b/fig0_1.cpp
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+
+// FIG 0/1 Basic sub-channel organization
+// ETSI EN 300 401 6.2.1
+void fig0_1(fig0_common_t& fig0, int indent)
+{
+ int i = 1;
+ uint8_t* f = fig0.f;
+ char desc[128];
+
+ while (i < fig0.figlen-3) {
+ // iterate over subchannels
+ int subch_id = f[i] >> 2;
+ int start_addr = ((f[i] & 0x03) << 8) |
+ (f[i+1]);
+ int long_flag = (f[i+2] >> 7);
+
+ if (long_flag) {
+ int option = (f[i+2] >> 4) & 0x07;
+ int protection_level = (f[i+2] >> 2) & 0x03;
+ int subchannel_size = ((f[i+2] & 0x03) << 8 ) |
+ f[i+3];
+
+ i += 4;
+
+ if (option == 0x00) {
+ sprintf(desc,
+ "Subch 0x%x, start_addr %d, long, EEP %d-A, subch size %d",
+ subch_id, start_addr, protection_level, subchannel_size);
+ }
+ else if (option == 0x01) {
+ sprintf(desc,
+ "Subch 0x%x, start_addr %d, long, EEP %d-B, subch size %d",
+ subch_id, start_addr, protection_level, subchannel_size);
+ }
+ else {
+ sprintf(desc,
+ "Subch 0x%x, start_addr %d, long, invalid option %d, protection %d, subch size %d",
+ subch_id, start_addr, option, protection_level, subchannel_size);
+ }
+ }
+ else {
+ int table_switch = (f[i+2] >> 6) & 0x01;
+ uint32_t table_index = (f[i+2] & 0x3F);
+
+
+ if (table_switch == 0) {
+ sprintf(desc,
+ "Subch 0x%x, start_addr %d, short, table index %d",
+ subch_id, start_addr, table_index);
+ }
+ else {
+ sprintf(desc,
+ "Subch 0x%x, start_addr %d, short, invalid table_switch(=1), table index %d",
+ subch_id, start_addr, table_index);
+ }
+
+ i += 3;
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ }
+
+}
+
diff --git a/fig0_10.cpp b/fig0_10.cpp
new file mode 100644
index 0000000..c374ef9
--- /dev/null
+++ b/fig0_10.cpp
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// FIG 0/10 Date and time
+// ETSI EN 300 401 8.1.3.1
+void fig0_10(fig0_common_t& fig0, int indent)
+{
+ char desc[256];
+ char dateStr[256];
+ dateStr[0] = 0;
+ uint8_t* f = fig0.f;
+
+ //bool RFU = f[1] >> 7;
+
+ uint32_t MJD = (((uint32_t)f[1] & 0x7F) << 10) |
+ ((uint32_t)(f[2]) << 2) |
+ (f[3] >> 6);
+ sprintfMJD(dateStr, MJD);
+
+ bool LSI = f[3] & 0x20;
+ bool ConfInd = f[3] & 0x10;
+ fig0.wm_decoder.push_confind_bit(ConfInd);
+ bool UTC = f[3] & 0x8;
+
+ uint8_t hours = ((f[3] & 0x7) << 2) |
+ ( f[4] >> 6);
+
+ uint8_t minutes = f[4] & 0x3f;
+
+ if (UTC) {
+ uint8_t seconds = f[5] >> 2;
+ uint16_t milliseconds = ((uint16_t)(f[5] & 0x2) << 8) | f[6];
+
+ sprintf(desc, "FIG 0/%d(long): MJD=0x%X %s, LSI %u, ConfInd %u, UTC Time: %02d:%02d:%02d.%d",
+ fig0.ext(), MJD, dateStr, LSI, ConfInd, hours, minutes, seconds, milliseconds);
+ printbuf(desc, indent+1, NULL, 0);
+ }
+ else {
+ sprintf(desc, "FIG 0/%d(short): MJD=0x%X %s, LSI %u, ConfInd %u, UTC Time: %02d:%02d",
+ fig0.ext(), MJD, dateStr, LSI, ConfInd, hours, minutes);
+ printbuf(desc, indent+1, NULL, 0);
+ }
+}
+
diff --git a/fig0_11.cpp b/fig0_11.cpp
new file mode 100644
index 0000000..cc6bead
--- /dev/null
+++ b/fig0_11.cpp
@@ -0,0 +1,185 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// FIG 0/11 Region definition
+// ETSI EN 300 401 8.1.16.1
+void fig0_11(fig0_common_t& fig0, int indent)
+{
+ Lat_Lng gps_pos = {0, 0};
+ int16_t Latitude_coarse, Longitude_coarse;
+ uint16_t Region_Id, Extent_Latitude, Extent_Longitude, key;
+ uint8_t i = 1, j, k, GATy, Rfu, Length_TII_list, Rfa, MainId, Length_SubId_list, SubId;
+ int8_t bit_pos;
+ char desc[256];
+ char tmpbuf[256];
+ bool GE_flag;
+ uint8_t* f = fig0.f;
+ uint8_t Mode_Identity = get_mode_identity();
+
+ while (i < (fig0.figlen - 1)) {
+ // iterate over Region definition
+ GATy = f[i] >> 4;
+ GE_flag = (f[i] >> 3) & 0x01;
+ Region_Id = ((uint16_t)(f[i] & 0x07) << 8) | ((uint16_t)f[i+1]);
+ key = ((uint16_t)fig0.oe() << 12) | ((uint16_t)fig0.pd() << 11) | Region_Id;
+ i += 2;
+ if (GATy == 0) {
+ // TII list
+ sprintf(desc, "GATy=%d Geographical area defined by a TII list, G/E flag=%d %s coverage area, RegionId=0x%X, database key=0x%X",
+ GATy, GE_flag, GE_flag?"Global":"Ensemble", Region_Id, key);
+ if (i < fig0.figlen) {
+ Rfu = f[i] >> 5;
+ if (Rfu != 0) {
+ sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
+ strcat(desc, tmpbuf);
+ }
+ Length_TII_list = f[i] & 0x1F;
+ sprintf(tmpbuf, ", Length of TII list=%d", Length_TII_list);
+ strcat(desc, tmpbuf);
+ if (Length_TII_list == 0) {
+ strcat(desc, ", CEI");
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ i++;
+
+ for(j = 0;(i < (fig0.figlen - 1)) && (j < Length_TII_list); j++) {
+ // iterate over Transmitter group
+ Rfa = f[i] >> 7;
+ MainId = f[i] & 0x7F;
+ if (Rfa != 0) {
+ sprintf(desc, "Rfa=%d invalid value, MainId=0x%X",
+ Rfa, MainId);
+ }
+ else {
+ sprintf(desc, "MainId=0x%X", MainId);
+ }
+ // check MainId value
+ if ((Mode_Identity == 1) || (Mode_Identity == 2) || (Mode_Identity == 4)) {
+ if (MainId > 69) {
+ // The coding range shall be 0 to 69 for transmission modes I, II and IV
+ sprintf(tmpbuf, " invalid value for transmission mode %d", Mode_Identity);
+ strcat(desc, tmpbuf);
+ }
+ }
+ else if (Mode_Identity == 3) {
+ if (MainId > 5) {
+ // The coding range shall be 0 to 5 for transmission modes I, II and IV
+ sprintf(tmpbuf, " invalid value for transmission mode %d", Mode_Identity);
+ strcat(desc, tmpbuf);
+ }
+ }
+ Rfa = f[i+1] >> 5;
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ Length_SubId_list = f[i+1] & 0x1F;
+ sprintf(tmpbuf, ", Length of SubId=%d", Length_SubId_list);
+ strcat(desc, tmpbuf);
+ printbuf(desc, indent+2, NULL, 0);
+ i += 2;
+
+ bit_pos = 3;
+ SubId = 0;
+ for(k = 0;(i < fig0.figlen) && (k < Length_SubId_list); k++) {
+ // iterate SubId
+ if (bit_pos >= 0) {
+ SubId |= (f[i] >> bit_pos) & 0x1F;
+ sprintf(desc, "SubId=0x%X", SubId);
+ // check SubId value
+ if ((SubId == 0) || (SubId > 23)) {
+ strcat(desc, " invalid value");
+ }
+ printbuf(desc, indent+3, NULL, 0);
+ bit_pos -= 5;
+ SubId = 0;
+ }
+ if (bit_pos < 0) {
+ SubId = (f[i] << abs(bit_pos)) & 0x1F;
+ bit_pos += 8;
+ i++;
+ }
+ }
+ if (bit_pos > 3) {
+ // jump padding
+ i++;
+ }
+ if (k < Length_SubId_list) {
+ sprintf(desc, "%d SubId missing, fig length too short !", (Length_SubId_list - k));
+ printbuf(desc, indent+3, NULL, 0);
+ fprintf(stderr, "WARNING: FIG 0/%d length %d too short !\n", fig0.ext(), fig0.figlen);
+ }
+ }
+ if (j < Length_TII_list) {
+ sprintf(desc, "%d Transmitter group missing, fig length too short !", (Length_TII_list - j));
+ printbuf(desc, indent+2, NULL, 0);
+ fprintf(stderr, "WARNING: FIG 0/%d length %d too short !\n", fig0.ext(), fig0.figlen);
+ }
+ }
+ }
+ else if (GATy == 1) {
+ // Coordinates
+ sprintf(desc, "GATy=%d Geographical area defined as a spherical rectangle by the geographical co-ordinates of one corner and its latitude and longitude extents, G/E flag=%d %s coverage area, RegionId=0x%X, database key=0x%X",
+ GATy, GE_flag, GE_flag?"Global":"Ensemble", Region_Id, key);
+ if (i < (fig0.figlen - 6)) {
+ Latitude_coarse = ((int16_t)f[i] << 8) | ((uint16_t)f[i+1]);
+ Longitude_coarse = ((int16_t)f[i+2] << 8) | ((uint16_t)f[i+3]);
+ gps_pos.latitude = ((double)Latitude_coarse) * 90 / 32768;
+ gps_pos.longitude = ((double)Latitude_coarse) * 180 / 32768;
+ sprintf(tmpbuf, ", Lat Lng coarse=0x%X 0x%X => %f, %f",
+ Latitude_coarse, Longitude_coarse, gps_pos.latitude, gps_pos.longitude);
+ strcat(desc, tmpbuf);
+ Extent_Latitude = ((uint16_t)f[i+4] << 4) | ((uint16_t)(f[i+5] >> 4));
+ Extent_Longitude = ((uint16_t)(f[i+5] & 0x0F) << 8) | ((uint16_t)f[i+6]);
+ gps_pos.latitude += ((double)Extent_Latitude) * 90 / 32768;
+ gps_pos.longitude += ((double)Extent_Longitude) * 180 / 32768;
+ sprintf(tmpbuf, ", Extent Lat Lng=0x%X 0x%X => %f, %f",
+ Extent_Latitude, Extent_Longitude, gps_pos.latitude, gps_pos.longitude);
+ strcat(desc, tmpbuf);
+ }
+ else {
+ sprintf(tmpbuf, ", Coordinates missing, fig length too short !");
+ strcat(desc, tmpbuf);
+ fprintf(stderr, "WARNING: FIG 0/%d length %d too short !\n", fig0.ext(), fig0.figlen);
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ i += 7;
+ }
+ else {
+ // Rfu
+ sprintf(desc, "GATy=%d reserved for future use of the geographical, G/E flag=%d %s coverage area, RegionId=0x%X, database key=0x%X, stop Region definition iteration %d/%d",
+ GATy, GE_flag, GE_flag?"Global":"Ensemble", Region_Id, key, i, fig0.figlen);
+ printbuf(desc, indent+1, NULL, 0);
+ // stop Region definition iteration
+ i = fig0.figlen;
+ }
+ }
+}
diff --git a/fig0_13.cpp b/fig0_13.cpp
new file mode 100644
index 0000000..113bb49
--- /dev/null
+++ b/fig0_13.cpp
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+std::string get_fig_0_13_userapp(int user_app_type)
+{
+ switch (user_app_type) {
+ case 0x000: return "Reserved for future definition";
+ case 0x001: return "Not used";
+ case 0x002: return "MOT Slideshow";
+ case 0x003: return "MOT Broadacst Web Site";
+ case 0x004: return "TPEG";
+ case 0x005: return "DGPS";
+ case 0x006: return "TMC";
+ case 0x007: return "EPG";
+ case 0x008: return "DAB Java";
+ case 0x44a: return "Journaline";
+ default: return "Reserved for future applications";
+ }
+}
+
+// FIG 0/13 User application information
+// ETSI EN 300 401 8.1.20
+void fig0_13(fig0_common_t& fig0, int indent)
+{
+ uint32_t SId;
+ uint8_t SCIdS;
+ uint8_t No;
+ uint8_t* f = fig0.f;
+ const int figtype = 0;
+ char desc[256];
+
+ int k = 1;
+
+ if (fig0.pd() == 0) { // Programme services, 16 bit SId
+ SId = (f[k] << 8) |
+ f[k+1];
+ k+=2;
+
+ SCIdS = f[k] >> 4;
+ No = f[k] & 0x0F;
+ k++;
+ }
+ else { // Data services, 32 bit SId
+ SId = (f[k] << 24) |
+ (f[k+1] << 16) |
+ (f[k+2] << 8) |
+ f[k+3];
+ k+=4;
+
+ SCIdS = f[k] >> 4;
+ No = f[k] & 0x0F;
+ k++;
+
+ }
+
+ sprintf(desc, "FIG %d/%d: SId=0x%X SCIdS=%u No=%u",
+ figtype, fig0.ext(), SId, SCIdS, No);
+ printbuf(desc, indent+1, NULL, 0);
+
+ for (int numapp = 0; numapp < No; numapp++) {
+ uint16_t user_app_type = ((f[k] << 8) |
+ (f[k+1] & 0xE0)) >> 5;
+ uint8_t user_app_len = f[k+1] & 0x1F;
+ k+=2;
+
+ sprintf(desc, "User Application %d '%s'; length %u",
+ user_app_type,
+ get_fig_0_13_userapp(user_app_type).c_str(),
+ user_app_len);
+ printbuf(desc, indent+2, NULL, 0);
+ }
+}
+
diff --git a/fig0_14.cpp b/fig0_14.cpp
new file mode 100644
index 0000000..8d63cae
--- /dev/null
+++ b/fig0_14.cpp
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// fig 0/14 FEC Scheme: this 2-bit field shall indicate the Forward Error Correction scheme in use, as follows:
+const char *FEC_schemes_str[4] = {
+ "no FEC scheme applied",
+ "FEC scheme applied according to ETSI EN 300 401 clause 5.3.5",
+ "reserved for future definition",
+ "reserved for future definition"
+};
+
+
+// FIG 0/14 FEC sub-channel organization
+// ETSI EN 300 401 6.2.2
+void fig0_14(fig0_common_t& fig0, int indent)
+{
+ uint8_t i = 1, SubChId, FEC_scheme;
+ uint8_t* f = fig0.f;
+ char desc[256];
+
+ while (i < fig0.figlen) {
+ // iterate over Sub-channel
+ SubChId = f[i] >> 2;
+ FEC_scheme = f[i] & 0x3;
+ sprintf(desc, "SubChId=0x%X, FEC scheme=%d %s",
+ SubChId, FEC_scheme, FEC_schemes_str[FEC_scheme]);
+ printbuf(desc, indent+1, NULL, 0);
+ i++;
+ }
+}
+
diff --git a/fig0_16.cpp b/fig0_16.cpp
new file mode 100644
index 0000000..aee7d10
--- /dev/null
+++ b/fig0_16.cpp
@@ -0,0 +1,100 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// FIG 0/16 Programme Number & fig0.oe() Programme Number
+// ETSI EN 300 401 8.1.4 & 8.1.10.3
+void fig0_16(fig0_common_t& fig0, int indent)
+{
+ uint16_t SId, PNum, New_SId, New_PNum;
+ uint8_t i = 1, Rfa, Rfu;
+ char tmpbuf[256];
+ char desc[256];
+ bool Continuation_flag, Update_flag;
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 4)) {
+ // iterate over Programme Number
+ SId = ((uint16_t)f[i] << 8) | ((uint16_t)f[i+1]);
+ PNum = ((uint16_t)f[i+2] << 8) | ((uint16_t)f[i+3]);
+ Rfa = f[i+4] >> 6;
+ Rfu = (f[i+4] >> 2) & 0x0F;
+ Continuation_flag = (f[i+4] >> 1) & 0x01;
+ Update_flag = f[i+4] & 0x01;
+
+ sprintf(desc, "SId=0x%X, PNum=0x%X ", SId, PNum);
+ // Append PNum decoded string
+ strcatPNum(desc, PNum);
+
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+
+ if (Rfu != 0) {
+ sprintf(tmpbuf, ", Rfu=0x%X invalid value", Rfu);
+ strcat(desc, tmpbuf);
+ }
+
+ sprintf(tmpbuf, ", Continuation flag=%d the programme will %s, Update flag=%d %sre-direction",
+ Continuation_flag, Continuation_flag?"be interrupted but continued later":"not be subject to a planned interruption",
+ Update_flag, Update_flag?"":"no ");
+ strcat(desc, tmpbuf);
+ i += 5;
+
+ if (Update_flag != 0) {
+ // In the case of a re-direction, the New SId and New PNum shall be appended
+ if (i < (fig0.figlen - 1)) {
+ New_SId = ((uint16_t)f[i] << 8) | ((uint16_t)f[i+1]);
+ sprintf(tmpbuf, ", New SId=0x%X", New_SId);
+ strcat(desc, tmpbuf);
+ if (i < (fig0.figlen - 3)) {
+ New_PNum = ((uint16_t)f[i+2] << 8) | ((uint16_t)f[i+3]);
+ sprintf(tmpbuf, ", New PNum=0x%X ", New_PNum);
+ strcat(desc, tmpbuf);
+ // Append New_PNum decoded string
+ strcatPNum(desc, New_PNum);
+ }
+ else {
+ sprintf(tmpbuf, ", missing New PNum !");
+ strcat(desc, tmpbuf);
+ }
+ }
+ else {
+ sprintf(tmpbuf, ", missing New SId and New PNum !");
+ strcat(desc, tmpbuf);
+ }
+ i += 4;
+ }
+
+ printbuf(desc, indent+1, NULL, 0);
+ }
+}
+
diff --git a/fig0_17.cpp b/fig0_17.cpp
new file mode 100644
index 0000000..264d151
--- /dev/null
+++ b/fig0_17.cpp
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// FIG 0/17 Programme Type
+// ETSI EN 300 401 8.1.5
+void fig0_17(fig0_common_t& fig0, int indent)
+{
+ uint16_t SId;
+ uint8_t i = 1, Rfa, Language, Int_code, Comp_code;
+ char tmpbuf[256];
+ char desc[256];
+ bool SD_flag, PS_flag, L_flag, CC_flag, Rfu;
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 3)) {
+ // iterate over announcement support
+ SId = (f[i] << 8) | f[i+1];
+ SD_flag = (f[i+2] >> 7);
+ PS_flag = ((f[i+2] >> 6) & 0x01);
+ L_flag = ((f[i+2] >> 5) & 0x01);
+ CC_flag = ((f[i+2] >> 4) & 0x01);
+ Rfa = (f[i+2] & 0x0F);
+ sprintf(desc, "SId=0x%X, S/D=%d Programme Type codes and language (when present), %srepresent the current programme contents, P/S=%d %s service component, L flag=%d language field %s, CC flag=%d complementary code and preceding Rfa and Rfu fields %s",
+ SId, SD_flag, SD_flag?"":"may not ", PS_flag, PS_flag?"secondary":"primary",
+ L_flag, L_flag?"present":"absent", CC_flag, CC_flag?"present":"absent");
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=0x%X invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ i += 3;
+ if (L_flag != 0) {
+ if (i < fig0.figlen) {
+ Language = f[i];
+ sprintf(tmpbuf, ", Language=0x%X %s", Language,
+ get_language_name(Language));
+ strcat(desc, tmpbuf);
+ }
+ else {
+ sprintf(tmpbuf, ", Language= invalid FIG length");
+ strcat(desc, tmpbuf);
+ }
+ i++;
+ }
+ if (i < fig0.figlen) {
+ Rfa = f[i] >> 6;
+ Rfu = (f[i] >> 5) & 0x01;
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=0x%X invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ if (Rfu != 0) {
+ sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
+ strcat(desc, tmpbuf);
+ }
+ Int_code = f[i] & 0x1F;
+ sprintf(tmpbuf, ", Int code=0x%X %s", Int_code, get_programme_type(get_international_table() , Int_code));
+ strcat(desc, tmpbuf);
+ i++;
+ }
+ else {
+ sprintf(tmpbuf, ", Int code= invalid FIG length");
+ strcat(desc, tmpbuf);
+ }
+ if (CC_flag != 0) {
+ if (i < fig0.figlen) {
+ Rfa = f[i] >> 6;
+ Rfu = (f[i] >> 5) & 0x01;
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=0x%X invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ if (Rfu != 0) {
+ sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
+ strcat(desc, tmpbuf);
+ }
+ Comp_code = f[i] & 0x1F;
+ sprintf(tmpbuf, ", Comp code=0x%X %s", Comp_code, get_programme_type(get_international_table() , Comp_code));
+ strcat(desc, tmpbuf);
+ i++;
+ }
+ else {
+ sprintf(tmpbuf, ", Comp code= invalid FIG length");
+ strcat(desc, tmpbuf);
+ }
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ }
+}
+
diff --git a/fig0_18.cpp b/fig0_18.cpp
new file mode 100644
index 0000000..c7d7f8a
--- /dev/null
+++ b/fig0_18.cpp
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+
+// FIG 0/18 Announcement support
+// ETSI EN 300 401 8.1.6.1
+void fig0_18(fig0_common_t& fig0, int indent)
+{
+ uint32_t key;
+ uint16_t SId, Asu_flags;
+ uint8_t i = 1, j, Rfa, Number_clusters;
+ char tmpbuf[256];
+ char desc[256];
+ uint8_t* f = fig0.f;
+ const int figtype = 0;
+
+ while (i < (fig0.figlen - 4)) {
+ // iterate over announcement support
+ // SId, Asu flags, Rfa, Number of clusters
+ SId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
+ Asu_flags = ((uint16_t)f[i+2] << 8) | (uint16_t)f[i+3];
+ Rfa = (f[i+4] >> 5);
+ Number_clusters = (f[i+4] & 0x1F);
+ sprintf(desc, "SId=0x%X, Asu flags=0x%04x", SId, Asu_flags);
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", Number of clusters=%d", Number_clusters);
+ strcat(desc, tmpbuf);
+ key = ((uint32_t)fig0.oe() << 17) | ((uint32_t)fig0.pd() << 16) | (uint32_t)SId;
+ sprintf(tmpbuf, ", database key=0x%05x", key);
+ strcat(desc, tmpbuf);
+ // CEI Change Event Indication
+ if ((Number_clusters == 0) && (Asu_flags == 0)) {
+ sprintf(tmpbuf, ", CEI");
+ strcat(desc, tmpbuf);
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ i += 5;
+
+ for(j = 0; (j < Number_clusters) && (i < fig0.figlen); j++) {
+ // iterate over Cluster Id
+ sprintf(desc, "Cluster Id=0x%X", f[i]);
+ printbuf(desc, indent+2, NULL, 0);
+ i++;
+ }
+ if (j < Number_clusters) {
+ sprintf(desc, "missing Cluster Id, fig length too short !");
+ printbuf(desc, indent+1, NULL, 0);
+ fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, fig0.ext(), fig0.figlen);
+ }
+
+ // decode announcement support types
+ for(j = 0; j < 16; j++) {
+ if (Asu_flags & (1 << j)) {
+ sprintf(desc, "Announcement support=%s", get_announcement_type(j));
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ }
+ }
+}
+
diff --git a/fig0_19.cpp b/fig0_19.cpp
new file mode 100644
index 0000000..1d3593e
--- /dev/null
+++ b/fig0_19.cpp
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+
+// FIG 0/19 Announcement switching
+// ETSI EN 300 401 8.1.6.2
+void fig0_19(fig0_common_t& fig0, int indent)
+{
+ uint16_t Asw_flags;
+ uint8_t i = 1, j, Cluster_Id, SubChId, Rfa, RegionId_LP;
+ char tmpbuf[256];
+ char desc[256];
+ bool New_flag, Region_flag;
+ uint8_t* f = fig0.f;
+ const int figtype = 0;
+
+ while (i < (fig0.figlen - 3)) {
+ // iterate over announcement switching
+ // Cluster Id, Asw flags, New flag, Region flag,
+ // SubChId, Rfa, Region Id Lower Part
+ Cluster_Id = f[i];
+ Asw_flags = ((uint16_t)f[i+1] << 8) | (uint16_t)f[i+2];
+ New_flag = (f[i+3] >> 7);
+ Region_flag = (f[i+3] >> 6) & 0x1;
+ SubChId = (f[i+3] & 0x3F);
+ sprintf(desc, "Cluster Id=0x%02x, Asw flags=0x%04x, New flag=%d %s, Region flag=%d last byte %s, SubChId=%d",
+ Cluster_Id, Asw_flags, New_flag, (New_flag)?"new":"repeat", Region_flag, (Region_flag)?"present":"absent", SubChId);
+ if (Region_flag) {
+ if (i < (fig0.figlen - 4)) {
+ // read region lower part
+ Rfa = (f[i+4] >> 6);
+ RegionId_LP = (f[i+4] & 0x3F);
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", Region Lower Part=0x%02x", RegionId_LP);
+ strcat(desc, tmpbuf);
+ }
+ else {
+ sprintf(tmpbuf, "missing Region Lower Part, fig length too short !");
+ strcat(desc, tmpbuf);
+ fprintf(stderr, "WARNING: FIG %d/%d length %d too short !\n", figtype, fig0.ext(), fig0.figlen);
+ }
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ // decode announcement switching types
+ for(j = 0; j < 16; j++) {
+ if (Asw_flags & (1 << j)) {
+ sprintf(desc, "Announcement switching=%s", get_announcement_type(j));
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ }
+ i += (4 + Region_flag);
+ }
+}
+
diff --git a/fig0_2.cpp b/fig0_2.cpp
new file mode 100644
index 0000000..29b52fa
--- /dev/null
+++ b/fig0_2.cpp
@@ -0,0 +1,146 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <string>
+#include <cstring>
+
+// FIG 0/2 Basic service and service component definition
+// ETSI EN 300 401 6.3.1
+void fig0_2(fig0_common_t& fig0, int indent)
+{
+ uint16_t sref, sid;
+ uint8_t cid, ecc, local, caid, ncomp, timd, ps, ca, subchid, scty;
+ int k = 1;
+ std::string psdesc;
+ uint8_t* f = fig0.f;
+ char sctydesc[32];
+ char desc[256];
+
+ while (k < fig0.figlen) {
+ if (fig0.pd() == 0) {
+ sid = f[k] * 256 + f[k+1];
+ cid = (f[k] & 0xF0) >> 4;
+ sref = (f[k] & 0x0F) * 256 + f[k+1];
+ k += 2;
+ }
+ else {
+ sid = f[k] * 256 * 256 * 256 + \
+ f[k+1] * 256 * 256 + \
+ f[k+2] * 256 + \
+ f[k+3];
+
+ ecc = f[k];
+ cid = (f[k+1] & 0xF0) >> 4;
+ sref = (f[k+1] & 0x0F) * 256 * 256 + \
+ f[k+2] * 256 + \
+ f[k+3];
+
+ k += 4;
+ }
+
+ local = (f[k] & 0x80) >> 7;
+ caid = (f[k] & 0x70) >> 4;
+ ncomp = f[k] & 0x0F;
+
+ if (fig0.pd() == 0)
+ sprintf(desc,
+ "Service ID=0x%X (Country id=%d, Service reference=%d), Number of components=%d, Local flag=%d, CAID=%d",
+ sid, cid, sref, ncomp, local, caid);
+ else
+ sprintf(desc,
+ "Service ID=0x%X (ECC=%d, Country id=%d, Service reference=%d), Number of components=%d, Local flag=%d, CAID=%d",
+ sid, ecc, cid, sref, ncomp, local, caid);
+ printbuf(desc, indent+1, NULL, 0);
+
+ k++;
+ for (int i=0; i<ncomp; i++) {
+ uint8_t scomp[2];
+
+ memcpy(scomp, f+k, 2);
+ sprintf(desc, "Component[%d]", i);
+ printbuf(desc, indent+2, scomp, 2, "");
+ timd = (scomp[0] & 0xC0) >> 6;
+ ps = (scomp[1] & 0x02) >> 1;
+ ca = scomp[1] & 0x01;
+ scty = scomp[0] & 0x3F;
+ subchid = (scomp[1] & 0xFC) >> 2;
+
+ /* useless, kept as reference
+ if (timd == 3) {
+ uint16_t scid;
+ scid = scty*64 + subchid;
+ }
+ */
+
+ if (ps == 0) {
+ psdesc = "Secondary service";
+ }
+ else {
+ psdesc = "Primary service";
+ }
+
+
+ if (timd == 0) {
+ //MSC stream audio
+ if (scty == 0)
+ sprintf(sctydesc, "MPEG Foreground sound (%d)", scty);
+ else if (scty == 1)
+ sprintf(sctydesc, "MPEG Background sound (%d)", scty);
+ else if (scty == 2)
+ sprintf(sctydesc, "Multi Channel sound (%d)", scty);
+ else if (scty == 63)
+ sprintf(sctydesc, "AAC sound (%d)", scty);
+ else
+ sprintf(sctydesc, "Unknown ASCTy (%d)", scty);
+
+ sprintf(desc, "Stream audio mode, %s, %s, SubChannel ID=%02X, CA=%d", psdesc.c_str(), sctydesc, subchid, ca);
+ printbuf(desc, indent+3, NULL, 0);
+ }
+ else if (timd == 1) {
+ // MSC stream data
+ sprintf(sctydesc, "DSCTy=%d %s", scty, get_dscty_type(scty));
+ sprintf(desc, "Stream data mode, %s, %s, SubChannel ID=%02X, CA=%d", psdesc.c_str(), sctydesc, subchid, ca);
+ printbuf(desc, indent+3, NULL, 0);
+ }
+ else if (timd == 2) {
+ // FIDC
+ sprintf(sctydesc, "DSCTy=%d %s", scty, get_dscty_type(scty));
+ sprintf(desc, "FIDC mode, %s, %s, Fast Information Data Channel ID=%02X, CA=%d", psdesc.c_str(), sctydesc, subchid, ca);
+ printbuf(desc, indent+3, NULL, 0);
+ }
+ else if (timd == 3) {
+ // MSC Packet mode
+ sprintf(desc, "MSC Packet Mode, %s, Service Component ID=%02X, CA=%d", psdesc.c_str(), subchid, ca);
+ printbuf(desc, indent+3, NULL, 0);
+ }
+ k += 2;
+ }
+ }
+}
+
+
diff --git a/fig0_21.cpp b/fig0_21.cpp
new file mode 100644
index 0000000..9e6b113
--- /dev/null
+++ b/fig0_21.cpp
@@ -0,0 +1,302 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+
+// FIG 0/21 Frequency Information
+// ETSI EN 300 401 8.1.8
+void fig0_21(fig0_common_t& fig0, int indent)
+{
+ float freq;
+ uint32_t ifreq;
+ uint64_t key;
+ uint16_t RegionId, Id_field;
+ uint8_t i = 1, j, k, Length_FI_list, RandM, Length_Freq_list, Control_field;
+ uint8_t Control_field_trans_mode, Id_field2;
+ char tmpbuf[256];
+ char desc[256];
+ bool Continuity_flag;
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 1)) {
+ // iterate over frequency information
+ // decode RegionId, Length of FI list
+ RegionId = (f[i] << 3) | (f[i+1] >> 5);
+ Length_FI_list = (f[i+1] & 0x1F);
+ sprintf(desc, "RegionId=0x%03x", RegionId);
+ printbuf(desc, indent+1, NULL, 0);
+ i += 2;
+ if ((i + Length_FI_list) <= fig0.figlen) {
+ j = i;
+ while ((j + 2) < (i + Length_FI_list)) {
+ // iterate over FI list x
+ // decode Id field, R&M, Continuity flag, Length of Freq list
+ Id_field = (f[j] << 8) | f[j+1];
+ RandM = f[j+2] >> 4;
+ Continuity_flag = (f[j+2] >> 3) & 0x01;
+ Length_Freq_list = f[j+2] & 0x07;
+ sprintf(desc, "Id_field=");
+ switch (RandM) {
+ case 0x0:
+ case 0x1:
+ strcat(desc, "EId");
+ break;
+ case 0x6:
+ strcat(desc, "DRM Service Id");
+ break;
+ case 0x8:
+ strcat(desc, "RDS PI");
+ break;
+ case 0x9:
+ case 0xa:
+ case 0xc:
+ strcat(desc, "Dummy");
+ break;
+ case 0xe:
+ strcat(desc, "AMSS Service Id");
+ break;
+ default:
+ strcat(desc, "invalid");
+ break;
+ }
+ sprintf(tmpbuf, "=0x%X, R&M=0x%1x", Id_field, RandM);
+ strcat(desc, tmpbuf);
+ switch (RandM) {
+ case 0x0:
+ strcat(desc, "=DAB ensemble, no local windows");
+ break;
+ case 0x6:
+ strcat(desc, "=DRM");
+ break;
+ case 0x8:
+ strcat(desc, "=FM with RDS");
+ break;
+ case 0x9:
+ strcat(desc, "=FM without RDS");
+ break;
+ case 0xa:
+ strcat(desc, "=AM (MW in 9 kHz steps & LW)");
+ break;
+ case 0xc:
+ strcat(desc, "=AM (MW in 5 kHz steps & SW)");
+ break;
+ case 0xe:
+ strcat(desc, "=AMSS");
+ break;
+ default:
+ strcat(desc, "=Rfu");
+ break;
+ }
+ sprintf(tmpbuf, ", Continuity flag=%d", Continuity_flag);
+ strcat(desc, tmpbuf);
+ if ((fig0.oe() == 0) || ((fig0.oe() == 1) && (RandM != 0x6) && \
+ ((RandM < 0x8) || (RandM > 0xa)) && (RandM != 0xc) && (RandM != 0xe))) {
+ if (Continuity_flag == 0) {
+ switch (RandM) {
+ case 0x0:
+ case 0x1:
+ strcat(desc, "=continuous output not expected");
+ break;
+ case 0x6:
+ strcat(desc, "=no compensating time delay on DRM audio signal");
+ break;
+ case 0x8:
+ case 0x9:
+ strcat(desc, "=no compensating time delay on FM audio signal");
+ break;
+ case 0xa:
+ case 0xc:
+ case 0xe:
+ strcat(desc, "=no compensating time delay on AM audio signal");
+ break;
+ default:
+ strcat(desc, "=Rfu");
+ break;
+ }
+ }
+ else { // Continuity_flag == 1
+ switch (RandM) {
+ case 0x0:
+ case 0x1:
+ strcat(desc, "=continuous output possible");
+ break;
+ case 0x6:
+ strcat(desc, "=compensating time delay on DRM audio signal");
+ break;
+ case 0x8:
+ case 0x9:
+ strcat(desc, "=compensating time delay on FM audio signal");
+ break;
+ case 0xa:
+ case 0xc:
+ case 0xe:
+ strcat(desc, "=compensating time delay on AM audio signal");
+ break;
+ default:
+ strcat(desc, "=Rfu");
+ break;
+ }
+ }
+ }
+ else { // fig0.oe() == 1
+ strcat(desc, "=reserved for future addition");
+ }
+ key = ((uint64_t)fig0.oe() << 32) | ((uint64_t)fig0.pd() << 31) | \
+ ((uint64_t)RegionId << 20) | ((uint64_t)Id_field << 4) | \
+ (uint64_t)RandM;
+ sprintf(tmpbuf, ", database key=0x%09" PRId64, key);
+ // CEI Change Event Indication
+ if (Length_Freq_list == 0) {
+ strcat(tmpbuf, ", CEI");
+ }
+ strcat(desc, tmpbuf);
+ printbuf(desc, indent+2, NULL, 0);
+ j += 3; // add header
+
+ k = j;
+ switch (RandM) {
+ case 0x0:
+ case 0x1:
+ while((k + 2) < (j + Length_Freq_list)) {
+ // iteration over Freq list
+ ifreq = (((uint32_t)(f[k] & 0x07) << 16) | ((uint32_t)f[k+1] << 8) | (uint32_t)f[k+2]) * 16;
+ if (ifreq != 0) {
+ Control_field = (f[k] >> 3);
+ Control_field_trans_mode = (Control_field >> 1) & 0x07;
+ if ((Control_field & 0x10) == 0) {
+ sprintf(desc, "%d KHz, ", ifreq);
+ if ((Control_field & 0x01) == 0) {
+ strcat(desc, "geographically adjacent area, ");
+ }
+ else { // (Control_field & 0x01) == 1
+ strcat(desc, "no geographically adjacent area, ");
+ }
+ if (Control_field_trans_mode == 0) {
+ strcat(desc, "no transmission mode signalled");
+ }
+ else if (Control_field_trans_mode <= 4) {
+ sprintf(tmpbuf, "transmission mode %d", Control_field_trans_mode);
+ strcat(desc, tmpbuf);
+ }
+ else { // Control_field_trans_mode > 4
+ sprintf(tmpbuf, "invalid transmission mode 0x%x", Control_field_trans_mode);
+ strcat(desc, tmpbuf);
+ }
+ }
+ else { // (Control_field & 0x10) == 0x10
+ sprintf(desc, "%d KHz, invalid Control field b23 0x%x", ifreq, Control_field);
+ }
+ }
+ else {
+ sprintf(desc, "Frequency not to be used (0)");
+ }
+ printbuf(desc, indent+3, NULL, 0);
+ k += 3;
+ }
+ break;
+ case 0x8:
+ case 0x9:
+ case 0xa:
+ while(k < (j + Length_Freq_list)) {
+ // iteration over Freq list
+ if (f[k] != 0) { // freq != 0
+ if (RandM == 0xa) {
+ if (f[k] < 16) {
+ ifreq = (144 + ((uint32_t)f[k] * 9));
+ }
+ else { // f[k] >= 16
+ ifreq = (387 + ((uint32_t)f[k] * 9));
+ }
+ sprintf(desc, "%d KHz", ifreq);
+ }
+ else { // RandM == 8 or 9
+ freq = (87.5 + ((float)f[k] * 0.1));
+ sprintf(desc, "%.1f MHz", freq);
+ }
+ }
+ else {
+ sprintf(desc, "Frequency not to be used (0)");
+ }
+ printbuf(desc, indent+3, NULL, 0);
+ k++;
+ }
+ break;
+ case 0xc:
+ while((k + 1) < (j + Length_Freq_list)) {
+ // iteration over Freq list
+ ifreq = (((uint32_t)f[k] << 8) | (uint32_t)f[k+1]) * 5;
+ if (ifreq != 0) {
+ sprintf(desc, "%d KHz", ifreq);
+ }
+ else {
+ sprintf(desc, "Frequency not to be used (0)");
+ }
+ printbuf(desc, indent+3, NULL, 0);
+ k += 2;
+ }
+ break;
+ case 0x6:
+ case 0xe:
+ while((k + 2) < (j + Length_Freq_list)) {
+ // iteration over Freq list
+ Id_field2 = f[k];
+ ifreq = ((((uint32_t)f[k+1] & 0x7f) << 8) | (uint32_t)f[k+2]);
+ if (ifreq != 0) {
+ sprintf(desc, "%d KHz", ifreq);
+ }
+ else {
+ sprintf(desc, "Frequency not to be used (0)");
+ }
+ if (RandM == 0x6) {
+ sprintf(tmpbuf, ", DRM Service Id 0x%X", Id_field2);
+ strcat(desc, tmpbuf);
+ }
+ else if (RandM == 0xe) {
+ sprintf(tmpbuf, ", AMSS Service Id 0x%X", Id_field2);
+ strcat(desc, tmpbuf);
+ }
+ if ((f[k+1] & 0x80) == 0x80) {
+ sprintf(tmpbuf, ", invalid Rfu b15 set to 1 instead of 0");
+ strcat(desc, tmpbuf);
+ }
+ printbuf(desc, indent+3, NULL, 0);
+ k += 3;
+ }
+ break;
+ default:
+ break;
+ }
+ j += Length_Freq_list;
+ }
+ i += Length_FI_list;
+ }
+ }
+}
+
diff --git a/fig0_22.cpp b/fig0_22.cpp
new file mode 100644
index 0000000..5c29e8d
--- /dev/null
+++ b/fig0_22.cpp
@@ -0,0 +1,163 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+
+// map for fig 0/22 database
+std::map<uint16_t, Lat_Lng> fig0_22_key_Lat_Lng;
+
+void fig0_22_cleardb()
+{
+ fig0_22_key_Lat_Lng.clear();
+}
+
+// FIG 0/22 Transmitter Identification Information (TII) database
+// ETSI EN 300 401 8.1.9
+void fig0_22(fig0_common_t& fig0, int indent)
+{
+ Lat_Lng gps_pos = {0, 0};
+ double latitude_sub, longitude_sub;
+ int16_t Latitude_coarse, Longitude_coarse;
+ uint16_t key, TD;
+ int16_t Latitude_offset, Longitude_offset;
+ uint8_t i = 1, j, MainId = 0, Rfu, Nb_SubId_fields, SubId;
+ uint8_t Latitude_fine, Longitude_fine;
+ char tmpbuf[256];
+ char desc[256];
+ bool MS;
+ const uint8_t Mode_Identity = get_mode_identity();
+ uint8_t* f = fig0.f;
+
+ while (i < fig0.figlen) {
+ // iterate over Transmitter Identification Information (TII) fields
+ MS = f[i] >> 7;
+ MainId = f[i] & 0x7F;
+ key = (fig0.oe() << 8) | (fig0.pd() << 7) | MainId;
+ sprintf(desc, "M/S=%d %sidentifier, MainId=0x%X",
+ MS, MS?"Sub-":"Main ", MainId);
+ // check MainId value
+ if ((Mode_Identity == 1) || (Mode_Identity == 2) || (Mode_Identity == 4)) {
+ if (MainId > 69) {
+ // The coding range shall be 0 to 69 for transmission modes I, II and IV
+ sprintf(tmpbuf, " invalid value for transmission mode %d", Mode_Identity);
+ strcat(desc, tmpbuf);
+ }
+ }
+ else if (Mode_Identity == 3) {
+ if (MainId > 5) {
+ // The coding range shall be 0 to 5 for transmission modes I, II and IV
+ sprintf(tmpbuf, " invalid value for transmission mode %d", Mode_Identity);
+ strcat(desc, tmpbuf);
+ }
+ }
+ // print database key
+ sprintf(tmpbuf, ", database key=0x%X", key);
+ strcat(desc, tmpbuf);
+ i++;
+ if (MS == 0) {
+ // Main identifier
+
+ if (i < (fig0.figlen - 4)) {
+ Latitude_coarse = (f[i] << 8) | f[i+1];
+ Longitude_coarse = (f[i+2] << 8) | f[i+3];
+ Latitude_fine = f[i+4] >> 4;
+ Longitude_fine = f[i+4] & 0x0F;
+ gps_pos.latitude = (double)((int32_t)((((int32_t)Latitude_coarse) << 4) | (uint32_t)Latitude_fine)) * 90 / 524288;
+ gps_pos.longitude = (double)((int32_t)((((int32_t)Longitude_coarse) << 4) | (uint32_t)Longitude_fine)) * 180 / 524288;
+ fig0_22_key_Lat_Lng[key] = gps_pos;
+ sprintf(tmpbuf, ", Lat Lng coarse=0x%X 0x%X, Lat Lng fine=0x%X 0x%X => Lat Lng=%f, %f",
+ Latitude_coarse, Longitude_coarse, Latitude_fine, Longitude_fine,
+ gps_pos.latitude, gps_pos.longitude);
+ strcat(desc, tmpbuf);
+ i += 5;
+ }
+ else {
+ strcat(desc, ", invalid length of Latitude Longitude coarse fine");
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ }
+ else { // MS == 1
+ // Sub-identifier
+
+ if (i < fig0.figlen) {
+ Rfu = f[i] >> 3;
+ Nb_SubId_fields = f[i] & 0x07;
+ if (Rfu != 0) {
+ sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", Number of SubId fields=%d%s",
+ Nb_SubId_fields, (Nb_SubId_fields == 0)?", CEI":"");
+ strcat(desc, tmpbuf);
+ printbuf(desc, indent+1, NULL, 0);
+ i++;
+
+ for(j = i; ((j < (i + (Nb_SubId_fields * 6))) && (j < (fig0.figlen - 5))); j += 6) {
+ // iterate over SubId fields
+ SubId = f[j] >> 3;
+ sprintf(desc, "SubId=0x%X", SubId);
+ // check SubId value
+ if ((SubId == 0) || (SubId > 23)) {
+ strcat(desc, " invalid value");
+ }
+
+ TD = ((f[j] & 0x03) << 8) | f[j+1];
+ Latitude_offset = (f[j+2] << 8) | f[j+3];
+ Longitude_offset = (f[j+4] << 8) | f[j+5];
+ sprintf(tmpbuf, ", TD=%d us, Lat Lng offset=0x%X 0x%X",
+ TD, Latitude_offset, Longitude_offset);
+ strcat(desc, tmpbuf);
+
+ if (fig0_22_key_Lat_Lng.count(key) > 0) {
+ // latitude longitude available in database for Main Identifier
+ latitude_sub = (90 * (double)Latitude_offset / 524288) + fig0_22_key_Lat_Lng[key].latitude;
+ longitude_sub = (180 * (double)Longitude_offset / 524288) + fig0_22_key_Lat_Lng[key].longitude;
+ sprintf(tmpbuf, " => Lat Lng=%f, %f", latitude_sub, longitude_sub);
+ strcat(desc, tmpbuf);
+ }
+ else {
+ // latitude longitude not available in database for Main Identifier
+ latitude_sub = 90 * (double)Latitude_offset / 524288;
+ longitude_sub = 180 * (double)Longitude_offset / 524288;
+ sprintf(tmpbuf, " => Lat Lng=%f, %f wrong value because Main identifier latitude/longitude not available in database", latitude_sub, longitude_sub);
+ strcat(desc, tmpbuf);
+ }
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ i += (Nb_SubId_fields * 6);
+ }
+ else {
+ strcat(desc, ", invalid fig length or Number of SubId fields length");
+ printbuf(desc, indent+1, NULL, 0);
+ }
+ }
+ }
+}
+
diff --git a/fig0_24.cpp b/fig0_24.cpp
new file mode 100644
index 0000000..03642fb
--- /dev/null
+++ b/fig0_24.cpp
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// FIG 0/24 fig0.oe() Services
+// ETSI EN 300 401 8.1.10.2
+void fig0_24(fig0_common_t& fig0, int indent)
+{
+ uint64_t key;
+ uint32_t SId;
+ uint16_t EId;
+ uint8_t i = 1, j, Number_of_EIds, CAId;
+ char tmpbuf[256];
+ char desc[256];
+ uint8_t* f = fig0.f;
+ bool Rfa;
+
+ while (i < (fig0.figlen - (((uint8_t)fig0.pd() + 1) * 2))) {
+ // iterate over other ensembles services
+ if (fig0.pd() == 0) {
+ SId = ((uint32_t)f[i] << 8) | (uint32_t)f[i+1];
+ i += 2;
+ }
+ else { // fig0.pd() == 1
+ SId = ((uint32_t)f[i] << 24) | ((uint32_t)f[i+1] << 16) |
+ ((uint32_t)f[i+2] << 8) | (uint32_t)f[i+3];
+ i += 4;
+ }
+ Rfa = (f[i] >> 7);
+ CAId = (f[i] >> 4);
+ Number_of_EIds = (f[i] & 0x0f);
+ key = ((uint64_t)fig0.oe() << 33) | ((uint64_t)fig0.pd() << 32) | \
+ (uint64_t)SId;
+ if (fig0.pd() == 0) {
+ sprintf(desc, "SId=0x%X, CAId=%d, Number of EId=%d, database key=%09" PRId64, SId, CAId, Number_of_EIds, key);
+ }
+ else { // fig0.pd() == 1
+ sprintf(desc, "SId=0x%X, CAId=%d, Number of EId=%d, database key=%09" PRId64, SId, CAId, Number_of_EIds, key);
+ }
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ // CEI Change Event Indication
+ if (Number_of_EIds == 0) {
+ sprintf(tmpbuf, ", CEI");
+ strcat(desc, tmpbuf);
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ i++;
+
+ for(j = i; ((j < (i + (Number_of_EIds * 2))) && (j < fig0.figlen)); j += 2) {
+ // iterate over EIds
+ EId = ((uint16_t)f[j] <<8) | (uint16_t)f[j+1];
+ sprintf(desc, "EId 0x%04x", EId);
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ i += (Number_of_EIds * 2);
+ }
+}
+
diff --git a/fig0_25.cpp b/fig0_25.cpp
new file mode 100644
index 0000000..7e6b69b
--- /dev/null
+++ b/fig0_25.cpp
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+
+// FIG 0/25 fig0.oe() Announcement support
+// ETSI EN 300 401 8.1.10.5.1
+void fig0_25(fig0_common_t& fig0, int indent)
+{
+ uint32_t key;
+ uint16_t SId, Asu_flags, EId;
+ uint8_t i = 1, j, Rfu, Number_EIds;
+ char tmpbuf[256];
+ char desc[256];
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 4)) {
+ // iterate over other ensembles announcement support
+ // SId, Asu flags, Rfu, Number of EIds
+ SId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
+ Asu_flags = ((uint16_t)f[i+2] << 8) | (uint16_t)f[i+3];
+ Rfu = (f[i+4] >> 4);
+ Number_EIds = (f[i+4] & 0x0F);
+ sprintf(desc, "SId=0x%X, Asu flags=0x%X", SId, Asu_flags);
+ if (Rfu != 0) {
+ sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", Number of EIds=%d", Number_EIds);
+ strcat(desc, tmpbuf);
+ key = ((uint32_t)fig0.oe() << 17) | ((uint32_t)fig0.pd() << 16) | (uint32_t)SId;
+ sprintf(tmpbuf, ", database key=0x%05x", key);
+ strcat(desc, tmpbuf);
+ // CEI Change Event Indication
+ if (Number_EIds == 0) {
+ sprintf(tmpbuf, ", CEI");
+ strcat(desc, tmpbuf);
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ i += 5;
+
+ for(j = 0; (j < Number_EIds) && (i < (fig0.figlen - 1)); j++) {
+ // iterate over EIds
+ EId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
+ sprintf(desc, "EId=0x%X", EId);
+ printbuf(desc, indent+2, NULL, 0);
+ i += 2;
+ }
+ if (j < Number_EIds) {
+ sprintf(desc, "missing EId, fig length too short !");
+ printbuf(desc, indent+1, NULL, 0);
+ fprintf(stderr, "WARNING: FIG 0/%d length %d too short !\n", fig0.ext(), fig0.figlen);
+ }
+
+ // decode fig0.oe() announcement support types
+ for(j = 0; j < 16; j++) {
+ if (Asu_flags & (1 << j)) {
+ sprintf(desc, "fig0.oe() Announcement support=%s", get_announcement_type(j));
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ }
+ }
+}
+
diff --git a/fig0_26.cpp b/fig0_26.cpp
new file mode 100644
index 0000000..b519ed8
--- /dev/null
+++ b/fig0_26.cpp
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+
+// FIG 0/26 fig0.oe() Announcement switching
+// ETSI EN 300 401 8.1.10.5.2
+void fig0_26(fig0_common_t& fig0, int indent)
+{
+ uint16_t Asw_flags, EId_Other_Ensemble;
+ uint8_t i = 1, j, Rfa, Cluster_Id_Current_Ensemble, Region_Id_Current_Ensemble;
+ uint8_t Cluster_Id_Other_Ensemble, Region_Id_Other_Ensemble;
+ bool New_flag, Region_flag;
+ char tmpbuf[256];
+ char desc[256];
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 6)) {
+ // iterate over other ensembles announcement switching
+ Cluster_Id_Current_Ensemble = f[i];
+ Asw_flags = ((uint16_t)f[i+1] << 8) | (uint16_t)f[i+2];
+ New_flag = f[i+3] >> 7;
+ Region_flag = (f[i+3] >> 6) & 0x01;
+ Region_Id_Current_Ensemble = f[i+3] & 0x3F;
+ EId_Other_Ensemble = ((uint16_t)f[i+4] << 8) | (uint16_t)f[i+5];
+ Cluster_Id_Other_Ensemble = f[i+6];
+ sprintf(desc, "Cluster Id Current Ensemble=0x%X, Asw flags=0x%X, New flag=%d %s announcement, Region flag=%d last byte %s, Region Id Current Ensemble=0x%X, EId Other Ensemble=0x%X, Cluster Id Other Ensemble=0x%X",
+ Cluster_Id_Current_Ensemble, Asw_flags, New_flag, New_flag?"newly introduced":"repeated",
+ Region_flag, Region_flag?"present":"absent. The announcement concerns the whole service area",
+ Region_Id_Current_Ensemble, EId_Other_Ensemble, Cluster_Id_Other_Ensemble);
+ i += 7;
+ if (Region_flag != 0) {
+ if (i < fig0.figlen) {
+ // get Region Id Other Ensemble
+ Rfa = (f[i] >> 6);
+ Region_Id_Other_Ensemble = f[i] & 0x3F;
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", Region Id Other Ensemble=0x%X", Region_Id_Other_Ensemble);
+ strcat(desc, tmpbuf);
+ }
+ else {
+ sprintf(tmpbuf, "missing Region Id Other Ensemble, fig length too short !");
+ strcat(desc, tmpbuf);
+ fprintf(stderr, "WARNING: FIG 0/%d length %d too short !\n", fig0.ext(), fig0.figlen);
+ }
+ i++;
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ // decode announcement switching types
+ for(j = 0; j < 16; j++) {
+ if (Asw_flags & (1 << j)) {
+ sprintf(desc, "Announcement switching=%s", get_announcement_type(j));
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ }
+ }
+}
+
diff --git a/fig0_27.cpp b/fig0_27.cpp
new file mode 100644
index 0000000..6e906b7
--- /dev/null
+++ b/fig0_27.cpp
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+
+// FIG 0/27 FM Announcement support
+// ETSI EN 300 401 8.1.11.2.1
+void fig0_27(fig0_common_t& fig0, int indent)
+{
+ uint16_t SId, PI;
+ uint8_t i = 1, j, Rfu, Number_PI_codes, key;
+ char tmpbuf[256];
+ char desc[256];
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 2)) {
+ // iterate over FM announcement support
+ SId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
+ Rfu = f[i+2] >> 4;
+ Number_PI_codes = f[i+2] & 0x0F;
+ key = (fig0.oe() << 5) | (fig0.pd() << 4) | Number_PI_codes;
+ sprintf(desc, "SId=0x%X", SId);
+ if (Rfu != 0) {
+ sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", Number of PI codes=%d", Number_PI_codes);
+ strcat(desc, tmpbuf);
+ if (Number_PI_codes > 12) {
+ strcat(desc, " above maximum value of 12");
+ fprintf(stderr, "WARNING: FIG 0/%d Number of PI codes=%d > 12 (maximum value)\n", fig0.ext(), Number_PI_codes);
+ }
+ sprintf(tmpbuf, ", database key=0x%02X", key);
+ strcat(desc, tmpbuf);
+ // CEI Change Event Indication
+ if (Number_PI_codes == 0) {
+ // The Change Event Indication (CEI) is signalled by the Number of PI codes field = 0
+ strcat(desc, ", CEI");
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ i += 3;
+ for(j = 0; (j < Number_PI_codes) && (i < (fig0.figlen - 1)); j++) {
+ // iterate over PI
+ PI = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1];
+ sprintf(desc, "PI=0x%X", PI);
+ printbuf(desc, indent+2, NULL, 0);
+ i += 2;
+ }
+ if (j != Number_PI_codes) {
+ sprintf(desc, "fig length too short !");
+ printbuf(desc, indent+2, NULL, 0);
+ fprintf(stderr, "WARNING: FIG 0/%d length %d too short !\n", fig0.ext(), fig0.figlen);
+ }
+ }
+}
+
diff --git a/fig0_28.cpp b/fig0_28.cpp
new file mode 100644
index 0000000..8c727b9
--- /dev/null
+++ b/fig0_28.cpp
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+
+// FIG 0/28 FM Announcement switching
+// ETSI EN 300 401 8.1.11.2.2
+void fig0_28(fig0_common_t& fig0, int indent)
+{
+ uint16_t PI;
+ uint8_t i = 1, Cluster_Id_Current_Ensemble, Region_Id_Current_Ensemble;
+ bool New_flag, Rfa;
+ char tmpbuf[256];
+ char desc[256];
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 3)) {
+ // iterate over FM announcement switching
+ Cluster_Id_Current_Ensemble = f[i];
+ New_flag = f[i+1] >> 7;
+ Rfa = (f[i+1] >> 6) & 0x01;
+ Region_Id_Current_Ensemble = f[i+1] & 0x3F;
+ PI = ((uint16_t)f[i+2] << 8) | (uint16_t)f[i+3];
+ sprintf(desc, "Cluster Id Current Ensemble=0x%X", Cluster_Id_Current_Ensemble);
+ if (Cluster_Id_Current_Ensemble == 0) {
+ strcat(desc, " invalid value");
+ fprintf(stderr, "WARNING: FIG 0/%d Cluster Id Current Ensemble invalid value 0\n", fig0.ext());
+ }
+ sprintf(tmpbuf, ", New flag=%d %s announcement",
+ New_flag, New_flag?"newly introduced":"repeated");
+ strcat(desc, tmpbuf);
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", Region Id Current Ensemble=0x%X, PI=0x%X", Region_Id_Current_Ensemble, PI);
+ strcat(desc, tmpbuf);
+ printbuf(desc, indent+1, NULL, 0);
+ i += 4;
+ }
+}
+
diff --git a/fig0_3.cpp b/fig0_3.cpp
new file mode 100644
index 0000000..71547b6
--- /dev/null
+++ b/fig0_3.cpp
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+
+// FIG 0/3 Service component in packet mode with or without Conditional Access
+// ETSI EN 300 401 6.3.2
+void fig0_3(fig0_common_t& fig0, int indent)
+{
+ uint16_t SCId, Packet_address, CAOrg;
+ uint8_t i = 1, Rfa, DSCTy, SubChId, CAMode, SharedFlag;
+ char tmpbuf[256];
+ char desc[256];
+ bool CAOrg_flag, DG_flag, Rfu;
+
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 4)) {
+ // iterate over service component in packet mode
+ SCId = ((uint16_t)f[i] << 4) | ((uint16_t)(f[i+1] >> 4) & 0x0F);
+ Rfa = (f[i+1] >> 1) & 0x07;
+ CAOrg_flag = f[i+1] & 0x01;
+ DG_flag = (f[i+2] >> 7) & 0x01;
+ Rfu = (f[i+2] >> 6) & 0x01;
+ DSCTy = f[i+2] & 0x3F;
+ SubChId = (f[i+3] >> 2);
+ Packet_address = ((uint16_t)(f[i+3] & 0x03) << 8) | ((uint16_t)f[i+4]);
+ sprintf(desc,
+ "SCId=0x%X, CAOrg flag=%d CAOrg field %s, DG flag=%d"
+ " data groups are %sused to transport the service component,"
+ " DSCTy=%d %s, SubChId=0x%X, Packet address=0x%X",
+ SCId, CAOrg_flag, CAOrg_flag?"present":"absent", DG_flag,
+ DG_flag ? "not ": "",
+ DSCTy, get_dscty_type(DSCTy), SubChId,
+ Packet_address);
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ if (Rfu != 0) {
+ sprintf(tmpbuf, ", Rfu=%d invalid value", Rfu);
+ strcat(desc, tmpbuf);
+ }
+ i += 5;
+ if (CAOrg_flag) {
+ if (i < (fig0.figlen - 1)) {
+ CAOrg = ((uint16_t)f[i] << 8) | ((uint16_t)f[i+1]);
+ CAMode = (f[i] >> 5);
+ SharedFlag = f[i+1];
+ sprintf(tmpbuf, ", CAOrg=0x%X CAMode=%d \"%s\" SharedFlag=0x%X%s",
+ CAOrg, CAMode, get_ca_mode(CAMode), SharedFlag, (SharedFlag == 0) ? " invalid" : "");
+ strcat(desc, tmpbuf);
+ }
+ else {
+ sprintf(tmpbuf, ", invalid figlen");
+ strcat(desc, tmpbuf);
+ }
+ i += 2;
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ }
+}
+
diff --git a/fig0_31.cpp b/fig0_31.cpp
new file mode 100644
index 0000000..e4c3a92
--- /dev/null
+++ b/fig0_31.cpp
@@ -0,0 +1,110 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// FIG 0/31 FIC re-direction
+// ETSI EN 300 401 8.1.12
+void fig0_31(fig0_common_t& fig0, int indent)
+{
+ uint32_t FIG_type0_flag_field = 0, flag_field;
+ uint8_t i = 1, j, FIG_type1_flag_field = 0, FIG_type2_flag_field = 0;
+ char desc[256];
+ uint8_t* f = fig0.f;
+
+ if (i < (fig0.figlen - 5)) {
+ // Read FIC re-direction
+ FIG_type0_flag_field = ((uint32_t)f[i] << 24) | ((uint32_t)f[i+1] << 16) |
+ ((uint32_t)f[i+2] << 8) | (uint32_t)f[i+3];
+ FIG_type1_flag_field = f[i+4];
+ FIG_type2_flag_field = f[i+5];
+
+ sprintf(desc, "FIG type 0 flag field=0x%X, FIG type 1 flag field=0x%X, FIG type 2 flag field=0x%X",
+ FIG_type0_flag_field, FIG_type1_flag_field, FIG_type2_flag_field);
+ printbuf(desc, indent+1, NULL, 0);
+
+ for(j = 0; j < 32; j++) {
+ // iterate over FIG type 0 re-direction
+ flag_field = FIG_type0_flag_field & ((uint32_t)1 << j);
+ if ((flag_field != 0) && ((j <= 5) || (j == 8) ||
+ (j == 10) || (j == 13) || (j == 14) ||
+ (j == 19) || (j == 26) || (j == 28))) {
+ sprintf(desc, "fig0.oe()=%d FIG 0/%d carried in AIC, invalid configuration, shall always be carried entirely in the FIC",
+ fig0.oe(), j);
+ printbuf(desc, indent+2, NULL, 0);
+ fprintf(stderr, "WARNING: FIG 0/%d FIG re-direction of fig0.oe()=%d FIG0/%d not allowed\n", fig0.ext(), fig0.oe(), j);
+ }
+ else if ((flag_field != 0) && ((j == 21) || (j == 24))) {
+ sprintf(desc, "fig0.oe()=%d FIG 0/%d carried in AIC, same shall be carried in FIC", fig0.oe(), j);
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ else if (flag_field != 0) {
+ if (fig0.oe() == 0) {
+ sprintf(desc, "fig0.oe()=%d FIG 0/%d carried in AIC, same shall be carried in FIC", fig0.oe(), j);
+ }
+ else { // fig0.oe() == 1
+ sprintf(desc, "fig0.oe()=%d FIG 0/%d carried in AIC, may be carried entirely in AIC", fig0.oe(), j);
+ }
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ }
+
+ for(j = 0; j < 8; j++) {
+ // iterate over FIG type 1 re-direction
+ flag_field = FIG_type1_flag_field & ((uint32_t)1 << j);
+ if (flag_field != 0) {
+ if (fig0.oe() == 0) {
+ sprintf(desc, "fig0.oe()=%d FIG 1/%d carried in AIC, same shall be carried in FIC", fig0.oe(), j);
+ }
+ else { // fig0.oe() == 1
+ sprintf(desc, "fig0.oe()=%d FIG 1/%d carried in AIC, may be carried entirely in AIC", fig0.oe(), j);
+ }
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ }
+
+ for(j = 0; j < 8; j++) {
+ // iterate over FIG type 2 re-direction
+ flag_field = FIG_type2_flag_field & ((uint32_t)1 << j);
+ if (flag_field != 0) {
+ if (fig0.oe() == 0) {
+ sprintf(desc, "fig0.oe()=%d FIG 2/%d carried in AIC, same shall be carried in FIC", fig0.oe(), j);
+ }
+ else { // fig0.oe() == 1
+ sprintf(desc, "fig0.oe()=%d FIG 2/%d carried in AIC, may be carried entirely in AIC", fig0.oe(), j);
+ }
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ }
+ }
+ if (fig0.figlen != 7) {
+ fprintf(stderr, "WARNING: FIG 0/%d invalid length %d, expecting 7\n", fig0.ext(), fig0.figlen);
+ }
+}
+
diff --git a/fig0_5.cpp b/fig0_5.cpp
new file mode 100644
index 0000000..f3ef08c
--- /dev/null
+++ b/fig0_5.cpp
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// FIG 0/5 Service component language
+// ETSI EN 300 401 8.1.2
+void fig0_5(fig0_common_t& fig0, int indent)
+{
+ uint16_t SCId;
+ uint8_t i = 1, SubChId, FIDCId, Language, Rfa;
+ char tmpbuf[256];
+ char desc[256];
+ bool LS_flag, MSC_FIC_flag;
+
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 1)) {
+ // iterate over service component language
+ LS_flag = f[i] >> 7;
+ if (LS_flag == 0) {
+ // Short form (L/S = 0)
+ MSC_FIC_flag = (f[i] >> 6) & 0x01;
+ Language = f[i+1];
+ if (MSC_FIC_flag == 0) {
+ // 0: MSC in Stream mode and SubChId identifies the sub-channel
+ SubChId = f[i] & 0x3F;
+ sprintf(desc, "L/S flag=%d short form, MSC/FIC flag=%d MSC, SubChId=0x%X, Language=0x%X %s",
+ LS_flag, MSC_FIC_flag, SubChId, Language,
+ get_language_name(Language));
+ }
+ else {
+ // 1: FIC and FIDCId identifies the component
+ FIDCId = f[i] & 0x3F;
+ sprintf(desc, "L/S flag=%d short form, MSC/FIC flag=%d FIC, FIDCId=0x%X, Language=0x%X %s",
+ LS_flag, MSC_FIC_flag, FIDCId, Language,
+ get_language_name(Language));
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ i += 2;
+ }
+ else {
+ // Long form (L/S = 1)
+ if (i < (fig0.figlen - 2)) {
+ Rfa = (f[i] >> 4) & 0x07;
+ SCId = (((uint16_t)f[i] & 0x0F) << 8) | (uint16_t)f[i+1];
+ Language = f[i+2];
+ sprintf(desc, "L/S flag=%d long form", LS_flag);
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", SCId=0x%X, Language=0x%X %s",
+ SCId, Language,
+ get_language_name(Language));
+ strcat(desc, tmpbuf);
+ printbuf(desc, indent+1, NULL, 0);
+ }
+ i += 3;
+ }
+ }
+}
+
diff --git a/fig0_6.cpp b/fig0_6.cpp
new file mode 100644
index 0000000..e863951
--- /dev/null
+++ b/fig0_6.cpp
@@ -0,0 +1,170 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// map between fig 0/6 database key and LA to detect activation and deactivation of links
+static std::map<uint16_t, bool> fig0_6_key_la;
+
+void fig0_6_cleardb()
+{
+ fig0_6_key_la.clear();
+}
+
+// FIG 0/6 Service linking information
+// ETSI EN 300 401 8.1.15
+void fig0_6(fig0_common_t& fig0, int indent)
+{
+ uint32_t j;
+ uint16_t LSN, key;
+ uint8_t i = 1, Number_of_Ids, IdLQ;
+ char signal_link[256];
+ char desc[256];
+ bool Id_list_flag, LA, SH, ILS, Shd;
+
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - 1)) {
+ // iterate over service linking
+ Id_list_flag = (f[i] >> 7) & 0x01;
+ LA = (f[i] >> 6) & 0x01;
+ SH = (f[i] >> 5) & 0x01;
+ ILS = (f[i] >> 4) & 0x01;
+ LSN = ((f[i] & 0x0F) << 8) | f[i+1];
+ key = (fig0.oe() << 15) | (fig0.pd() << 14) | (SH << 13) | (ILS << 12) | LSN;
+ strcpy(signal_link, "");
+ // check activation / deactivation
+ if ((fig0_6_key_la.count(key) > 0) && (fig0_6_key_la[key] != LA)) {
+ if (LA == 0) {
+ strcat(signal_link, " deactivated");
+ }
+ else {
+ strcat(signal_link, " activated");
+ }
+ }
+ fig0_6_key_la[key] = LA;
+ i += 2;
+ if (Id_list_flag == 0) {
+ if (fig0.cn() == 0) { // Id_list_flag=0 && fig0.cn()=0: CEI Change Event Indication
+ strcat(signal_link, " CEI");
+ }
+ sprintf(desc, "Id list flag=%d, LA=%d %s, S/H=%d %s, ILS=%d %s, LSN=%d, database key=0x%04x%s",
+ Id_list_flag, LA, (LA)?"active":"inactive", SH, (SH)?"Hard":"Soft", ILS, (ILS)?"international":"national", LSN, key, signal_link);
+ printbuf(desc, indent+1, NULL, 0);
+ }
+ else { // Id_list_flag == 1
+ if (i < fig0.figlen) {
+ Number_of_Ids = (f[i] & 0x0F);
+ if (fig0.pd() == 0) {
+ IdLQ = (f[i] >> 5) & 0x03;
+ Shd = (f[i] >> 4) & 0x01;
+ sprintf(desc, "Id list flag=%d, LA=%d %s, S/H=%d %s, ILS=%d %s, LSN=%d, database key=0x%04x, IdLQ=%d, Shd=%d %s, Number of Ids=%d%s",
+ Id_list_flag, LA, (LA)?"active":"inactive", SH, (SH)?"Hard":"Soft", ILS, (ILS)?"international":"national", LSN, key, IdLQ, Shd, (Shd)?"b11-8 in 4-F are different services":"single service", Number_of_Ids, signal_link);
+ printbuf(desc, indent+1, NULL, 0);
+ if (ILS == 0) {
+ // read Id list
+ for(j = 0; ((j < Number_of_Ids) && ((i+2+(j*2)) < fig0.figlen)); j++) {
+ // ETSI EN 300 401 8.1.15:
+ // The IdLQ shall not apply to the first entry in the Id list when fig0.oe() = "0" and
+ // when the version number of the type 0 field is set to "0" (using the C/N flag, see clause 5.2.2.1)
+ // ... , the first entry in the Id list of each Service linking field shall be
+ // the SId that applies to the service in the ensemble.
+ if (((j == 0) && (fig0.oe() == 0) && (fig0.cn() == 0)) ||
+ (IdLQ == 0)) {
+ sprintf(desc, "DAB SId 0x%X", ((f[i+1+(j*2)] << 8) | f[i+2+(j*2)]));
+ }
+ else if (IdLQ == 1) {
+ sprintf(desc, "RDS PI 0x%X", ((f[i+1+(j*2)] << 8) | f[i+2+(j*2)]));
+ }
+ else if (IdLQ == 2) {
+ sprintf(desc, "AM-FM service 0x%X", ((f[i+1+(j*2)] << 8) | f[i+2+(j*2)]));
+ }
+ else { // IdLQ == 3
+ sprintf(desc, "invalid ILS IdLQ configuration");
+ }
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ // check deadlink
+ if ((Number_of_Ids == 0) && (IdLQ == 1)) {
+ sprintf(desc, "deadlink");
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ i += (Number_of_Ids * 2) + 1;
+ }
+ else { // fig0.pd() == 0 && ILS == 1
+ // read Id list
+ for(j = 0; ((j < Number_of_Ids) && ((i+3+(j*3)) < fig0.figlen)); j++) {
+ // ETSI EN 300 401 8.1.15:
+ // The IdLQ shall not apply to the first entry in the Id list when fig0.oe() = "0" and
+ // when the version number of the type 0 field is set to "0" (using the C/N flag, see clause 5.2.2.1)
+ // ... , the first entry in the Id list of each Service linking field shall be
+ // the SId that applies to the service in the ensemble.
+ if (((j == 0) && (fig0.oe() == 0) && (fig0.cn() == 0)) ||
+ (IdLQ == 0)) {
+ sprintf(desc, "DAB SId ecc 0x%02X Id 0x%04X", f[i+1+(j*3)], ((f[i+2+(j*3)] << 8) | f[i+3+(j*3)]));
+ }
+ else if (IdLQ == 1) {
+ sprintf(desc, "RDS PI ecc 0x%02X Id 0x%04X", f[i+1+(j*3)], ((f[i+2+(j*3)] << 8) | f[i+3+(j*3)]));
+ }
+ else if (IdLQ == 2) {
+ sprintf(desc, "AM-FM service ecc 0x%02X Id 0x%04X", f[i+1+(j*3)], ((f[i+2+(j*3)] << 8) | f[i+3+(j*3)]));
+ }
+ else { // IdLQ == 3
+ sprintf(desc, "DRM/AMSS service ecc 0x%02X Id 0x%04X", f[i+1+(j*3)], ((f[i+2+(j*3)] << 8) | f[i+3+(j*3)]));
+ }
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ // check deadlink
+ if ((Number_of_Ids == 0) && (IdLQ == 1)) {
+ sprintf(desc, "deadlink");
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ i += (Number_of_Ids * 3) + 1;
+ }
+ }
+ else { // fig0.pd() == 1
+ sprintf(desc, "Id list flag=%d, LA=%d %s, S/H=%d %s, ILS=%d %s, LSN=%d, database key=0x%04x, Number of Ids=%d%s",
+ Id_list_flag, LA, (LA)?"active":"inactive", SH, (SH)?"Hard":"Soft", ILS, (ILS)?"international":"national", LSN, key, Number_of_Ids, signal_link);
+ printbuf(desc, indent+1, NULL, 0);
+ if (Number_of_Ids > 0) {
+ // read Id list
+ for(j = 0; ((j < Number_of_Ids) && ((i+4+(j*4)) < fig0.figlen)); j++) {
+ sprintf(desc, "SId 0x%X",
+ ((f[i+1+(j*4)] << 24) | (f[i+2+(j*4)] << 16) | (f[i+3+(j*4)] << 8) | f[i+4+(j*4)]));
+ printbuf(desc, indent+2, NULL, 0);
+ }
+ }
+ i += (Number_of_Ids * 4) + 1;
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/fig0_8.cpp b/fig0_8.cpp
new file mode 100644
index 0000000..7143768
--- /dev/null
+++ b/fig0_8.cpp
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// FIG 0/8 Service component global definition
+// ETSI EN 300 401 6.3.5
+void fig0_8(fig0_common_t& fig0, int indent)
+{
+ uint32_t SId;
+ uint16_t SCId;
+ uint8_t i = 1, Rfa, SCIdS, SubChId, FIDCId;
+ char tmpbuf[256];
+ char desc[256];
+ bool Ext_flag, LS_flag, MSC_FIC_flag;
+ uint8_t* f = fig0.f;
+
+ while (i < (fig0.figlen - (2 + (2 * fig0.pd())))) {
+ // iterate over service component global definition
+ if (fig0.pd() == 0) {
+ // Programme services, 16 bit SId
+ SId = (f[i] << 8) | f[i+1];
+ i += 2;
+ }
+ else {
+ // Data services, 32 bit SId
+ SId = ((uint32_t)f[i] << 24) | ((uint32_t)f[i+1] << 16) |
+ ((uint32_t)f[i+2] << 8) | (uint32_t)f[i+3];
+ i += 4;
+ }
+ Ext_flag = f[i] >> 7;
+ Rfa = (f[i] >> 4) & 0x7;
+ SCIdS = f[i] & 0x0F;
+ sprintf(desc, "SId=0x%X, fig0.ext() flag=%d 8-bit Rfa %s", SId, Ext_flag, (Ext_flag)?"present":"absent");
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", SCIdS=0x%X", SCIdS);
+ strcat(desc, tmpbuf);
+ i++;
+ if (i < fig0.figlen) {
+ LS_flag = f[i] >> 7;
+ sprintf(tmpbuf, ", L/S flag=%d %s", LS_flag, (LS_flag)?"Long form":"Short form");
+ strcat(desc, tmpbuf);
+ if (LS_flag == 0) {
+ // Short form
+ if (i < (fig0.figlen - Ext_flag)) {
+ MSC_FIC_flag = (f[i] >> 6) & 0x01;
+ if (MSC_FIC_flag == 0) {
+ // MSC in stream mode and SubChId identifies the sub-channel
+ SubChId = f[i] & 0x3F;
+ sprintf(tmpbuf, ", MSC/FIC flag=%d MSC, SubChId=0x%X", MSC_FIC_flag, SubChId);
+ strcat(desc, tmpbuf);
+ }
+ else {
+ // FIC and FIDCId identifies the component
+ FIDCId = f[i] & 0x3F;
+ sprintf(tmpbuf, ", MSC/FIC flag=%d FIC, FIDCId=0x%X", MSC_FIC_flag, FIDCId);
+ strcat(desc, tmpbuf);
+ }
+ if (Ext_flag == 1) {
+ // Rfa field present
+ Rfa = f[i+1];
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=0x%X invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ }
+ }
+ i += (1 + Ext_flag);
+ }
+ else {
+ // Long form
+ if (i < (fig0.figlen - 1)) {
+ Rfa = (f[i] >> 4) & 0x07;
+ SCId = (((uint16_t)f[i] & 0x0F) << 8) | (uint16_t)f[i+1];
+ if (Rfa != 0) {
+ sprintf(tmpbuf, ", Rfa=%d invalid value", Rfa);
+ strcat(desc, tmpbuf);
+ }
+ sprintf(tmpbuf, ", SCId=0x%X", SCId);
+ strcat(desc, tmpbuf);
+ }
+ i += 2;
+ }
+ }
+ printbuf(desc, indent+1, NULL, 0);
+ }
+}
+
diff --git a/fig0_9.cpp b/fig0_9.cpp
new file mode 100644
index 0000000..9fa4562
--- /dev/null
+++ b/fig0_9.cpp
@@ -0,0 +1,131 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <cstdio>
+#include <cstring>
+#include <map>
+
+// fig 0/9 global variables
+uint8_t Ensemble_ECC=0;
+int8_t Ensemble_LTO=0;
+bool LTO_uniq;
+
+
+// FIG 0/9 Country, LTO and International table
+// ETSI EN 300 401 8.1.3.2
+void fig0_9(fig0_common_t& fig0, int indent)
+{
+ uint32_t SId;
+ uint8_t i = 1, j, key, Number_of_services, ECC;
+ int8_t LTO;
+ char tmpbuf[256];
+ char desc[256];
+ bool Ext_flag;
+ uint8_t* f = fig0.f;
+
+ if (i < (fig0.figlen - 2)) {
+ // get Ensemble LTO, ECC and International Table Id
+ key = ((uint8_t)fig0.oe() << 1) | (uint8_t)fig0.pd();
+ Ext_flag = f[i] >> 7;
+ LTO_uniq = (f[i]>> 6) & 0x01;
+ Ensemble_LTO = f[i] & 0x3F;
+ if (Ensemble_LTO & 0x20) {
+ // negative Ensemble LTO
+ Ensemble_LTO |= 0xC0;
+ }
+ sprintf(desc, "fig0.ext() flag=%d extended field %s, LTO uniq=%d %s, Ensemble LTO=0x%X %s%d:%02d",
+ Ext_flag, Ext_flag?"present":"absent", LTO_uniq,
+ LTO_uniq?"several time zones":"one time zone (time specified by Ensemble LTO)",
+ (Ensemble_LTO & 0x3F), (Ensemble_LTO >= 0)?"":"-" , abs(Ensemble_LTO) >> 1, (Ensemble_LTO & 0x01) * 30);
+ if (abs(Ensemble_LTO) > 24) {
+ sprintf(tmpbuf, " out of range -12 hours to +12 hours");
+ strcat(desc, tmpbuf);
+ }
+ Ensemble_ECC = f[i+1];
+ uint8_t International_Table_Id = f[i+2];
+ set_international_table(International_Table_Id);
+ sprintf(tmpbuf, ", Ensemble ECC=0x%X, International Table Id=0x%X, database key=0x%x",
+ Ensemble_ECC, International_Table_Id, key);
+ strcat(desc, tmpbuf);
+ printbuf(desc, indent+1, NULL, 0);
+ i += 3;
+ if (Ext_flag == 1) {
+ // extended field present
+ while (i < fig0.figlen) {
+ // iterate over extended sub-field
+ Number_of_services = f[i] >> 6;
+ LTO = f[i] & 0x3F;
+ if (LTO & 0x20) {
+ // negative LTO
+ LTO |= 0xC0;
+ }
+ sprintf(desc, "Number of services=%d, LTO=0x%X %s%d:%02d",
+ Number_of_services, (LTO & 0x3F), (LTO >= 0)?"":"-" , abs(LTO) >> 1, (LTO & 0x01) * 30);
+ if (abs(LTO) > 24) {
+ sprintf(tmpbuf, " out of range -12 hours to +12 hours");
+ strcat(desc, tmpbuf);
+ }
+ // CEI Change Event Indication
+ if ((Number_of_services == 0) && (LTO == 0) /* && (Ext_flag == 1) */) {
+ sprintf(tmpbuf, ", CEI");
+ strcat(desc, tmpbuf);
+ }
+ i++;
+ if (fig0.pd() == 0) {
+ // Programme services, 16 bit SId
+ if (i < fig0.figlen) {
+ ECC = f[i];
+ sprintf(tmpbuf, ", ECC=0x%X", ECC);
+ strcat(desc, tmpbuf);
+ printbuf(desc, indent+2, NULL, 0);
+ i++;
+ for(j = i; ((j < (i + (Number_of_services * 2))) && (j < fig0.figlen)); j += 2) {
+ // iterate over SId
+ SId = ((uint32_t)f[j] << 8) | (uint32_t)f[j+1];
+ sprintf(desc, "SId 0x%X", SId);
+ printbuf(desc, indent+3, NULL, 0);
+ }
+ i += (Number_of_services * 2);
+ }
+ }
+ else {
+ // Data services, 32 bit SId
+ printbuf(desc, indent+2, NULL, 0);
+ for(j = i; ((j < (i + (Number_of_services * 4))) && (j < fig0.figlen)); j += 4) {
+ // iterate over SId
+ SId = ((uint32_t)f[j] << 24) | ((uint32_t)f[j+1] << 16) |
+ ((uint32_t)f[j+2] << 8) | (uint32_t)f[j+3];
+ sprintf(desc, "SId 0x%X", SId);
+ printbuf(desc, indent+3, NULL, 0);
+ }
+ i += (Number_of_services * 4);
+ }
+ }
+ }
+ }
+}
+
diff --git a/figs.cpp b/figs.cpp
new file mode 100644
index 0000000..4e7e94c
--- /dev/null
+++ b/figs.cpp
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "figs.hpp"
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <sstream>
+#include <time.h>
+#include "utils.hpp"
+
+
+static uint8_t Mode_Identity = 0;
+
+void set_mode_identity(uint8_t mid)
+{
+ Mode_Identity = mid;
+}
+
+uint8_t get_mode_identity()
+{
+ return Mode_Identity;
+}
+
+static size_t International_Table_Id;
+
+size_t get_international_table(void)
+{
+ return International_Table_Id;
+}
+
+void set_international_table(size_t intl_table)
+{
+ International_Table_Id = intl_table;
+}
+
+
+void figs_cleardb()
+{
+ // remove elements from fig0_6_key_la and fig0_22_key_Lat_Lng map containers
+ fig0_22_cleardb();
+ fig0_6_cleardb();
+}
+
+
+void fig0_select(fig0_common_t& fig0, int indent)
+{
+ switch (fig0.ext()) {
+ case 0: fig0_0(fig0, indent); break;
+ case 1: fig0_1(fig0, indent); break;
+ case 2: fig0_2(fig0, indent); break;
+ case 3: fig0_3(fig0, indent); break;
+ case 5: fig0_5(fig0, indent); break;
+ case 6: fig0_6(fig0, indent); break;
+ case 8: fig0_8(fig0, indent); break;
+ case 9: fig0_9(fig0, indent); break;
+ case 10: fig0_10(fig0, indent); break;
+ case 11: fig0_11(fig0, indent); break;
+ case 13: fig0_13(fig0, indent); break;
+ case 14: fig0_14(fig0, indent); break;
+ case 16: fig0_16(fig0, indent); break;
+ case 17: fig0_17(fig0, indent); break;
+ case 18: fig0_18(fig0, indent); break;
+ case 19: fig0_19(fig0, indent); break;
+ case 21: fig0_21(fig0, indent); break;
+ case 22: fig0_22(fig0, indent); break;
+ case 24: fig0_24(fig0, indent); break;
+ case 25: fig0_25(fig0, indent); break;
+ case 26: fig0_26(fig0, indent); break;
+ case 27: fig0_27(fig0, indent); break;
+ case 28: fig0_28(fig0, indent); break;
+ case 31: fig0_31(fig0, indent); break;
+ default: {
+ char desc[256];
+ sprintf(desc, "FIG 0/%d: unknown", fig0.ext());
+ printbuf(desc, indent, fig0.f+1, fig0.figlen-1);
+ break;
+ }
+ }
+}
+
diff --git a/figs.hpp b/figs.hpp
new file mode 100644
index 0000000..0e2efb8
--- /dev/null
+++ b/figs.hpp
@@ -0,0 +1,96 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#pragma once
+
+#include <cstdint>
+#include "utils.hpp"
+#include "tables.hpp"
+#include "watermarkdecoder.hpp"
+
+void figs_cleardb(void);
+
+struct fig0_common_t {
+ fig0_common_t(
+ uint8_t* fig_data,
+ uint16_t fig_len,
+ WatermarkDecoder &wm_dec) :
+ f(fig_data),
+ figlen(fig_len),
+ wm_decoder(wm_dec) { }
+
+ uint8_t* f;
+ uint16_t figlen;
+ WatermarkDecoder &wm_decoder;
+
+ uint16_t cn(void) { return (f[0] & 0x80) >> 7; }
+ uint16_t oe(void) { return (f[0] & 0x40) >> 6; }
+ uint16_t pd(void) { return (f[0] & 0x20) >> 5; }
+ uint16_t ext(void) { return f[0] & 0x1F; }
+};
+
+// FIG 0/11 and 0/22 struct
+struct Lat_Lng {
+ double latitude, longitude;
+};
+
+// Which international table has been chosen
+size_t get_international_table(void);
+void set_international_table(size_t intl_table);
+
+// MID is used by some FIGs. It is signalled in LIDATA - FC - MID
+void set_mode_identity(uint8_t mid);
+uint8_t get_mode_identity();
+
+void fig0_select(fig0_common_t& fig0, int indent);
+
+void fig0_0(fig0_common_t& fig0, int indent);
+void fig0_1(fig0_common_t& fig0, int indent);
+void fig0_2(fig0_common_t& fig0, int indent);
+void fig0_3(fig0_common_t& fig0, int indent);
+void fig0_5(fig0_common_t& fig0, int indent);
+void fig0_6_cleardb();
+void fig0_6(fig0_common_t& fig0, int indent);
+void fig0_8(fig0_common_t& fig0, int indent);
+void fig0_9(fig0_common_t& fig0, int indent);
+void fig0_10(fig0_common_t& fig0, int indent);
+void fig0_11(fig0_common_t& fig0, int indent);
+void fig0_13(fig0_common_t& fig0, int indent);
+void fig0_14(fig0_common_t& fig0, int indent);
+void fig0_16(fig0_common_t& fig0, int indent);
+void fig0_17(fig0_common_t& fig0, int indent);
+void fig0_18(fig0_common_t& fig0, int indent);
+void fig0_19(fig0_common_t& fig0, int indent);
+void fig0_21(fig0_common_t& fig0, int indent);
+void fig0_22_cleardb();
+void fig0_22(fig0_common_t& fig0, int indent);
+void fig0_24(fig0_common_t& fig0, int indent);
+void fig0_25(fig0_common_t& fig0, int indent);
+void fig0_26(fig0_common_t& fig0, int indent);
+void fig0_27(fig0_common_t& fig0, int indent);
+void fig0_28(fig0_common_t& fig0, int indent);
+void fig0_31(fig0_common_t& fig0, int indent);
+
diff --git a/tables.cpp b/tables.cpp
new file mode 100644
index 0000000..ed3ecbe
--- /dev/null
+++ b/tables.cpp
@@ -0,0 +1,311 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "tables.hpp"
+#include <string>
+#include <vector>
+#include <stdexcept>
+
+static const std::vector<const char*> language_names = {
+ "Unknown/not applicable",
+ "Albanian",
+ "Breton",
+ "Catalan",
+ "Croatian",
+ "Welsh",
+ "Czech",
+ "Danish",
+ "German",
+ "English",
+ "Spanish",
+ "Esperanto",
+ "Estonian",
+ "Basque",
+ "Faroese",
+ "French",
+ "Frisian",
+ "Irish",
+ "Gaelic",
+ "Galician",
+ "Icelandic",
+ "Italian",
+ "Lappish",
+ "Latin",
+ "Latvian",
+ "Luxembourgian",
+ "Lithuanian",
+ "Hungarian",
+ "Maltese",
+ "Dutch",
+ "Norwegian",
+ "Occitan",
+ "Polish",
+ "Portuguese",
+ "Romanian",
+ "Romansh",
+ "Serbian",
+ "Slovak",
+ "Slovene",
+ "Finnish",
+ "Swedish",
+ "Turkish",
+ "Flemish",
+ "Walloon",
+ "rfu",
+ "rfu",
+ "rfu",
+ "rfu",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Reserved for national assignment",
+ "Background sound/clean feed",
+ "rfu",
+ "rfu",
+ "rfu",
+ "rfu",
+ "Zulu",
+ "Vietnamese",
+ "Uzbek",
+ "Urdu",
+ "Ukranian",
+ "Thai",
+ "Telugu",
+ "Tatar",
+ "Tamil",
+ "Tadzhik",
+ "Swahili",
+ "Sranan Tongo",
+ "Somali",
+ "Sinhalese",
+ "Shona",
+ "Serbo-Croat",
+ "Rusyn",
+ "Russian",
+ "Quechua",
+ "Pushtu",
+ "Punjabi",
+ "Persian",
+ "Papiamento",
+ "Oriya",
+ "Nepali",
+ "Ndebele",
+ "Marathi",
+ "Moldavian",
+ "Malaysian",
+ "Malagasay",
+ "Macedonian",
+ "Laotian",
+ "Korean",
+ "Khmer",
+ "Kazakh",
+ "Kannada",
+ "Japanese",
+ "Indonesian",
+ "Hindi",
+ "Hebrew",
+ "Hausa",
+ "Gurani",
+ "Gujurati",
+ "Greek",
+ "Georgian",
+ "Fulani",
+ "Dari",
+ "Chuvash",
+ "Chinese",
+ "Burmese",
+ "Bulgarian",
+ "Bengali",
+ "Belorussian",
+ "Bambora",
+ "Azerbaijani",
+ "Assamese",
+ "Armenian",
+ "Arabic",
+ "Amharic",
+};
+
+const char* get_language_name(size_t language_code)
+{
+ if (language_code < language_names.size())
+ {
+ return language_names[language_code];
+ }
+
+ throw std::runtime_error("Invalid language_code!");
+}
+
+// fig 0/18 0/19 announcement types (ETSI TS 101 756 V1.6.1 (2014-05) table 14 & 15)
+const char *announcement_types_str[16] = {
+ "Alarm",
+ "Road Traffic flash",
+ "Transport flash",
+ "Warning/Service",
+ "News flash",
+ "Area weather flash",
+ "Event announcement",
+ "Special event",
+ "Programme Information",
+ "Sport report",
+ "Financial report",
+ "Reserved for future definition",
+ "Reserved for future definition",
+ "Reserved for future definition",
+ "Reserved for future definition",
+ "Reserved for future definition"
+};
+
+const char* get_announcement_type(size_t announcement_type)
+{
+ return announcement_types_str[announcement_type];
+}
+
+
+// fig 0/17 Programme type codes
+#define INTERNATIONAL_TABLE_SIZE 2
+#define PROGRAMME_TYPE_CODES_SIZE 32
+const char *Programme_type_codes_str[INTERNATIONAL_TABLE_SIZE][PROGRAMME_TYPE_CODES_SIZE] = {
+ { // ETSI TS 101 756 V1.6.1 (2014-05) table 12
+ "No programme type", "News",
+ "Current Affairs", "Information",
+ "Sport", "Education",
+ "Drama", "Culture",
+ "Science", "Varied",
+ "Pop Music", "Rock Music",
+ "Easy Listening Music", "Light Classical",
+ "Serious Classical", "Other Music",
+ "Weather/meteorology", "Finance/Business",
+ "Children's programmes", "Social Affairs",
+ "Religion", "Phone In",
+ "Travel", "Leisure",
+ "Jazz Music", "Country Music",
+ "National Music", "Oldies Music",
+ "Folk Music", "Documentary",
+ "Not used", "Not used"
+ },
+ { // ETSI TS 101 756 V1.6.1 (2014-05) table 13
+ "No program type", "News",
+ "Information", "Sports",
+ "Talk", "Rock",
+ "Classic Rock", "Adult Hits",
+ "Soft Rock", "Top 40",
+ "Country", "Oldies",
+ "Soft", "Nostalgia",
+ "Jazz", "Classical",
+ "Rhythm and Blues", "Soft Rhythm and Blues",
+ "Foreign Language", "Religious Music",
+ "Religious Talk", "Personality",
+ "Public", "College",
+ "rfu", "rfu",
+ "rfu", "rfu",
+ "rfu", "Weather",
+ "Not used", "Not used"
+ }
+};
+
+const char* get_programme_type(size_t int_table_Id, size_t pty)
+{
+ if ((int_table_Id - 1) < INTERNATIONAL_TABLE_SIZE) {
+ if (pty < PROGRAMME_TYPE_CODES_SIZE) {
+ return Programme_type_codes_str[int_table_Id - 1][pty];
+ }
+ else {
+ return "invalid programme type";
+ }
+ }
+ else {
+ return "unknown international table Id";
+ }
+}
+
+
+const char *DSCTy_types_str[64] = {
+ // ETSI TS 101 756 V1.6.1 (2014-05) table 2
+ "Unspecified data", "Traffic Message Channel (TMC)",
+ "Emergency Warning System (EWS)", "Interactive Text Transmission System (ITTS)",
+ "Paging", "Transparent Data Channel (TDC)",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "MPEG-2 Transport Stream, see ETSI TS 102 427", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Rfu",
+ "Rfu", "Embedded IP packets",
+ "Multimedia Object Transfer (MOT)", "Proprietary service: no DSCTy signalled",
+ "Not used", "Not used"
+};
+
+const char* get_dscty_type(size_t dscty)
+{
+ return DSCTy_types_str[dscty];
+}
+
+// ETSI TS 102 367 V1.2.1 (2006-01) 5.4.1 Conditional Access Mode (CAMode)
+// Used in FIG 0/3
+const char *CAMode_str[8] = {
+ "Sub-channel CA", "Data Group CA",
+ "MOT CA", "proprietary CA",
+ "reserved", "reserved",
+ "reserved", "reserved"
+};
+
+const char* get_ca_mode(size_t ca_mode)
+{
+ return CAMode_str[ca_mode];
+}
+
diff --git a/tables.hpp b/tables.hpp
new file mode 100644
index 0000000..992aabd
--- /dev/null
+++ b/tables.hpp
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#pragma once
+
+#include "utils.hpp"
+
+const char* get_language_name(size_t language_code);
+const char* get_announcement_type(size_t announcement_type);
+
+/* get_programme_type_str return the programme type string from international
+ * table Id and programme type
+ */
+const char* get_programme_type(size_t int_table_Id, size_t pty);
+
+// fig 0/2 fig 0/3 DSCTy types string:
+const char* get_dscty_type(size_t dscty);
+
+const char* get_ca_mode(size_t ca_mode);
+
diff --git a/utils.cpp b/utils.cpp
new file mode 100644
index 0000000..71df396
--- /dev/null
+++ b/utils.cpp
@@ -0,0 +1,154 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ etisnoop.cpp
+ Parse ETI NI G.703 file
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#include "utils.hpp"
+#include <cstring>
+
+using namespace std;
+
+static int verbosity = 0;
+
+void set_verbosity(int v)
+{
+ verbosity = v;
+}
+
+int get_verbosity()
+{
+ return verbosity;
+}
+
+void printbuf(string header,
+ int indent_level,
+ uint8_t* buffer,
+ size_t size,
+ string desc)
+{
+ if (verbosity > 0) {
+ for (int i = 0; i < indent_level; i++) {
+ printf("\t");
+ }
+
+ printf("%s", header.c_str());
+
+ if (verbosity > 1) {
+ if (size != 0) {
+ printf(": ");
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ printf("%02x ", buffer[i]);
+ }
+ }
+
+ if (desc != "") {
+ printf(" [%s] ", desc.c_str());
+ }
+
+ printf("\n");
+ }
+}
+
+void printinfo(string header,
+ int indent_level,
+ int min_verb)
+{
+ if (verbosity >= min_verb) {
+ for (int i = 0; i < indent_level; i++) {
+ printf("\t");
+ }
+ printf("%s\n", header.c_str());
+ }
+}
+
+int sprintfMJD(char *dst, int mjd) {
+ // EN 62106 Annex G
+ // These formulas are applicable between the inclusive dates: 1st March 1900 to 28th February 2100
+ int y, m, k;
+ struct tm timeDate;
+
+ memset(&timeDate, 0, sizeof(struct tm));
+
+ // find Y, M, D from MJD
+ y = (int)(((double)mjd - 15078.2) / 365.25);
+ m = (int)(((double)mjd - 14956.1 - (int)((double)y * 365.25)) / 30.6001);
+ timeDate.tm_mday = mjd - 14956 - (int)((double)y * 365.25) - (int)((double)m * 30.6001);
+ if ((m == 14) || (m == 15)) {
+ k = 1;
+ }
+ else {
+ k = 0;
+ }
+ timeDate.tm_year = y + k;
+ timeDate.tm_mon = (m - 1 - (k * 12)) - 1;
+
+ // find WD from MJD
+ timeDate.tm_wday = (((mjd + 2) % 7) + 1) % 7;
+
+ //timeDate.tm_yday = 0; // Number of days since the first day of January not calculated
+ timeDate.tm_isdst = -1; // No time print then information not available
+
+ // print date string
+ if ((timeDate.tm_mday < 0) || (timeDate.tm_mon < 0) || (timeDate.tm_year < 0)) {
+ return sprintf(dst, "invalid MJD mday=%d mon=%d year=%d", timeDate.tm_mday, timeDate.tm_mon, timeDate.tm_year);
+ }
+ return strftime(dst, 256, "%a %b %d %Y", &timeDate);
+}
+
+char *strcatPNum(char *dest_str, uint16_t Programme_Number) {
+ uint8_t day, hour, minute;
+ char tempbuf[256];
+
+ minute = (uint8_t)(Programme_Number & 0x003F);
+ hour = (uint8_t)((Programme_Number >> 6) & 0x001F);
+ day = (uint8_t)((Programme_Number >> 11) & 0x001F);
+ if (day != 0) {
+ sprintf(tempbuf, "day of month=%d time=%02d:%02d", day, hour, minute);
+ }
+ else { // day == 0
+ // Special codes are allowed when the date part of the PNum field
+ // signals date = "0". In this case, the hours and minutes part of
+ // the field shall contain a special code, as follows
+ if ((hour == 0) && (minute == 0)) {
+ sprintf(tempbuf, "Status code: no meaningful PNum is currently provided");
+ }
+ else if ((hour == 0) && (minute == 1)) {
+ sprintf(tempbuf, "Blank code: the current programme is not worth recording");
+ }
+ else if ((hour == 0) && (minute == 2)) {
+ sprintf(tempbuf, "Interrupt code: the interrupt is unplanned (for example a traffic announcement)");
+ }
+ else {
+ sprintf(tempbuf, "invalid value");
+ }
+ }
+ return strcat(dest_str, tempbuf);
+}
+
+
diff --git a/utils.hpp b/utils.hpp
new file mode 100644
index 0000000..3913f87
--- /dev/null
+++ b/utils.hpp
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ etisnoop.cpp
+ Parse ETI NI G.703 file
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#pragma once
+
+#include <string>
+#include <cstdint>
+#include <inttypes.h>
+
+void set_verbosity(int v);
+int get_verbosity(void);
+
+void printbuf(std::string header,
+ int indent_level,
+ uint8_t* buffer,
+ size_t size,
+ std::string desc="");
+
+void printinfo(std::string header,
+ int indent_level,
+ int min_verb = 0);
+
+// sprintfMJD: convert MJD (Modified Julian Date) into date string
+int sprintfMJD(char *dst, int mjd);
+
+// strcatPNum decode Programme_Number into string and append it to dest_str
+// Programme_Number: this 16-bit field shall define the date and time at which
+// a programme begins or will be continued. This field is coded in the same way
+// as the RDS "Programme Item Number (PIN)" feature (EN 62106).
+char *strcatPNum(char *dest_str, uint16_t Programme_Number);
+
diff --git a/watermarkdecoder.hpp b/watermarkdecoder.hpp
new file mode 100644
index 0000000..57196e1
--- /dev/null
+++ b/watermarkdecoder.hpp
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2016 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/>.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#pragma once
+#include <vector>
+#include <sstream>
+#include "utils.hpp"
+
+class WatermarkDecoder
+{
+ public:
+ WatermarkDecoder() {}
+
+ void push_confind_bit(bool confind)
+ {
+ // The ConfInd of FIG 0/10 contains the CRC-DABMUX and ODR-DabMux watermark
+ m_confind_bits.push_back(confind);
+ }
+
+ std::string calculate_watermark()
+ {
+ // First try to find the 0x55 0x55 sync in the waternark data
+ size_t bit_ix;
+ int alternance_count = 0;
+ bool last_bit = 1;
+ for (bit_ix = 0; bit_ix < m_confind_bits.size(); bit_ix++) {
+ if (alternance_count == 16) {
+ break;
+ }
+ else {
+ if (last_bit != m_confind_bits[bit_ix]) {
+ last_bit = m_confind_bits[bit_ix];
+ alternance_count++;
+ }
+ else {
+ alternance_count = 0;
+ last_bit = 1;
+ }
+ }
+
+ }
+
+ printf("Found SYNC at offset %zu out of %zu\n", bit_ix - alternance_count, m_confind_bits.size());
+
+ std::stringstream watermark_ss;
+
+ uint8_t b = 0;
+ size_t i = 0;
+ while (bit_ix < m_confind_bits.size()) {
+
+ b |= m_confind_bits[bit_ix] << (7 - i);
+
+ if (i == 7) {
+ watermark_ss << (char)b;
+
+ b = 0;
+ i = 0;
+ }
+ else {
+ i++;
+ }
+
+ bit_ix += 2;
+ }
+
+ return watermark_ss.str();
+ }
+
+ private:
+ const WatermarkDecoder& operator=(const WatermarkDecoder&) = delete;
+ WatermarkDecoder(const WatermarkDecoder&) = delete;
+
+ std::vector<bool> m_confind_bits;
+};
+