diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2016-01-15 15:03:38 +0100 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2016-01-15 15:03:38 +0100 |
commit | 3576bfcd68ff58e022efc07ef8e1f1b757ccf6fb (patch) | |
tree | dd8b4ae93ce1b87260794022f56cf8892958692a | |
parent | 275b55e39a84a3877e7e68b7c800bd0de28c128c (diff) | |
download | etisnoop-3576bfcd68ff58e022efc07ef8e1f1b757ccf6fb.tar.gz etisnoop-3576bfcd68ff58e022efc07ef8e1f1b757ccf6fb.tar.bz2 etisnoop-3576bfcd68ff58e022efc07ef8e1f1b757ccf6fb.zip |
Refactor all FIG0/x into separate files
-rw-r--r-- | CMakeLists.txt | 35 | ||||
-rw-r--r-- | Makefile | 24 | ||||
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | etisnoop.cpp | 2331 | ||||
-rw-r--r-- | fig0_0.cpp | 60 | ||||
-rw-r--r-- | fig0_1.cpp | 91 | ||||
-rw-r--r-- | fig0_10.cpp | 72 | ||||
-rw-r--r-- | fig0_11.cpp | 185 | ||||
-rw-r--r-- | fig0_13.cpp | 101 | ||||
-rw-r--r-- | fig0_14.cpp | 59 | ||||
-rw-r--r-- | fig0_16.cpp | 100 | ||||
-rw-r--r-- | fig0_17.cpp | 117 | ||||
-rw-r--r-- | fig0_18.cpp | 91 | ||||
-rw-r--r-- | fig0_19.cpp | 85 | ||||
-rw-r--r-- | fig0_2.cpp | 146 | ||||
-rw-r--r-- | fig0_21.cpp | 302 | ||||
-rw-r--r-- | fig0_22.cpp | 163 | ||||
-rw-r--r-- | fig0_24.cpp | 88 | ||||
-rw-r--r-- | fig0_25.cpp | 91 | ||||
-rw-r--r-- | fig0_26.cpp | 88 | ||||
-rw-r--r-- | fig0_27.cpp | 83 | ||||
-rw-r--r-- | fig0_28.cpp | 69 | ||||
-rw-r--r-- | fig0_3.cpp | 88 | ||||
-rw-r--r-- | fig0_31.cpp | 110 | ||||
-rw-r--r-- | fig0_5.cpp | 89 | ||||
-rw-r--r-- | fig0_6.cpp | 170 | ||||
-rw-r--r-- | fig0_8.cpp | 117 | ||||
-rw-r--r-- | fig0_9.cpp | 131 | ||||
-rw-r--r-- | figs.cpp | 112 | ||||
-rw-r--r-- | figs.hpp | 96 | ||||
-rw-r--r-- | tables.cpp | 311 | ||||
-rw-r--r-- | tables.hpp | 43 | ||||
-rw-r--r-- | utils.cpp | 154 | ||||
-rw-r--r-- | utils.hpp | 57 | ||||
-rw-r--r-- | watermarkdecoder.hpp | 98 |
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 @@ -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; +}; + |