From 4809f3c042a99a639542b1e6cd22657871113260 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 2 Sep 2016 17:43:32 +0200 Subject: Move all sources to src/ --- src/dabplussnoop.cpp | 324 +++++++++++++++++++ src/dabplussnoop.hpp | 133 ++++++++ src/etiinput.cpp | 198 ++++++++++++ src/etiinput.hpp | 49 +++ src/etisnoop.cpp | 805 +++++++++++++++++++++++++++++++++++++++++++++++ src/faad_decoder.cpp | 177 +++++++++++ src/faad_decoder.hpp | 100 ++++++ src/fig0_0.cpp | 62 ++++ src/fig0_1.cpp | 112 +++++++ src/fig0_10.cpp | 74 +++++ src/fig0_11.cpp | 209 ++++++++++++ src/fig0_13.cpp | 131 ++++++++ src/fig0_14.cpp | 81 +++++ src/fig0_16.cpp | 127 ++++++++ src/fig0_17.cpp | 138 ++++++++ src/fig0_18.cpp | 112 +++++++ src/fig0_19.cpp | 105 +++++++ src/fig0_2.cpp | 168 ++++++++++ src/fig0_21.cpp | 323 +++++++++++++++++++ src/fig0_22.cpp | 186 +++++++++++ src/fig0_24.cpp | 109 +++++++ src/fig0_25.cpp | 112 +++++++ src/fig0_26.cpp | 109 +++++++ src/fig0_27.cpp | 104 ++++++ src/fig0_28.cpp | 90 ++++++ src/fig0_3.cpp | 110 +++++++ src/fig0_31.cpp | 133 ++++++++ src/fig0_5.cpp | 114 +++++++ src/fig0_6.cpp | 190 +++++++++++ src/fig0_8.cpp | 147 +++++++++ src/fig0_9.cpp | 133 ++++++++ src/fig1.cpp | 157 +++++++++ src/figs.cpp | 114 +++++++ src/figs.hpp | 113 +++++++ src/firecode.c | 50 +++ src/firecode.h | 33 ++ src/lib_crc.c | 459 +++++++++++++++++++++++++++ src/lib_crc.h | 66 ++++ src/repetitionrate.cpp | 196 ++++++++++++ src/repetitionrate.hpp | 47 +++ src/rsdecoder.cpp | 83 +++++ src/rsdecoder.hpp | 43 +++ src/tables.cpp | 311 ++++++++++++++++++ src/tables.hpp | 43 +++ src/utils.cpp | 154 +++++++++ src/utils.hpp | 57 ++++ src/watermarkdecoder.hpp | 98 ++++++ src/wavfile.c | 82 +++++ src/wavfile.h | 17 + 49 files changed, 7088 insertions(+) create mode 100644 src/dabplussnoop.cpp create mode 100644 src/dabplussnoop.hpp create mode 100644 src/etiinput.cpp create mode 100644 src/etiinput.hpp create mode 100644 src/etisnoop.cpp create mode 100644 src/faad_decoder.cpp create mode 100644 src/faad_decoder.hpp create mode 100644 src/fig0_0.cpp create mode 100644 src/fig0_1.cpp create mode 100644 src/fig0_10.cpp create mode 100644 src/fig0_11.cpp create mode 100644 src/fig0_13.cpp create mode 100644 src/fig0_14.cpp create mode 100644 src/fig0_16.cpp create mode 100644 src/fig0_17.cpp create mode 100644 src/fig0_18.cpp create mode 100644 src/fig0_19.cpp create mode 100644 src/fig0_2.cpp create mode 100644 src/fig0_21.cpp create mode 100644 src/fig0_22.cpp create mode 100644 src/fig0_24.cpp create mode 100644 src/fig0_25.cpp create mode 100644 src/fig0_26.cpp create mode 100644 src/fig0_27.cpp create mode 100644 src/fig0_28.cpp create mode 100644 src/fig0_3.cpp create mode 100644 src/fig0_31.cpp create mode 100644 src/fig0_5.cpp create mode 100644 src/fig0_6.cpp create mode 100644 src/fig0_8.cpp create mode 100644 src/fig0_9.cpp create mode 100644 src/fig1.cpp create mode 100644 src/figs.cpp create mode 100644 src/figs.hpp create mode 100644 src/firecode.c create mode 100644 src/firecode.h create mode 100644 src/lib_crc.c create mode 100644 src/lib_crc.h create mode 100644 src/repetitionrate.cpp create mode 100644 src/repetitionrate.hpp create mode 100644 src/rsdecoder.cpp create mode 100644 src/rsdecoder.hpp create mode 100644 src/tables.cpp create mode 100644 src/tables.hpp create mode 100644 src/utils.cpp create mode 100644 src/utils.hpp create mode 100644 src/watermarkdecoder.hpp create mode 100644 src/wavfile.c create mode 100644 src/wavfile.h (limited to 'src') diff --git a/src/dabplussnoop.cpp b/src/dabplussnoop.cpp new file mode 100644 index 0000000..cd91d49 --- /dev/null +++ b/src/dabplussnoop.cpp @@ -0,0 +1,324 @@ +/* + Copyright (C) 2014, 2015 Matthias P. Braendli (http://www.opendigitalradio.org) + + 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 . + + dabplussnoop.cpp + Parse DAB+ frames from a ETI file + + Authors: + Matthias P. Braendli + Mathias Coinchon +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dabplussnoop.hpp" +extern "C" { +#include "firecode.h" +#include "lib_crc.h" +} +#include "faad_decoder.hpp" +#include "rsdecoder.hpp" + +#define DPS_INDENT "\t\t" +#define DPS_PREFIX "DAB+ decode:" + +#define DPS_DEBUG 0 + +using namespace std; + +void DabPlusSnoop::push(uint8_t* streamdata, size_t streamsize) +{ + // Try to decode audio + size_t original_size = m_data.size(); + m_data.resize(original_size + streamsize); + memcpy(&m_data[original_size], streamdata, streamsize); + + if (seek_valid_firecode()) { + // m_data now points to a valid header + + if (decode()) { + + // First dump to subchannel file (superframe+parity word) + if (m_raw_data_stream_fd == NULL) { + stringstream dump_filename; + dump_filename << "stream-" << m_index << ".msc"; + + m_raw_data_stream_fd = fopen(dump_filename.str().c_str(), "w"); + + if (m_raw_data_stream_fd == NULL) { + perror("File open failed"); + } + } + + fwrite(&m_data[0], m_subchannel_index, 120, m_raw_data_stream_fd); + + // We have been able to decode the AUs, now flush vector + m_data.clear(); + } + } +} + +// Idea and some code taken from Xpadxpert +bool DabPlusSnoop::seek_valid_firecode() +{ + if (m_data.size() < 10) { + // Not enough data + return false; + } + + bool crc_ok = false; + size_t i; + + for (i = 0; i < m_data.size() - 10; i++) { + uint8_t* b = &m_data[i]; + + // the three bytes after the firecode must not be zero + // (simple plausibility check to avoid sync in zero byte region) + if (b[3] != 0x00 || (b[4] & 0xF0) != 0x00) { + uint16_t header_firecode = (b[0] << 8) | b[1]; + uint16_t calculated_firecode = firecode_crc(b+2, 9); + + if (header_firecode == calculated_firecode) { + crc_ok = true; + break; + } + } + } + + if (crc_ok) { +#if DPS_DEBUG + printf(DPS_PREFIX " Found valid FireCode at %zu\n", i); +#endif + //erase elements before the header + m_data.erase(m_data.begin(), m_data.begin() + i); + return true; + } + else { +#if DPS_DEBUG + printf(DPS_PREFIX " No valid FireCode found\n"); +#endif + + m_data.clear(); + return false; + } +} + +bool DabPlusSnoop::decode() +{ +#if DPS_DEBUG + printf(DPS_PREFIX " We have %zu bytes of data\n", m_data.size()); +#endif + + const size_t sf_len = m_subchannel_index * 120; + if (m_subchannel_index && m_data.size() >= sf_len) { + std::vector b(sf_len); + std::copy(m_data.begin(), m_data.begin() + sf_len, b.begin()); + + RSDecoder rs_dec; + int rs_errors = rs_dec.DecodeSuperframe(b, m_subchannel_index); + + if (rs_errors == -1) { + return false; + } + + // -- Parse he_aac_super_frame + // ---- Parse he_aac_super_frame_header + // ------ Parse firecode and audio params + uint16_t header_firecode = (b[0] << 8) | b[1]; + uint8_t audio_params = b[2]; + int rfa = (audio_params & 0x80) ? true : false; + m_dac_rate = (audio_params & 0x40) ? true : false; + m_sbr_flag = (audio_params & 0x20) ? true : false; + m_aac_channel_mode = (audio_params & 0x10) ? true : false; + m_ps_flag = (audio_params & 0x08) ? true : false; + m_mpeg_surround_config = (audio_params & 0x07); + + int num_aus; + if (!m_dac_rate && m_sbr_flag) num_aus = 2; + // AAC core sampling rate 16 kHz + else if (m_dac_rate && m_sbr_flag) num_aus = 3; + // AAC core sampling rate 24 kHz + else if (!m_dac_rate && !m_sbr_flag) num_aus = 4; + // AAC core sampling rate 32 kHz + else if (m_dac_rate && !m_sbr_flag) num_aus = 6; + // AAC core sampling rate 48 kHz + +#if DPS_DEBUG + printf( DPS_INDENT DPS_PREFIX "\n" + DPS_INDENT "\tfirecode 0x%x\n" + DPS_INDENT "\trfa %d\n" + DPS_INDENT "\tdac_rate %d\n" + DPS_INDENT "\tsbr_flag %d\n" + DPS_INDENT "\taac_channel_mode %d\n" + DPS_INDENT "\tps_flag %d\n" + DPS_INDENT "\tmpeg_surround_config %d\n" + DPS_INDENT "\tnum_aus %d\n", + header_firecode, rfa, m_dac_rate, m_sbr_flag, + m_aac_channel_mode, m_ps_flag, m_mpeg_surround_config, + num_aus); +#else + // Avoid "unused variable" warning + (void)header_firecode; + (void)rfa; +#endif + + + // ------ Parse au_start + auto au_starts = b.begin() + 3; + + vector au_start_nibbles(0); + + /* Each AU_START is encoded in three nibbles. + * When we have n AUs, we have n-1 au_start values. */ + for (int i = 0; i < (num_aus-1)*3; i++) { + if (i % 2 == 0) { + char nibble = au_starts[i/2] >> 4; + + au_start_nibbles.push_back( nibble ); + } + else { + char nibble = au_starts[i/2] & 0x0F; + + au_start_nibbles.push_back( nibble ); + } + + } + + + vector au_start(num_aus); + + if (num_aus == 2) + au_start[0] = 5; + else if (num_aus == 3) + au_start[0] = 6; + else if (num_aus == 4) + au_start[0] = 8; + else if (num_aus == 6) + au_start[0] = 11; + + + int nib = 0; + for (int au = 1; au < num_aus; au++) { + au_start[au] = au_start_nibbles[nib] << 8 | \ + au_start_nibbles[nib+1] << 4 | \ + au_start_nibbles[nib+2]; + + nib += 3; + } + +#if DPS_DEBUG + printf(DPS_INDENT DPS_PREFIX " AU start\n"); + for (int au = 0; au < num_aus; au++) { + printf(DPS_INDENT "\tAU[%d] %d 0x%x\n", au, + au_start[au], + au_start[au]); + } +#endif + + return extract_au(au_start); + } + else { + return false; + } +} + +bool DabPlusSnoop::extract_au(vector au_start) +{ + vector > aus(au_start.size()); + + // The last entry of au_start must the end of valid + // AU data. We stop at m_subchannel_index * 110 because + // what comes after is RS parity + au_start.push_back(m_subchannel_index * 110); + + bool all_crc_ok = true; + + for (size_t au = 0; au < aus.size(); au++) + { +#if DPS_DEBUG + printf(DPS_PREFIX DPS_INDENT + "Copy au %zu of size %zu\n", + au, + au_start[au+1] - au_start[au]-2 ); +#endif + + aus[au].resize(au_start[au+1] - au_start[au]-2); + std::copy( + m_data.begin() + au_start[au], + m_data.begin() + au_start[au+1]-2, + aus[au].begin() ); + + /* Check CRC */ + uint16_t au_crc = m_data[au_start[au+1]-2] << 8 | \ + m_data[au_start[au+1]-1]; + + uint16_t calc_crc = 0xFFFF; + for (vector::iterator au_data = aus[au].begin(); + au_data != aus[au].end(); + ++au_data) { + calc_crc = update_crc_ccitt(calc_crc, *au_data); + } + calc_crc =~ calc_crc; + + if (calc_crc != au_crc) { + printf(DPS_INDENT DPS_PREFIX + "Erroneous CRC for au %zu: 0x%04x vs 0x%04x\n", + au, calc_crc, au_crc); + + all_crc_ok = false; + } + } + + if (all_crc_ok) { + return analyse_au(aus); + } + else { + //discard faulty superframe (to be improved to correct/conceal) + m_data.clear(); + return false; + } +} + +bool DabPlusSnoop::analyse_au(vector >& aus) +{ + stringstream ss_filename; + + ss_filename << "stream-" << m_index; + + if (!m_faad_decoder.is_initialised()) { + m_faad_decoder.open(ss_filename.str(), m_ps_flag, + m_aac_channel_mode, m_dac_rate, m_sbr_flag, + m_mpeg_surround_config); + } + + return m_faad_decoder.decode(aus); +} + +void DabPlusSnoop::close() +{ + m_faad_decoder.close(); + + if (m_raw_data_stream_fd) { + fclose(m_raw_data_stream_fd); + } +} diff --git a/src/dabplussnoop.hpp b/src/dabplussnoop.hpp new file mode 100644 index 0000000..865a35e --- /dev/null +++ b/src/dabplussnoop.hpp @@ -0,0 +1,133 @@ +/* + Copyright (C) 2014 Matthias P. Braendli (http://www.opendigitalradio.org) + + 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 . + + dabplussnoop.cpp + Parse DAB+ frames from a ETI file + + Authors: + Matthias P. Braendli +*/ + +/* From the spec: + +subchannel_index = MSC sub-channel size (kbps) / 8 +audio_super_frame_size (bytes) = subchannel_index * 110 + +// Derived from +// au_start[n] = au_start[n - 1] + au_size[n - 1] + 2; +// 2 bytes for CRC +au_size[n] = au_start[n+1] - au_start[n] - 2; + +he_aac_super_frame(subchannel_index) +{ + he_aac_super_frame_header() + { + header_firecode 16 + rfa 1 + dac_rate 1 + sbr_flag 1 + aac_channel_mode 1 + ps_flag 1 + mpeg_surround_config 3 + + // end of audio parameters + if ((dac_rate == 0) && (sbr_flag == 1)) num_aus = 2; + // AAC core sampling rate 16 kHz + if ((dac_rate == 1) && (sbr_flag == 1)) num_aus = 3; + // AAC core sampling rate 24 kHz + if ((dac_rate == 0) && (sbr_flag == 0)) num_aus = 4; + // AAC core sampling rate 32 kHz + if ((dac_rate == 1) && (sbr_flag == 0)) num_aus = 6; + // AAC core sampling rate 48 kHz + + for (n = 1; n < num_aus; n++) { + au_start[n]; 12 + } + + if !((dac_rate == 1) && (sbr_flag == 1)) + alignment 4 + } + + for (n = 0; n < num_aus; n++) { + au[n] 8 * au_size[n] + au_crc[n] 16 + } + +} +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "faad_decoder.hpp" + +#ifndef __DABPLUSSNOOP_H_ +#define __DABPLUSSNOOP_H_ + + +class DabPlusSnoop +{ + public: + DabPlusSnoop() : + m_index(0), + m_subchannel_index(0), + m_data(0), + m_raw_data_stream_fd(NULL) {} + + void set_subchannel_index(unsigned subchannel_index) + { + m_subchannel_index = subchannel_index; + } + + void set_index(int index) + { + m_index = index; + } + + void push(uint8_t* streamdata, size_t streamsize); + + void close(void); + + private: + /* Data needed for FAAD */ + FaadDecoder m_faad_decoder; + int m_index; + + bool m_ps_flag; + bool m_aac_channel_mode; + bool m_dac_rate; + bool m_sbr_flag; + int m_mpeg_surround_config; + + /* Functions */ + + bool seek_valid_firecode(void); + bool decode(void); + bool extract_au(std::vector au_start); + bool analyse_au(std::vector >& aus); + + unsigned m_subchannel_index; + std::vector m_data; + + FILE* m_raw_data_stream_fd; +}; + +#endif + diff --git a/src/etiinput.cpp b/src/etiinput.cpp new file mode 100644 index 0000000..8f47ec0 --- /dev/null +++ b/src/etiinput.cpp @@ -0,0 +1,198 @@ +/* + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Her Majesty the Queen in Right of Canada (Communications Research + Center Canada) + + Copyrigth (C) 2014 + Matthias P. Braendli, matthias.braendli@mpb.li + + Taken from ODR-DabMod + + Supported file formats: RAW, FRAMED, STREAMED + Supports re-sync to RAW ETI file + */ +/* + This file is part of ODR-DabMod. + + ODR-DabMod 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. + + ODR-DabMod 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 ODR-DabMod. If not, see . + */ +#include "etiinput.hpp" +#include +#include +#include +#include +#include +#include +#include /* Definition of AT_* constants */ +#include + +int identify_eti_format(FILE* inputFile, int *streamType) +{ + *streamType = ETI_STREAM_TYPE_NONE; + + struct stat inputFileStat; + fstat(fileno(inputFile), &inputFileStat); + size_t inputfilelength_ = inputFileStat.st_size; + + int nbframes_; + + uint32_t sync; + uint32_t nbFrames; + uint16_t frameSize; + + char discard_buffer[6144]; + + if (fread(&sync, sizeof(sync), 1, inputFile) != 1) { + fprintf(stderr, "Unable to read sync in input file!\n"); + perror(""); + return -1; + } + if ((sync == 0x49c5f8ff) || (sync == 0xb63a07ff)) { + *streamType = ETI_STREAM_TYPE_RAW; + if (inputfilelength_ > 0) { + nbframes_ = inputfilelength_ / 6144; + } + else { + nbframes_ = ~0; + } + if (fseek(inputFile, -sizeof(sync), SEEK_CUR) != 0) { + // if the seek fails, consume the rest of the frame + if (fread(discard_buffer, 6144 - sizeof(sync), 1, inputFile) + != 1) { + fprintf(stderr, "Unable to read from input file!\n"); + perror(""); + return -1; + } + } + return 0; + } + + nbFrames = sync; + if (fread(&frameSize, sizeof(frameSize), 1, inputFile) != 1) { + fprintf(stderr, "Unable to read frame size in input file!\n"); + perror(""); + return -1; + } + sync >>= 16; + sync &= 0xffff; + sync |= ((uint32_t)frameSize) << 16; + + if ((sync == 0x49c5f8ff) || (sync == 0xb63a07ff)) { + *streamType = ETI_STREAM_TYPE_STREAMED; + frameSize = nbFrames & 0xffff; + if (inputfilelength_ > 0) { + nbframes_ = inputfilelength_ / (frameSize + 2); + } + else { + nbframes_ = ~0; + } + if (fseek(inputFile, -6, SEEK_CUR) != 0) { + // if the seek fails, consume the rest of the frame + if (fread(discard_buffer, frameSize - 4, 1, inputFile) + != 1) { + fprintf(stderr, "Unable to read from input file!\n"); + perror(""); + return -1; + } + } + return 0; + } + + if (fread(&sync, sizeof(sync), 1, inputFile) != 1) { + fprintf(stderr, "Unable to read nb frame in input file!\n"); + perror(""); + return -1; + } + if ((sync == 0x49c5f8ff) || (sync == 0xb63a07ff)) { + *streamType = ETI_STREAM_TYPE_FRAMED; + if (fseek(inputFile, -6, SEEK_CUR) != 0) { + // if the seek fails, consume the rest of the frame + if (fread(discard_buffer, frameSize - 4, 1, inputFile) + != 1) { + fprintf(stderr, "Unable to read from input file!\n"); + perror(""); + return -1; + } + } + nbframes_ = ~0; + return 0; + } + + // Search for the sync marker byte by byte + for (size_t i = 10; i < 6144 + 10; ++i) { + sync >>= 8; + sync &= 0xffffff; + if (fread((uint8_t*)&sync + 3, 1, 1, inputFile) != 1) { + fprintf(stderr, "Unable to read from input file!\n"); + perror(""); + return -1; + } + if ((sync == 0x49c5f8ff) || (sync == 0xb63a07ff)) { + *streamType = ETI_STREAM_TYPE_RAW; + if (inputfilelength_ > 0) { + nbframes_ = (inputfilelength_ - i) / 6144; + } + else { + nbframes_ = ~0; + } + if (fseek(inputFile, -sizeof(sync), SEEK_CUR) != 0) { + if (fread(discard_buffer, 6144 - sizeof(sync), 1, inputFile) + != 1) { + fprintf(stderr, "Unable to read from input file!\n"); + perror(""); + return -1; + } + } + return 0; + } + } + + (void)nbframes_; // suppress warning "nbframes_ unused" + fprintf(stderr, "Bad input file format!\n"); + return -1; +} + +int get_eti_frame(FILE* inputfile, int stream_type, void* buf) +{ + // Initialise buffer + memset(buf, 0x55, 6144); + + uint16_t frameSize; + if (stream_type == ETI_STREAM_TYPE_RAW) { + frameSize = 6144; + } + else { + if (fread(&frameSize, sizeof(frameSize), 1, inputfile) != 1) { + // EOF + return 0; + } + } + + if (frameSize > 6144) { // there might be a better limit + printf("Wrong frame size %u in ETI file!\n", frameSize); + return -1; + } + + int read_bytes = fread(buf, 1, frameSize, inputfile); + if (read_bytes != frameSize) { + // A short read of a frame (i.e. reading an incomplete frame) + // is not tolerated. Input files must not contain incomplete frames + printf("Incomplete frame in ETI file!\n"); + return -1; + } + + // We have added padding, so we always return 6144 bytes + return 6144; +} + diff --git a/src/etiinput.hpp b/src/etiinput.hpp new file mode 100644 index 0000000..d8b7691 --- /dev/null +++ b/src/etiinput.hpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Her Majesty the Queen in Right of Canada (Communications Research + Center Canada) + + Copyrigth (C) 2014 + Matthias P. Braendli, matthias.braendli@mpb.li + + Taken from ODR-DabMod + + Supported file formats: RAW, FRAMED, STREAMED + Supports re-sync to RAW ETI file + */ +/* + This file is part of ODR-DabMod. + + ODR-DabMod 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. + + ODR-DabMod 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 ODR-DabMod. If not, see . + */ +#include + +#ifndef _ETIINPUT_H_ +#define _ETIINPUT_H_ + + +#define ETI_STREAM_TYPE_NONE 0 +#define ETI_STREAM_TYPE_RAW 1 +#define ETI_STREAM_TYPE_STREAMED 2 +#define ETI_STREAM_TYPE_FRAMED 3 + +/* Identify the stream type, and return 0 on success, -1 on failure */ +int identify_eti_format(FILE* inputfile, int *stream_type); + +/* Read the next ETI frame into buf, which must be at least 6144 bytes big + * Return number of bytes read, or zero if EOF */ +int get_eti_frame(FILE* inputfile, int stream_type, void* buf); + +#endif + diff --git a/src/etisnoop.cpp b/src/etisnoop.cpp new file mode 100644 index 0000000..92f54fd --- /dev/null +++ b/src/etisnoop.cpp @@ -0,0 +1,805 @@ +/* + 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 . + + etisnoop.cpp + Parse ETI NI G.703 file + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern "C" { +#include "lib_crc.h" +} +#include "dabplussnoop.hpp" +#include "utils.hpp" +#include "etiinput.hpp" +#include "figs.hpp" +#include "watermarkdecoder.hpp" +#include "repetitionrate.hpp" + +struct FIG +{ + int type; + int ext; + int len; +}; + +class FIGalyser +{ + public: + FIGalyser() + { + clear(); + } + + void set_fib(int fib) + { + m_fib = fib; + } + + void push_back(int type, int ext, int len) + { + struct FIG fig = { + .type = type, + .ext = ext, + .len = len }; + + m_figs[m_fib].push_back(fig); + } + + void analyse() + { + printf("FIC "); + + for (size_t fib = 0; fib < m_figs.size(); fib++) { + int consumed = 7; + int fic_size = 0; + printf("[%1zu ", fib); + + for (size_t i = 0; i < m_figs[fib].size(); i++) { + FIG &f = m_figs[fib][i]; + printf("%01d/%02d (%2d) ", f.type, f.ext, f.len); + + consumed += 10; + + fic_size += f.len; + } + + printf(" "); + + int align = 60 - consumed; + if (align > 0) { + while (align--) { + printf(" "); + } + } + + printf("|"); + + for (int i = 0; i < 15; i++) { + if (2*i < fic_size) { + printf("#"); + } + else { + printf("-"); + } + } + + printf("| ] "); + + } + + printf("\n"); + } + + void clear() + { + m_figs.clear(); + m_figs.resize(3); + } + + private: + int m_fib; + std::vector > m_figs; +}; + + +struct FIG0_13_shortAppInfo +{ + uint16_t SId; + uint8_t No:4; + uint8_t SCIdS:4; +} PACKED; + + +#define ETINIPACKETSIZE 6144 + +using namespace std; + +struct eti_analyse_config_t { + eti_analyse_config_t() : + etifd(nullptr), + ignore_error(false), + streams_to_decode(), + analyse_fic_carousel(false), + analyse_fig_rates(false), + analyse_fig_rates_per_second(false), + decode_watermark(false) {} + + FILE* etifd; + bool ignore_error; + std::map streams_to_decode; + bool analyse_fic_carousel; + bool analyse_fig_rates; + bool analyse_fig_rates_per_second; + bool decode_watermark; +}; + + +// Function prototypes +void decodeFIG(FIGalyser &figs, + WatermarkDecoder &wm_decoder, + uint8_t* figdata, + uint8_t figlen, + uint16_t figtype, + int indent); + +int eti_analyse(eti_analyse_config_t& config); + +const char *get_programme_type_str(size_t int_table_Id, size_t pty); +int sprintfMJD(char *dst, int mjd); + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 +const struct option longopts[] = { + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {"ignore-error", no_argument, 0, 'e'}, + {"decode-stream", required_argument, 0, 'd'}, + {"input", required_argument, 0, 'i'} +}; + +void usage(void) +{ + fprintf(stderr, + "Opendigitalradio ETISnoop analyser %s compiled at %s, %s\n\n" + "The ETISnoop analyser decodes and prints out a RAW ETI file in a\n" + "form that makes analysis easier.\n" + "\n" + " http://www.opendigitalradio.org\n" + "\n" + "Usage: etisnoop [-v] [-f] [-w] [-i filename] [-d stream_index]\n" + "\n" + " -v increase verbosity (can be given more than once)\n" + " -d N decode subchannel N into .msc and .wav files\n" + " -f analyse FIC carousel\n" + " -r analyse FIG rates in FIGs per second\n" + " -R analyse FIG rates in frames per FIG\n" + " -w decode CRC-DABMUX and ODR-DabMux watermark.\n", +#if defined(GITVERSION) + GITVERSION, +#else + VERSION, +#endif + __DATE__, __TIME__); +} + +int main(int argc, char *argv[]) +{ + int index; + int ch = 0; + string file_name("-"); + + eti_analyse_config_t config; + + while(ch != -1) { + ch = getopt_long(argc, argv, "d:efhrRvwi:", longopts, &index); + switch (ch) { + case 'd': + { + int subchix = atoi(optarg); + DabPlusSnoop dps; + config.streams_to_decode[subchix] = dps; + } + break; + case 'e': + config.ignore_error = true; + break; + case 'i': + file_name = optarg; + break; + case 'f': + config.analyse_fic_carousel = true; + break; + case 'r': + config.analyse_fig_rates = true; + config.analyse_fig_rates_per_second = true; + break; + case 'R': + config.analyse_fig_rates = true; + config.analyse_fig_rates_per_second = false; + break; + case 'v': + set_verbosity(get_verbosity() + 1); + break; + case 'w': + config.decode_watermark = true; + break; + case 'h': + usage(); + return 1; + break; + } + } + + FILE* etifd; + + if (file_name == "-") { + printf("Analysing stdin\n"); + etifd = stdin; + } + else { + etifd = fopen(file_name.c_str(), "r"); + if (etifd == NULL) { + perror("File open failed"); + return 1; + } + } + config.etifd = etifd; + + eti_analyse(config); + fclose(etifd); +} + +int eti_analyse(eti_analyse_config_t& config) +{ + uint8_t p[ETINIPACKETSIZE]; + string desc; + char prevsync[3]={0x00,0x00,0x00}; + uint8_t ficf,nst,fp,mid,ficl; + uint16_t fl,crch; + uint16_t crc; + uint8_t scid,tpl; + uint16_t sad[64],stl[64]; + char sdesc[256]; + uint32_t frame_nb = 0, frame_sec = 0, frame_ms = 0, frame_h, frame_m, frame_s; + + bool running = true; + + int stream_type = ETI_STREAM_TYPE_NONE; + if (identify_eti_format(config.etifd, &stream_type) == -1) { + printf("Could not identify stream type\n"); + + running = false; + } + else { + printf("Identified ETI type "); + if (stream_type == ETI_STREAM_TYPE_RAW) + printf("RAW\n"); + else if (stream_type == ETI_STREAM_TYPE_STREAMED) + printf("STREAMED\n"); + else if (stream_type == ETI_STREAM_TYPE_FRAMED) + printf("FRAMED\n"); + else + printf("?\n"); + } + + WatermarkDecoder wm_decoder; + + if (config.analyse_fig_rates) { + rate_display_header(config.analyse_fig_rates_per_second); + } + + while (running) { + + int ret = get_eti_frame(config.etifd, stream_type, p); + if (ret == -1) { + fprintf(stderr, "ETI file read error\n"); + break; + } + else if (ret == 0) { + fprintf(stderr, "End of ETI\n"); + break; + } + + // Timestamp and Frame Number + frame_h = (frame_sec / 3600); + frame_m = (frame_sec - (frame_h * 3600)) / 60; + frame_s = (frame_sec - (frame_h * 3600) - (frame_m * 60)); + sprintf(sdesc, "%02d:%02d:%02d.%03d frame %d", frame_h, frame_m, frame_s, frame_ms, frame_nb); + printbuf(sdesc, 0, NULL, 0); + frame_ms += 24; // + 24 ms + if (frame_ms >= 1000) { + frame_ms -= 1000; + frame_sec++; + } + frame_nb++; + + // SYNC + printbuf("SYNC", 0, p, 4); + + // SYNC - ERR + if (p[0] == 0xFF) { + desc = "No error"; + printbuf("ERR", 1, p, 1, desc); + } + else { + desc = "Error"; + printbuf("ERR", 1, p, 1, desc); + if (!config.ignore_error) { + printf("Aborting because of SYNC error\n"); + break; + } + } + + // SYNC - FSYNC + + if (memcmp(prevsync, "\x00\x00\x00", 3) == 0) { + if ( (memcmp(p + 1, "\x07\x3a\xb6", 3) == 0) || + (memcmp(p + 1, "\xf8\xc5\x49", 3) == 0) ) { + desc = "OK"; + memcpy(prevsync, p+1, 3); + } + else { + desc ="Wrong FSYNC"; + memcpy(prevsync, "\x00\x00\x00", 3); + } + } else if (memcmp(prevsync, "\x07\x3a\xb6", 3) == 0) { + if (memcmp(p + 1, "\xf8\xc5\x49", 3) != 0) { + desc = "Wrong FSYNC"; + memcpy(prevsync, "\x00\x00\x00", 3); + } else { + desc = "OK"; + memcpy(prevsync, p + 1, 3); + } + } else if (memcmp(prevsync, "\xf8\xc5\x49", 3) == 0) { + if (memcmp(p + 1, "\x07\x3a\xb6", 3) != 0) { + desc = "Wrong FSYNC"; + memcpy(prevsync, "\x00\x00\x00", 3); + } else { + desc = "OK"; + memcpy(prevsync, p + 1, 3); + } + } + printbuf("Sync FSYNC", 1, p + 1, 3, desc); + + // LIDATA + printbuf("LDATA", 0, NULL, 0); + // LIDATA - FC + printbuf("FC - Frame Characterization field", 1, p+4, 4); + // LIDATA - FC - FCT + char fct_str[25]; + sprintf(fct_str, "%d", p[4]); + int fct = p[4]; + printbuf("FCT - Frame Count", 2, p+4, 1, fct_str); + // LIDATA - FC - FICF + ficf = (p[5] & 0x80) >> 7; + + { + stringstream ss; + ss << (int)ficf; + if (ficf == 1) { + ss << "- FIC Information are present"; + } + else { + ss << "- FIC Information are not present"; + } + + printbuf("FICF - Fast Information Channel Flag", 2, NULL, 0, ss.str()); + } + + // LIDATA - FC - NST + nst = p[5] & 0x7F; + { + stringstream ss; + ss << (int)nst; + printbuf("NST - Number of streams", 2, NULL, 0, ss.str()); + } + + // LIDATA - FC - FP + fp = (p[6] & 0xE0) >> 5; + { + stringstream ss; + ss << (int)fp; + printbuf("FP - Frame Phase", 2, &fp, 1, ss.str()); + } + + // LIDATA - FC - MID + mid = (p[6] & 0x18) >> 3; + { + stringstream ss; + ss << "Mode "; + if (mid != 0) { + ss << (int)mid; + } + else { + ss << "4"; + } + printbuf("MID - Mode Identity", 2, &mid, 1, ss.str()); + set_mode_identity(mid); + } + + // LIDATA - FC - FL + fl = (p[6] & 0x07) * 256 + p[7]; + { + stringstream ss; + ss << fl << " words"; + printbuf("FL - Frame Length", 2, NULL, 0, ss.str()); + } + + if (ficf == 0) { + ficl = 0; + } + else if (mid == 3) { + ficl = 32; + } + else { + ficl = 24; + } + + // STC + printbuf("STC - Stream Characterisation", 1, NULL, 0); + + for (int i=0; i < nst; i++) { + sprintf(sdesc, "Stream number %d", i); + printbuf("STC - Stream Characterisation", 2, p + 8 + 4*i, 4, sdesc); + scid = (p[8 + 4*i] & 0xFC) >> 2; + sprintf(sdesc, "%d", scid); + printbuf("SCID - Sub-channel Identifier", 3, NULL, 0, sdesc); + sad[i] = (p[8+4*i] & 0x03) * 256 + p[9+4*i]; + sprintf(sdesc, "%d", sad[i]); + printbuf("SAD - Sub-channel Start Address", 3, NULL, 0, sdesc); + tpl = (p[10+4*i] & 0xFC) >> 2; + + if ((tpl & 0x20) >> 5 == 1) { + uint8_t opt, plevel; + string plevelstr; + opt = (tpl & 0x1c) >> 2; + plevel = (tpl & 0x03); + if (opt == 0x00) { + if (plevel == 0) + plevelstr = "1-A, 1/4, 16 CUs"; + else if (plevel == 1) + plevelstr = "2-A, 3/8, 8 CUs"; + else if (plevel == 2) + plevelstr = "3-A, 1/2, 6 CUs"; + else if (plevel == 3) + plevelstr = "4-A, 3/4, 4 CUs"; + } + else if (opt == 0x01) { + if (plevel == 0) + plevelstr = "1-B, 4/9, 27 CUs"; + else if (plevel == 1) + plevelstr = "2-B, 4/7, 21 CUs"; + else if (plevel == 2) + plevelstr = "3-B, 4/6, 18 CUs"; + else if (plevel == 3) + plevelstr = "4-B, 4/5, 15 CUs"; + } + else { + stringstream ss; + ss << "Unknown option " << opt; + plevelstr = ss.str(); + } + sprintf(sdesc, "0x%02x - Equal Error Protection. %s", tpl, plevelstr.c_str()); + } + else { + uint8_t tsw, uepidx; + tsw = (tpl & 0x08); + uepidx = tpl & 0x07; + sprintf(sdesc, "0x%02x - Unequal Error Protection. Table switch %d, UEP index %d", tpl, tsw, uepidx); + } + printbuf("TPL - Sub-channel Type and Protection Level", 3, NULL, 0, sdesc); + stl[i] = (p[10+4*i] & 0x03) * 256 + \ + p[11+4*i]; + sprintf(sdesc, "%d => %d kbit/s", stl[i], stl[i]*8/3); + printbuf("STL - Sub-channel Stream Length", 3, NULL, 0, sdesc); + + if (config.streams_to_decode.count(i) > 0) { + config.streams_to_decode[i].set_subchannel_index(stl[i]/3); + config.streams_to_decode[i].set_index(i); + } + } + + // EOH + printbuf("EOH - End Of Header", 1, p + 8 + 4*nst, 4); + uint16_t mnsc = p[8 + 4*nst] * 256 + \ + p[8 + 4*nst + 1]; + { + stringstream ss; + ss << mnsc; + printbuf("MNSC - Multiplex Network Signalling Channel", 2, p+8+4*nst, 2, ss.str()); + } + + crch = p[8 + 4*nst + 2]*256 + \ + p[8 + 4*nst + 3]; + crc = 0xffff; + + for (int i=4; i < 8 + 4*nst + 2; i++) + crc = update_crc_ccitt(crc, p[i]); + crc =~ crc; + + if (crc == crch) { + sprintf(sdesc,"CRC OK"); + } + else { + sprintf(sdesc,"CRC Mismatch: %02x",crc); + } + + printbuf("Header CRC", 2, p + 8 + 4*nst + 2, 2, sdesc); + + // MST - FIC + if (ficf == 1) { + int endmarker = 0; + int figcount = 0; + uint8_t *fib, *fig; + uint16_t figcrc; + + FIGalyser figs; + + uint8_t ficdata[32*4]; + memcpy(ficdata, p + 12 + 4*nst, ficl*4); + sprintf(sdesc, "FIC Data (%d bytes)", ficl*4); + //printbuf(sdesc, 1, ficdata, ficl*4); + printbuf(sdesc, 1, NULL, 0); + fib = p + 12 + 4*nst; + for(int i = 0; i < ficl*4/32; i++) { + sprintf(sdesc, "FIB %d", i); + printbuf(sdesc, 1, NULL, 0); + fig=fib; + figs.set_fib(i); + rate_new_fib(i); + endmarker=0; + figcount=0; + while (!endmarker) { + uint8_t figtype, figlen; + figtype = (fig[0] & 0xE0) >> 5; + if (figtype != 7) { + figlen = fig[0] & 0x1F; + sprintf(sdesc, "FIG %d [%d bytes]", figtype, figlen); + printbuf(sdesc, 3, fig+1, figlen); + decodeFIG(figs, wm_decoder, fig+1, figlen, figtype, 4); + fig += figlen + 1; + figcount += figlen + 1; + if (figcount >= 29) + endmarker = 1; + } + else { + endmarker = 1; + } + } + figcrc = fib[30]*256 + fib[31]; + crc = 0xffff; + for (int j = 0; j < 30; j++) { + crc = update_crc_ccitt(crc, fib[j]); + } + crc =~ crc; + if (crc == figcrc) + sprintf(sdesc, "FIB %d CRC OK", i); + else + sprintf(sdesc, "FIB %d CRC Mismatch: %02x", i, crc); + + printbuf(sdesc,3,fib+30,2); + fib += 32; + } + + if (config.analyse_fic_carousel) { + figs.analyse(); + } + } + + int offset = 0; + for (int i=0; i < nst; i++) { + uint8_t streamdata[684*8]; + memcpy(streamdata, p + 12 + 4*nst + ficf*ficl*4 + offset, stl[i]*8); + offset += stl[i] * 8; + if (config.streams_to_decode.count(i) > 0) { + sprintf(sdesc, "id %d, len %d, selected for decoding", i, stl[i]*8); + } + else { + sprintf(sdesc, "id %d, len %d, not selected for decoding", i, stl[i]*8); + } + if (get_verbosity() > 1) { + printbuf("Stream Data", 1, streamdata, stl[i]*8, sdesc); + } + else { + printbuf("Stream Data", 1, streamdata, 0, sdesc); + } + + if (config.streams_to_decode.count(i) > 0) { + config.streams_to_decode[i].push(streamdata, stl[i]*8); + } + + } + + //* EOF (4 Bytes) + // CRC (2 Bytes) + crch = p[12 + 4*nst + ficf*ficl*4 + offset] * 256 + \ + p[12 + 4*nst + ficf*ficl*4 + offset + 1]; + + crc = 0xffff; + + for (int i = 12 + 4*nst; i < 12 + 4*nst + ficf*ficl*4 + offset; i++) + crc = update_crc_ccitt(crc, p[i]); + crc =~ crc; + if (crc == crch) + sprintf(sdesc, "CRC OK"); + else + sprintf(sdesc, "CRC Mismatch: %02x", crc); + + printbuf("EOF", 1, p + 12 + 4*nst + ficf*ficl*4 + offset, 4); + printbuf("CRC", 2, p + 12 + 4*nst + ficf*ficl*4 + offset, 2, sdesc); + + // RFU (2 Bytes) + printbuf("RFU", 2, p + 12 + 4*nst + ficf*ficl*4 + offset + 2, 2); + + //* TIST (4 Bytes) + const size_t tist_ix = 12 + 4*nst + ficf*ficl*4 + offset + 4; + uint32_t TIST = (uint32_t)(p[tist_ix]) << 24 | + (uint32_t)(p[tist_ix+1]) << 16 | + (uint32_t)(p[tist_ix+2]) << 8 | + (uint32_t)(p[tist_ix+3]); + + sprintf(sdesc, "%f ms", (TIST & 0xFFFFFF) / 16384.0); + printbuf("TIST - Time Stamp", 1, p + tist_ix, 4, sdesc); + + if (get_verbosity()) { + printf("-------------------------------------------------------------------------------------------------------------\n"); + } + + if (config.analyse_fig_rates and (fct % 250) == 0) { + rate_display_analysis(false, config.analyse_fig_rates_per_second); + } + } + + + std::map::iterator it; + for (it = config.streams_to_decode.begin(); + it != config.streams_to_decode.end(); + ++it) { + it->second.close(); + } + + if (config.decode_watermark) { + std::string watermark(wm_decoder.calculate_watermark()); + printf("Watermark:\n %s\n", watermark.c_str()); + } + + if (config.analyse_fig_rates) { + rate_display_analysis(false, config.analyse_fig_rates_per_second); + } + + figs_cleardb(); + + + return 0; +} + +void decodeFIG(FIGalyser &figs, + WatermarkDecoder &wm_decoder, + uint8_t* f, + uint8_t figlen, + uint16_t figtype, + int indent) +{ + char desc[512]; + + switch (figtype) { + case 0: + { + fig0_common_t fig0(f, figlen, wm_decoder); + + sprintf(desc, "FIG %d/%d: C/N=%d OE=%d P/D=%d", + figtype, fig0.ext(), fig0.cn(), fig0.oe(), fig0.pd()); + printbuf(desc, indent, f+1, figlen-1); + + figs.push_back(figtype, fig0.ext(), figlen); + + bool complete = fig0_select(fig0, indent); + rate_announce_fig(figtype, fig0.ext(), complete); + } + break; + + case 1: + {// SHORT LABELS + fig1_common_t fig1(f, figlen); + + figs.push_back(figtype, fig1.ext(), figlen); + + bool complete = fig1_select(fig1, indent); + rate_announce_fig(figtype, fig1.ext(), complete); + } + break; + case 2: + {// LONG LABELS + uint16_t ext,oe; + + uint8_t toggle_flag = (f[0] & 0x80) >> 7; + uint8_t segment_index = (f[0] & 0x70) >> 4; + oe = (f[0] & 0x08) >> 3; + ext = f[0] & 0x07; + sprintf(desc, + "FIG %d/%d: Toggle flag=%d, Segment_index=%d, OE=%d", + figtype, ext, toggle_flag, segment_index, oe); + + printbuf(desc, indent, f+1, figlen-1); + + figs.push_back(figtype, ext, figlen); + + bool complete = true; // TODO verify + rate_announce_fig(figtype, ext, complete); + } + break; + case 5: + {// FIDC + uint16_t ext; + + uint8_t d1 = (f[0] & 0x80) >> 7; + uint8_t d2 = (f[0] & 0x40) >> 6; + uint8_t tcid = (f[0] & 0x38) >> 5; + ext = f[0] & 0x07; + sprintf(desc, + "FIG %d/%d: D1=%d, D2=%d, TCId=%d", + figtype, ext, d1, d2, tcid); + + printbuf(desc, indent, f+1, figlen-1); + + figs.push_back(figtype, ext, figlen); + + bool complete = true; // TODO verify + rate_announce_fig(figtype, ext, complete); + } + break; + case 6: + {// Conditional access + fprintf(stderr, "ERROR: ETI contains unsupported FIG 6\n"); + } + break; + default: + { + fprintf(stderr, "ERROR: ETI contains unknown FIG %d\n", figtype); + } + break; + } +} + + diff --git a/src/faad_decoder.cpp b/src/faad_decoder.cpp new file mode 100644 index 0000000..73ea374 --- /dev/null +++ b/src/faad_decoder.cpp @@ -0,0 +1,177 @@ +/* + Copyright (C) 2014 Matthias P. Braendli (http://www.opendigitalradio.org) + + 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 . + + faad_decoder.cpp + Use libfaad to decode the AAC content of the DAB+ subchannel + + Authors: + Matthias P. Braendli +*/ + +#include "faad_decoder.hpp" +extern "C" { +#include "wavfile.h" +} +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +FaadDecoder::FaadDecoder() : + m_data_len(0), + m_fd(NULL), + m_initialised(false) +{ +} + +void FaadDecoder::open(string filename, bool ps_flag, bool aac_channel_mode, + bool dac_rate, bool sbr_flag, int mpeg_surround_config) +{ + m_filename = filename; + m_ps_flag = ps_flag; + m_aac_channel_mode = aac_channel_mode; + m_dac_rate = dac_rate; + m_sbr_flag = sbr_flag; + m_mpeg_surround_config = mpeg_surround_config; +} + +bool FaadDecoder::decode(vector > aus) +{ + for (size_t au_ix = 0; au_ix < aus.size(); au_ix++) { + + vector& au = aus[au_ix]; + + NeAACDecFrameInfo hInfo; + int16_t* outBuffer; + + if (!m_initialised) { + /* AudioSpecificConfig structure (the only way to select 960 transform here!) + * + * 00010 = AudioObjectType 2 (AAC LC) + * xxxx = (core) sample rate index + * xxxx = (core) channel config + * 100 = GASpecificConfig with 960 transform + * + * SBR: implicit signaling sufficient - libfaad2 automatically assumes SBR on sample rates <= 24 kHz + * => explicit signaling works, too, but is not necessary here + * + * PS: implicit signaling sufficient - libfaad2 therefore always uses stereo output (if PS support was enabled) + * => explicit signaling not possible, as libfaad2 does not support AudioObjectType 29 (PS) + */ + + int core_sr_index = m_dac_rate ? (m_sbr_flag ? 6 : 3) : (m_sbr_flag ? 8 : 5); // 24/48/16/32 kHz + int core_ch_config = get_aac_channel_configuration(); + if(core_ch_config == -1) { + printf("Unrecognized mpeg surround config (ignored): %d\n", m_mpeg_surround_config); + return false; + } + + uint8_t asc[2]; + asc[0] = 0b00010 << 3 | core_sr_index >> 1; + asc[1] = (core_sr_index & 0x01) << 7 | core_ch_config << 3 | 0b100; + + + long unsigned samplerate; + unsigned char channels; + + long int init_result = NeAACDecInit2(m_faad_handle.decoder, asc, sizeof(asc), &samplerate, &channels); + if(init_result != 0) { + /* If some error initializing occured, skip the file */ + printf("Error initializing decoder library: %s\n", NeAACDecGetErrorMessage(-init_result)); + NeAACDecClose(m_faad_handle.decoder); + return false; + } + + m_initialised = true; + } + + outBuffer = (int16_t *)NeAACDecDecode(m_faad_handle.decoder, &hInfo, &au[0], au.size()); + assert(outBuffer != NULL); + + m_sample_rate = hInfo.samplerate; + m_channels = hInfo.channels; + size_t samples = hInfo.samples; + +#if 0 + printf("bytes consumed %d\n", (int)(hInfo.bytesconsumed)); + printf("samplerate = %d, samples = %zu, channels = %d," + " error = %d, sbr = %d\n", m_sample_rate, samples, + m_channels, hInfo.error, hInfo.sbr); + printf("header = %d\n", hInfo.header_type); +#endif + + if (hInfo.error != 0) { + printf("FAAD Warning: %s\n", + faacDecGetErrorMessage(hInfo.error)); + return false; + } + + if (m_fd == NULL) { + stringstream ss; + ss << m_filename << ".wav"; + m_fd = wavfile_open(ss.str().c_str(), m_sample_rate); + } + + if (samples) { + if (m_channels == 1) { + int16_t *buffer = (int16_t *)alloca (2 * samples); + size_t i; + for (i = 0; i < samples; i ++) { + buffer [2 * i] = ((int16_t *)outBuffer) [i]; + buffer [2 * i + 1] = buffer [2 * i]; + } + wavfile_write(m_fd, buffer, 2*samples); + } + else if (m_channels == 2) { + wavfile_write(m_fd, outBuffer, samples); + } + else { + printf("Cannot handle %d channels\n", m_channels); + } + } + + } + return true; +} + +void FaadDecoder::close() +{ + if (m_initialised) { + wavfile_close(m_fd); + } +} + +int FaadDecoder::get_aac_channel_configuration() +{ + switch(m_mpeg_surround_config) { + case 0: // no surround + return m_aac_channel_mode ? 2 : 1; + case 1: // 5.1 + return 6; + case 2: // 7.1 + return 7; + default: + return -1; + } +} + + diff --git a/src/faad_decoder.hpp b/src/faad_decoder.hpp new file mode 100644 index 0000000..470f905 --- /dev/null +++ b/src/faad_decoder.hpp @@ -0,0 +1,100 @@ +/* + Copyright (C) 2014 Matthias P. Braendli (http://www.opendigitalradio.org) + + 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 . + + faad_decoder.h + Use libfaad to decode the AAC content of the DAB+ subchannel + + Authors: + Matthias P. Braendli +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __FAAD_DECODER_H_ +#define __FAAD_DECODER_H_ + +class FaadHandle +{ + public: + FaadHandle() + { + decoder = NeAACDecOpen(); + } + + FaadHandle(const FaadHandle&) + { + this->decoder = NeAACDecOpen(); + } + + FaadHandle& operator=(const FaadHandle&) + { + this->decoder = NeAACDecOpen(); + return *this; + } + + ~FaadHandle() + { + NeAACDecClose(decoder); + decoder = NULL; + } + + NeAACDecHandle decoder; +}; + +class FaadDecoder +{ + public: + FaadDecoder(); + + void open(std::string filename, bool ps_flag, bool aac_channel_mode, + bool dac_rate, bool sbr_flag, int mpeg_surround_config); + + void close(void); + + bool decode(std::vector > aus); + + bool is_initialised(void) { return m_initialised; } + + private: + int get_aac_channel_configuration(); + size_t m_data_len; + + std::string m_filename; + FILE* m_fd; + + /* Data needed for FAAD */ + bool m_ps_flag; + bool m_aac_channel_mode; + bool m_dac_rate; + bool m_sbr_flag; + int m_mpeg_surround_config; + + int m_channels; + int m_sample_rate; + + bool m_initialised; + FaadHandle m_faad_handle; +}; + +#endif + diff --git a/src/fig0_0.cpp b/src/fig0_0.cpp new file mode 100644 index 0000000..82bfadf --- /dev/null +++ b/src/fig0_0.cpp @@ -0,0 +1,62 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include + +// FIG 0/0 Ensemble information +// ETSI EN 300 401 6.4 +bool 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); + + return true; +} + diff --git a/src/fig0_1.cpp b/src/fig0_1.cpp new file mode 100644 index 0000000..f5df4c4 --- /dev/null +++ b/src/fig0_1.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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include + +static std::unordered_set subchannels_seen; + +bool fig0_1_is_complete(int subch_id) +{ + bool complete = subchannels_seen.count(subch_id); + + if (complete) { + subchannels_seen.clear(); + } + else { + subchannels_seen.insert(subch_id); + } + + return complete; +} + +// FIG 0/1 Basic sub-channel organization +// ETSI EN 300 401 6.2.1 +bool fig0_1(fig0_common_t& fig0, int indent) +{ + int i = 1; + uint8_t* f = fig0.f; + char desc[128]; + bool complete = false; + + while (i < fig0.figlen-3) { + // iterate over subchannels + int subch_id = f[i] >> 2; + complete |= fig0_1_is_complete(subch_id); + + 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); + } + + return complete; +} + diff --git a/src/fig0_10.cpp b/src/fig0_10.cpp new file mode 100644 index 0000000..b02edfa --- /dev/null +++ b/src/fig0_10.cpp @@ -0,0 +1,74 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include + +// FIG 0/10 Date and time +// ETSI EN 300 401 8.1.3.1 +bool 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] & 0x3) << 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); + } + + return true; +} + diff --git a/src/fig0_11.cpp b/src/fig0_11.cpp new file mode 100644 index 0000000..40136c3 --- /dev/null +++ b/src/fig0_11.cpp @@ -0,0 +1,209 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set region_ids_seen; + +bool fig0_11_is_complete(int region_id) +{ + bool complete = region_ids_seen.count(region_id); + + if (complete) { + region_ids_seen.clear(); + } + else { + region_ids_seen.insert(region_id); + } + + return complete; +} + + +// FIG 0/11 Region definition +// ETSI EN 300 401 8.1.16.1 +bool 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(); + bool complete = false; + + 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]); + complete |= fig0_11_is_complete(Region_Id); + + 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; + } + } + + return complete; +} + diff --git a/src/fig0_13.cpp b/src/fig0_13.cpp new file mode 100644 index 0000000..ffb5b89 --- /dev/null +++ b/src/fig0_13.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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +/* EN 300 401, 8.1.20, User application information + * The combination of the SId and the SCIdS provides a service component + * identifier which is valid globally. + */ +using SId_t = int; +using SCIdS_t = int; +static std::set > components_ids_seen; + +bool fig0_13_is_complete(SId_t SId, SCIdS_t SCIdS) +{ + auto key = std::make_pair(SId, SCIdS); + bool complete = components_ids_seen.count(key); + + if (complete) { + components_ids_seen.clear(); + } + else { + components_ids_seen.insert(key); + } + + return complete; +} + + +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 +bool 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]; + bool complete = false; + + 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++; + + } + + complete |= fig0_13_is_complete(SId, SCIdS); + + 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); + } + + return complete; +} + diff --git a/src/fig0_14.cpp b/src/fig0_14.cpp new file mode 100644 index 0000000..e72d121 --- /dev/null +++ b/src/fig0_14.cpp @@ -0,0 +1,81 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set subch_ids_seen; + +bool fig0_14_is_complete(int subch_id) +{ + bool complete = subch_ids_seen.count(subch_id); + + if (complete) { + subch_ids_seen.clear(); + } + else { + subch_ids_seen.insert(subch_id); + } + + return complete; +} + + +// 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 +bool fig0_14(fig0_common_t& fig0, int indent) +{ + uint8_t i = 1, SubChId, FEC_scheme; + uint8_t* f = fig0.f; + char desc[256]; + bool complete = false; + + while (i < fig0.figlen) { + // iterate over Sub-channel + SubChId = f[i] >> 2; + complete |= fig0_14_is_complete(SubChId); + 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++; + } + + return complete; +} + diff --git a/src/fig0_16.cpp b/src/fig0_16.cpp new file mode 100644 index 0000000..24c9040 --- /dev/null +++ b/src/fig0_16.cpp @@ -0,0 +1,127 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +/* SId and PNum look like good candidates to uniquely identify a FIG0_16 + */ +using SId_t = int; +using PNum_t = int; +static std::set > components_seen; + +bool fig0_16_is_complete(SId_t SId, PNum_t PNum) +{ + auto key = std::make_pair(SId, PNum); + bool complete = components_seen.count(key); + + if (complete) { + components_seen.clear(); + } + else { + components_seen.insert(key); + } + + return complete; +} + + +// FIG 0/16 Programme Number & fig0.oe() Programme Number +// ETSI EN 300 401 8.1.4 & 8.1.10.3 +bool 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; + bool complete = false; + + 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]); + complete |= fig0_16_is_complete(SId, PNum); + 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); + } + + return complete; +} + diff --git a/src/fig0_17.cpp b/src/fig0_17.cpp new file mode 100644 index 0000000..01fa954 --- /dev/null +++ b/src/fig0_17.cpp @@ -0,0 +1,138 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set services_ids_seen; + +bool fig0_17_is_complete(int services_id) +{ + bool complete = services_ids_seen.count(services_id); + + if (complete) { + services_ids_seen.clear(); + } + else { + services_ids_seen.insert(services_id); + } + + return complete; +} + +// FIG 0/17 Programme Type +// ETSI EN 300 401 8.1.5 +bool fig0_17(fig0_common_t& fig0, int indent) +{ + uint16_t SId; + uint8_t i = 1, Rfa, Language, Int_code, Comp_code; + char tmpbuf[512]; + char desc[512]; + bool SD_flag, PS_flag, L_flag, CC_flag, Rfu; + uint8_t* f = fig0.f; + bool complete = false; + + while (i < (fig0.figlen - 3)) { + // iterate over announcement support + SId = (f[i] << 8) | f[i+1]; + complete |= fig0_17_is_complete(SId); + 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); + } + + return complete; +} + diff --git a/src/fig0_18.cpp b/src/fig0_18.cpp new file mode 100644 index 0000000..46a399c --- /dev/null +++ b/src/fig0_18.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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set services_seen; + +bool fig0_18_is_complete(int services_id) +{ + bool complete = services_seen.count(services_id); + + if (complete) { + services_seen.clear(); + } + else { + services_seen.insert(services_id); + } + + return complete; +} + + +// FIG 0/18 Announcement support +// ETSI EN 300 401 8.1.6.1 +bool 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; + bool complete = false; + + 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]; + complete |= fig0_18_is_complete(SId); + 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); + } + } + } + + return complete; +} + diff --git a/src/fig0_19.cpp b/src/fig0_19.cpp new file mode 100644 index 0000000..31b4ca1 --- /dev/null +++ b/src/fig0_19.cpp @@ -0,0 +1,105 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set clusters_seen; + +bool fig0_19_is_complete(int clusters_id) +{ + bool complete = clusters_seen.count(clusters_id); + + if (complete) { + clusters_seen.clear(); + } + else { + clusters_seen.insert(clusters_id); + } + + return complete; +} + +// FIG 0/19 Announcement switching +// ETSI EN 300 401 8.1.6.2 +bool 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; + bool complete = false; + + 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]; + complete |= fig0_19_is_complete(Cluster_Id); + 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); + } + + return complete; +} + diff --git a/src/fig0_2.cpp b/src/fig0_2.cpp new file mode 100644 index 0000000..27f01ac --- /dev/null +++ b/src/fig0_2.cpp @@ -0,0 +1,168 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set services_seen; + +bool fig0_2_is_complete(int services_id) +{ + bool complete = services_seen.count(services_id); + + if (complete) { + services_seen.clear(); + } + else { + services_seen.insert(services_id); + } + + return complete; +} + +// FIG 0/2 Basic service and service component definition +// ETSI EN 300 401 6.3.1 +bool 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]; + bool complete = false; + + while (k < fig0.figlen) { + if (fig0.pd() == 0) { + sid = f[k] * 256 + f[k+1]; + ecc = 0; + 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; + } + + complete |= fig0_2_is_complete(sid); + + 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> 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; + } + } + + return complete; +} + diff --git a/src/fig0_21.cpp b/src/fig0_21.cpp new file mode 100644 index 0000000..69f18b9 --- /dev/null +++ b/src/fig0_21.cpp @@ -0,0 +1,323 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set regions_seen; + +bool fig0_21_is_complete(int region_id) +{ + bool complete = regions_seen.count(region_id); + + if (complete) { + regions_seen.clear(); + } + else { + regions_seen.insert(region_id); + } + + return complete; +} + + +// FIG 0/21 Frequency Information +// ETSI EN 300 401 8.1.8 +bool 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; + bool complete = false; + + while (i < (fig0.figlen - 1)) { + // iterate over frequency information + // decode RegionId, Length of FI list + RegionId = (f[i] << 3) | (f[i+1] >> 5); + complete |= fig0_21_is_complete(RegionId); + 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; + } + } + + return complete; +} + diff --git a/src/fig0_22.cpp b/src/fig0_22.cpp new file mode 100644 index 0000000..dfee52b --- /dev/null +++ b/src/fig0_22.cpp @@ -0,0 +1,186 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set identifiers_seen; + +bool fig0_22_is_complete(int M_S, int MainId) +{ + int identifier = (M_S << 7) | MainId; + + bool complete = identifiers_seen.count(identifier); + + if (complete) { + identifiers_seen.clear(); + } + else { + identifiers_seen.insert(identifier); + } + + return complete; +} + + +// map for fig 0/22 database +std::map 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 +bool 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; + bool complete = false; + + while (i < fig0.figlen) { + // iterate over Transmitter Identification Information (TII) fields + MS = f[i] >> 7; + MainId = f[i] & 0x7F; + complete |= fig0_22_is_complete(MS, MainId); + 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); + } + } + } + + return complete; +} + diff --git a/src/fig0_24.cpp b/src/fig0_24.cpp new file mode 100644 index 0000000..7867d9e --- /dev/null +++ b/src/fig0_24.cpp @@ -0,0 +1,109 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set services_seen; + +bool fig0_24_is_complete(int services_id) +{ + bool complete = services_seen.count(services_id); + + if (complete) { + services_seen.clear(); + } + else { + services_seen.insert(services_id); + } + + return complete; +} + +// FIG 0/24 fig0.oe() Services +// ETSI EN 300 401 8.1.10.2 +bool 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; + bool complete = false; + + 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; + } + complete |= fig0_24_is_complete(SId); + 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); + } + + return complete; +} + diff --git a/src/fig0_25.cpp b/src/fig0_25.cpp new file mode 100644 index 0000000..7859868 --- /dev/null +++ b/src/fig0_25.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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set services_seen; + +bool fig0_25_is_complete(int services_id) +{ + bool complete = services_seen.count(services_id); + + if (complete) { + services_seen.clear(); + } + else { + services_seen.insert(services_id); + } + + return complete; +} + + +// FIG 0/25 fig0.oe() Announcement support +// ETSI EN 300 401 8.1.10.5.1 +bool 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; + bool complete = false; + + 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]; + complete |= fig0_25_is_complete(SId); + 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); + } + } + } + + return complete; +} + diff --git a/src/fig0_26.cpp b/src/fig0_26.cpp new file mode 100644 index 0000000..c2e4316 --- /dev/null +++ b/src/fig0_26.cpp @@ -0,0 +1,109 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set clusters_seen; + +bool fig0_26_is_complete(int cluster_id) +{ + bool complete = clusters_seen.count(cluster_id); + + if (complete) { + clusters_seen.clear(); + } + else { + clusters_seen.insert(cluster_id); + } + + return complete; +} + + +// FIG 0/26 fig0.oe() Announcement switching +// ETSI EN 300 401 8.1.10.5.2 +bool 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; + bool complete = false; + + while (i < (fig0.figlen - 6)) { + // iterate over other ensembles announcement switching + Cluster_Id_Current_Ensemble = f[i]; + complete = fig0_26_is_complete(Cluster_Id_Current_Ensemble); + 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); + } + } + } + + return complete; +} + diff --git a/src/fig0_27.cpp b/src/fig0_27.cpp new file mode 100644 index 0000000..1f3f08d --- /dev/null +++ b/src/fig0_27.cpp @@ -0,0 +1,104 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set services_seen; + +bool fig0_27_is_complete(int services_id) +{ + bool complete = services_seen.count(services_id); + + if (complete) { + services_seen.clear(); + } + else { + services_seen.insert(services_id); + } + + return complete; +} + + +// FIG 0/27 FM Announcement support +// ETSI EN 300 401 8.1.11.2.1 +bool 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; + bool complete = false; + + while (i < (fig0.figlen - 2)) { + // iterate over FM announcement support + SId = ((uint16_t)f[i] << 8) | (uint16_t)f[i+1]; + complete |= fig0_27_is_complete(SId); + 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); + } + } + + return complete; +} + diff --git a/src/fig0_28.cpp b/src/fig0_28.cpp new file mode 100644 index 0000000..68798c4 --- /dev/null +++ b/src/fig0_28.cpp @@ -0,0 +1,90 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set clusters_seen; + +bool fig0_28_is_complete(int cluster_id) +{ + bool complete = clusters_seen.count(cluster_id); + + if (complete) { + clusters_seen.clear(); + } + else { + clusters_seen.insert(cluster_id); + } + + return complete; +} + + +// FIG 0/28 FM Announcement switching +// ETSI EN 300 401 8.1.11.2.2 +bool 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; + bool complete = false; + + while (i < (fig0.figlen - 3)) { + // iterate over FM announcement switching + Cluster_Id_Current_Ensemble = f[i]; + complete = fig0_28_is_complete(Cluster_Id_Current_Ensemble); + 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; + } + + return complete; +} + diff --git a/src/fig0_3.cpp b/src/fig0_3.cpp new file mode 100644 index 0000000..adc502f --- /dev/null +++ b/src/fig0_3.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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include + +static std::unordered_set components_ids_seen; + +bool fig0_3_is_complete(int components_id) +{ + bool complete = components_ids_seen.count(components_id); + + if (complete) { + components_ids_seen.clear(); + } + else { + components_ids_seen.insert(components_id); + } + + return complete; +} + + +// FIG 0/3 Service component in packet mode with or without Conditional Access +// ETSI EN 300 401 6.3.2 +bool 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; + bool complete = false; + + 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); + complete |= fig0_3_is_complete(SCId); + 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); + } + + return complete; +} + diff --git a/src/fig0_31.cpp b/src/fig0_31.cpp new file mode 100644 index 0000000..cbaf296 --- /dev/null +++ b/src/fig0_31.cpp @@ -0,0 +1,133 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set figtype_flags_seen; + +bool fig0_31_is_complete(uint64_t figtype_flags) +{ + bool complete = figtype_flags_seen.count(figtype_flags); + + if (complete) { + figtype_flags_seen.clear(); + } + else { + figtype_flags_seen.insert(figtype_flags); + } + + return complete; +} + +// FIG 0/31 FIC re-direction +// ETSI EN 300 401 8.1.12 +bool 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; + bool complete = false; + + 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]; + + uint64_t key = ((uint64_t)FIG_type1_flag_field << 32) | ((uint64_t)FIG_type2_flag_field << 40) | FIG_type0_flag_field; + complete |= fig0_31_is_complete(key); + + 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); + } + + return complete; +} + diff --git a/src/fig0_5.cpp b/src/fig0_5.cpp new file mode 100644 index 0000000..8d966a6 --- /dev/null +++ b/src/fig0_5.cpp @@ -0,0 +1,114 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set components_seen; + +bool fig0_5_is_complete(int components_id) +{ + bool complete = components_seen.count(components_id); + + if (complete) { + components_seen.clear(); + } + else { + components_seen.insert(components_id); + } + + return complete; +} + +// FIG 0/5 Service component language +// ETSI EN 300 401 8.1.2 +bool 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; + bool complete = false; + + 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)); + } + + int key = (MSC_FIC_flag << 7) | (f[i] % 0x3F); + complete |= fig0_5_is_complete(key); + 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]; + int key = (LS_flag << 15) | SCId; + complete |= fig0_5_is_complete(key); + 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; + } + } + + return complete; +} + diff --git a/src/fig0_6.cpp b/src/fig0_6.cpp new file mode 100644 index 0000000..dc35640 --- /dev/null +++ b/src/fig0_6.cpp @@ -0,0 +1,190 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +static std::unordered_set links_seen; + +bool fig0_6_is_complete(int link_key) +{ + bool complete = links_seen.count(link_key); + + if (complete) { + links_seen.clear(); + } + else { + links_seen.insert(link_key); + } + + return complete; +} + +// map between fig 0/6 database key and LA to detect activation and deactivation of links +static std::map 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 +bool 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; + bool complete = false; + + 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; + complete |= fig0_6_is_complete(key); + 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; + } + } + } + } + + return complete; +} + diff --git a/src/fig0_8.cpp b/src/fig0_8.cpp new file mode 100644 index 0000000..60f2e0c --- /dev/null +++ b/src/fig0_8.cpp @@ -0,0 +1,147 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include + +/* EN 300 401, 8.1.14.3 Service component label + * The combination of the SId and the SCIdS provides a service component + * identifier which is valid globally. + */ +using SId_t = int; +using SCIdS_t = int; +static std::set > components_seen; + +bool fig0_8_is_complete(SId_t SId, SCIdS_t SCIdS) +{ + auto key = std::make_pair(SId, SCIdS); + bool complete = components_seen.count(key); + + if (complete) { + components_seen.clear(); + } + else { + components_seen.insert(key); + } + + return complete; +} + + + +// FIG 0/8 Service component global definition +// ETSI EN 300 401 6.3.5 +bool 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; + bool complete = false; + + 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; + complete |= fig0_8_is_complete(SId, SCIdS); + 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 < 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); + } + + return complete; +} + diff --git a/src/fig0_9.cpp b/src/fig0_9.cpp new file mode 100644 index 0000000..5c8271c --- /dev/null +++ b/src/fig0_9.cpp @@ -0,0 +1,133 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include + +// 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 +bool 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, "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); + } + } + } + } + + return true; +} + diff --git a/src/fig1.cpp b/src/fig1.cpp new file mode 100644 index 0000000..6bb095c --- /dev/null +++ b/src/fig1.cpp @@ -0,0 +1,157 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include + +using namespace std; + +// SHORT LABELS +bool fig1_select(fig1_common_t& fig1, int indent) +{ + uint16_t ext,oe,charset; + uint16_t flag; + char label[17]; + char desc[256]; + uint8_t* f = fig1.f; + const int figtype = 1; + + charset = (f[0] & 0xF0) >> 4; + oe = (f[0] & 0x08) >> 3; + ext = f[0] & 0x07; + sprintf(desc, + "FIG %d/%d: OE=%d, Charset=%d", + figtype, ext, oe, charset); + + printbuf(desc, indent, f+1, fig1.figlen-1); + memcpy(label, f+fig1.figlen-18, 16); + label[16] = 0x00; + flag = f[fig1.figlen-2] * 256 + \ + f[fig1.figlen-1]; + + switch (ext) { + case 0: // FIG 1/0 Ensemble label + { // ETSI EN 300 401 8.1.13 + uint16_t eid; + eid = f[1] * 256 + f[2]; + sprintf(desc, "Ensemble ID 0x%04X label: \"%s\", Short label mask: 0x%04X", eid, label, flag); + printinfo(desc, indent+1, 1); + } + break; + + case 1: // FIG 1/1 Programme service label + { // ETSI EN 300 401 8.1.14.1 + uint16_t sid; + sid = f[1] * 256 + f[2]; + sprintf(desc, "Service ID 0x%X label: \"%s\", Short label mask: 0x%04X", sid, label, flag); + printinfo(desc, indent+1, 1); + } + break; + + case 4: // FIG 1/4 Service component label + { // ETSI EN 300 401 8.1.14.3 + uint32_t sid; + uint8_t pd, SCIdS; + pd = (f[1] & 0x80) >> 7; + SCIdS = f[1] & 0x0F; + if (pd == 0) { + sid = f[2] * 256 + \ + f[3]; + } + else { + sid = f[2] * 256 * 256 * 256 + \ + f[3] * 256 * 256 + \ + f[4] * 256 + \ + f[5]; + } + sprintf(desc, + "Service ID 0x%X , Service Component ID 0x%04X Short, label: \"%s\", label mask: 0x%04X", + sid, SCIdS, label, flag); + printinfo(desc, indent+1, 1); + } + break; + + case 5: // FIG 1/5 Data service label + { // ETSI EN 300 401 8.1.14.2 + uint32_t sid; + sid = f[1] * 256 * 256 * 256 + \ + f[2] * 256 * 256 + \ + f[3] * 256 + \ + f[4]; + + sprintf(desc, + "Service ID 0x%X label: \"%s\", Short label mask: 0x%04X", + sid, label, flag); + printinfo(desc, indent+1, 1); + } + break; + + + case 6: // FIG 1/6 X-PAD user application label + { // ETSI EN 300 401 8.1.14.4 + uint32_t sid; + uint8_t pd, SCIdS, xpadapp; + string xpadappdesc; + + pd = (f[1] & 0x80) >> 7; + SCIdS = f[1] & 0x0F; + if (pd == 0) { + sid = f[2] * 256 + \ + f[3]; + xpadapp = f[4] & 0x1F; + } + else { + sid = f[2] * 256 * 256 * 256 + \ + f[3] * 256 * 256 + \ + f[4] * 256 + \ + f[5]; + xpadapp = f[6] & 0x1F; + } + + if (xpadapp == 2) { + xpadappdesc = "DLS"; + } + else if (xpadapp == 12) { + xpadappdesc = "MOT"; + } + else { + xpadappdesc = "?"; + } + + + sprintf(desc,"Service ID 0x%X , Service Component ID 0x%04X Short, X-PAD App %02X (%s), label: \"%s\", label mask: 0x%04X", + sid, SCIdS, xpadapp, xpadappdesc.c_str(), label, flag); + printbuf(desc,indent+1,NULL,0,""); + } + break; + } + + // FIG1s always contain a complete set of information + return true; +} + diff --git a/src/figs.cpp b/src/figs.cpp new file mode 100644 index 0000000..a1557d7 --- /dev/null +++ b/src/figs.cpp @@ -0,0 +1,114 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "figs.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(); +} + + +bool fig0_select(fig0_common_t& fig0, int indent) +{ + switch (fig0.ext()) { + case 0: return fig0_0(fig0, indent); break; + case 1: return fig0_1(fig0, indent); break; + case 2: return fig0_2(fig0, indent); break; + case 3: return fig0_3(fig0, indent); break; + case 5: return fig0_5(fig0, indent); break; + case 6: return fig0_6(fig0, indent); break; + case 8: return fig0_8(fig0, indent); break; + case 9: return fig0_9(fig0, indent); break; + case 10: return fig0_10(fig0, indent); break; + case 11: return fig0_11(fig0, indent); break; + case 13: return fig0_13(fig0, indent); break; + case 14: return fig0_14(fig0, indent); break; + case 16: return fig0_16(fig0, indent); break; + case 17: return fig0_17(fig0, indent); break; + case 18: return fig0_18(fig0, indent); break; + case 19: return fig0_19(fig0, indent); break; + case 21: return fig0_21(fig0, indent); break; + case 22: return fig0_22(fig0, indent); break; + case 24: return fig0_24(fig0, indent); break; + case 25: return fig0_25(fig0, indent); break; + case 26: return fig0_26(fig0, indent); break; + case 27: return fig0_27(fig0, indent); break; + case 28: return fig0_28(fig0, indent); break; + case 31: return 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; + } + } + + return false; +} + diff --git a/src/figs.hpp b/src/figs.hpp new file mode 100644 index 0000000..7bd5fd0 --- /dev/null +++ b/src/figs.hpp @@ -0,0 +1,113 @@ +/* + 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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#pragma once + +#include +#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; } +}; + +struct fig1_common_t { + fig1_common_t( + uint8_t* fig_data, + uint16_t fig_len) : + f(fig_data), + figlen(fig_len) {} + + uint8_t* f; + uint16_t figlen; + + uint8_t charset() { return (f[0] & 0xF0) >> 4; } + uint8_t oe() { return (f[0] & 0x08) >> 3; } + uint8_t ext() { return f[0] & 0x07; } +}; + +// 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(); + +bool fig0_select(fig0_common_t& fig0, int indent); + +bool fig0_0(fig0_common_t& fig0, int indent); +bool fig0_1(fig0_common_t& fig0, int indent); +bool fig0_2(fig0_common_t& fig0, int indent); +bool fig0_3(fig0_common_t& fig0, int indent); +bool fig0_5(fig0_common_t& fig0, int indent); +void fig0_6_cleardb(); +bool fig0_6(fig0_common_t& fig0, int indent); +bool fig0_8(fig0_common_t& fig0, int indent); +bool fig0_9(fig0_common_t& fig0, int indent); +bool fig0_10(fig0_common_t& fig0, int indent); +bool fig0_11(fig0_common_t& fig0, int indent); +bool fig0_13(fig0_common_t& fig0, int indent); +bool fig0_14(fig0_common_t& fig0, int indent); +bool fig0_16(fig0_common_t& fig0, int indent); +bool fig0_17(fig0_common_t& fig0, int indent); +bool fig0_18(fig0_common_t& fig0, int indent); +bool fig0_19(fig0_common_t& fig0, int indent); +bool fig0_21(fig0_common_t& fig0, int indent); +void fig0_22_cleardb(); +bool fig0_22(fig0_common_t& fig0, int indent); +bool fig0_24(fig0_common_t& fig0, int indent); +bool fig0_25(fig0_common_t& fig0, int indent); +bool fig0_26(fig0_common_t& fig0, int indent); +bool fig0_27(fig0_common_t& fig0, int indent); +bool fig0_28(fig0_common_t& fig0, int indent); +bool fig0_31(fig0_common_t& fig0, int indent); + +bool fig1_select(fig1_common_t& fig1, int indent); + diff --git a/src/firecode.c b/src/firecode.c new file mode 100644 index 0000000..47ee976 --- /dev/null +++ b/src/firecode.c @@ -0,0 +1,50 @@ +/* + Copyright (C) 2014 Matthias P. Braendli (http://www.opendigitalradio.org) + + 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 . + + firecode.c + Implement a FireCode CRC calculator + + Authors: + Matthias P. Braendli +*/ + +#include "firecode.h" + +uint16_t firecode_crc(uint8_t* buf, size_t size) +{ + int crc; + int gen_poly; + + crc = 0x0000; + gen_poly = 0x782F; // 0111 1000 0010 1111 (16, 14, 13, 12, 11, 5, 3, 2, 1, 0) + + for (size_t len = 0; len < size; len++) { + for (int i = 0x80; i != 0; i >>= 1) { + if ((crc & 0x8000) != 0) { + crc = (crc << 1) ^ gen_poly; + } + else { + crc = crc << 1; + } + if ((buf[len] & i) != 0) { + crc ^= gen_poly; + } + } + } + + return crc & 0xFFFF; +} + diff --git a/src/firecode.h b/src/firecode.h new file mode 100644 index 0000000..735a302 --- /dev/null +++ b/src/firecode.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2014 Matthias P. Braendli (http://www.opendigitalradio.org) + + 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 . + + firecode.c + Implement a FireCode CRC calculator + + Authors: + Matthias P. Braendli +*/ + +#ifndef _FIRECODE_H_ +#define _FIRECODE_H_ + +#include +#include + +uint16_t firecode_crc(uint8_t* buf, size_t size); + +#endif + diff --git a/src/lib_crc.c b/src/lib_crc.c new file mode 100644 index 0000000..26b6b99 --- /dev/null +++ b/src/lib_crc.c @@ -0,0 +1,459 @@ +#include "lib_crc.h" + + + + /*******************************************************************\ + * * + * Library : lib_crc * + * File : lib_crc.c * + * Author : Lammert Bies 1999-2008 * + * E-mail : info@lammertbies.nl * + * Language : ANSI C * + * * + * * + * Description * + * =========== * + * * + * The file lib_crc.c contains the private and public func- * + * tions used for the calculation of CRC-16, CRC-CCITT and * + * CRC-32 cyclic redundancy values. * + * * + * * + * Dependencies * + * ============ * + * * + * lib_crc.h CRC definitions and prototypes * + * * + * * + * Modification history * + * ==================== * + * * + * Date Version Comment * + * * + * 2008-04-20 1.16 Added CRC-CCITT calculation for Kermit * + * * + * 2007-04-01 1.15 Added CRC16 calculation for Modbus * + * * + * 2007-03-28 1.14 Added CRC16 routine for Sick devices * + * * + * 2005-12-17 1.13 Added CRC-CCITT with initial 0x1D0F * + * * + * 2005-05-14 1.12 Added CRC-CCITT with start value 0 * + * * + * 2005-02-05 1.11 Fixed bug in CRC-DNP routine * + * * + * 2005-02-04 1.10 Added CRC-DNP routines * + * * + * 1999-02-21 1.01 Added FALSE and TRUE mnemonics * + * * + * 1999-01-22 1.00 Initial source * + * * + \*******************************************************************/ + + + + /*******************************************************************\ + * * + * #define P_xxxx * + * * + * The CRC's are computed using polynomials. The coefficients * + * for the algorithms are defined by the following constants. * + * * + \*******************************************************************/ + +#define P_16 0xA001 +#define P_32 0xEDB88320L +#define P_CCITT 0x1021 +#define P_DNP 0xA6BC +#define P_KERMIT 0x8408 +#define P_SICK 0x8005 + + + + /*******************************************************************\ + * * + * static int crc_tab...init * + * static unsigned ... crc_tab...[] * + * * + * The algorithms use tables with precalculated values. This * + * speeds up the calculation dramaticaly. The first time the * + * CRC function is called, the table for that specific calcu- * + * lation is set up. The ...init variables are used to deter- * + * mine if the initialization has taken place. The calculated * + * values are stored in the crc_tab... arrays. * + * * + * The variables are declared static. This makes them invisi- * + * ble for other modules of the program. * + * * + \*******************************************************************/ + +static int crc_tab16_init = FALSE; +static int crc_tab32_init = FALSE; +static int crc_tabccitt_init = FALSE; +static int crc_tabdnp_init = FALSE; +static int crc_tabkermit_init = FALSE; + +static unsigned short crc_tab16[256]; +static unsigned long crc_tab32[256]; +static unsigned short crc_tabccitt[256]; +static unsigned short crc_tabdnp[256]; +static unsigned short crc_tabkermit[256]; + + + + /*******************************************************************\ + * * + * static void init_crc...tab(); * + * * + * Three local functions are used to initialize the tables * + * with values for the algorithm. * + * * + \*******************************************************************/ + +static void init_crc16_tab( void ); +static void init_crc32_tab( void ); +static void init_crcccitt_tab( void ); +static void init_crcdnp_tab( void ); +static void init_crckermit_tab( void ); + + + + /*******************************************************************\ + * * + * unsigned short update_crc_ccitt( unsigned long crc, char c ); * + * * + * The function update_crc_ccitt calculates a new CRC-CCITT * + * value based on the previous value of the CRC and the next * + * byte of the data to be checked. * + * * + \*******************************************************************/ + +unsigned short update_crc_ccitt( unsigned short crc, char c ) { + + unsigned short tmp, short_c; + + short_c = 0x00ff & (unsigned short) c; + + if ( ! crc_tabccitt_init ) init_crcccitt_tab(); + + tmp = (crc >> 8) ^ short_c; + crc = (crc << 8) ^ crc_tabccitt[tmp]; + + return crc; + +} /* update_crc_ccitt */ + + + + /*******************************************************************\ + * * + * unsigned short update_crc_sick( * + * unsigned long crc, char c, char prev_byte ); * + * * + * The function update_crc_sick calculates a new CRC-SICK * + * value based on the previous value of the CRC and the next * + * byte of the data to be checked. * + * * + \*******************************************************************/ + +unsigned short update_crc_sick( unsigned short crc, char c, char prev_byte ) { + + unsigned short short_c, short_p; + + short_c = 0x00ff & (unsigned short) c; + short_p = ( 0x00ff & (unsigned short) prev_byte ) << 8; + + if ( crc & 0x8000 ) crc = ( crc << 1 ) ^ P_SICK; + else crc = crc << 1; + + crc &= 0xffff; + crc ^= ( short_c | short_p ); + + return crc; + +} /* update_crc_sick */ + + + + /*******************************************************************\ + * * + * unsigned short update_crc_16( unsigned short crc, char c ); * + * * + * The function update_crc_16 calculates a new CRC-16 value * + * based on the previous value of the CRC and the next byte * + * of the data to be checked. * + * * + \*******************************************************************/ + +unsigned short update_crc_16( unsigned short crc, char c ) { + + unsigned short tmp, short_c; + + short_c = 0x00ff & (unsigned short) c; + + if ( ! crc_tab16_init ) init_crc16_tab(); + + tmp = crc ^ short_c; + crc = (crc >> 8) ^ crc_tab16[ tmp & 0xff ]; + + return crc; + +} /* update_crc_16 */ + + + + /*******************************************************************\ + * * + * unsigned short update_crc_kermit( unsigned short crc, char c ); * + * * + * The function update_crc_kermit calculates a new CRC value * + * based on the previous value of the CRC and the next byte * + * of the data to be checked. * + * * + \*******************************************************************/ + +unsigned short update_crc_kermit( unsigned short crc, char c ) { + + unsigned short tmp, short_c; + + short_c = 0x00ff & (unsigned short) c; + + if ( ! crc_tabkermit_init ) init_crckermit_tab(); + + tmp = crc ^ short_c; + crc = (crc >> 8) ^ crc_tabkermit[ tmp & 0xff ]; + + return crc; + +} /* update_crc_kermit */ + + + + /*******************************************************************\ + * * + * unsigned short update_crc_dnp( unsigned short crc, char c ); * + * * + * The function update_crc_dnp calculates a new CRC-DNP value * + * based on the previous value of the CRC and the next byte * + * of the data to be checked. * + * * + \*******************************************************************/ + +unsigned short update_crc_dnp( unsigned short crc, char c ) { + + unsigned short tmp, short_c; + + short_c = 0x00ff & (unsigned short) c; + + if ( ! crc_tabdnp_init ) init_crcdnp_tab(); + + tmp = crc ^ short_c; + crc = (crc >> 8) ^ crc_tabdnp[ tmp & 0xff ]; + + return crc; + +} /* update_crc_dnp */ + + + + /*******************************************************************\ + * * + * unsigned long update_crc_32( unsigned long crc, char c ); * + * * + * The function update_crc_32 calculates a new CRC-32 value * + * based on the previous value of the CRC and the next byte * + * of the data to be checked. * + * * + \*******************************************************************/ + +unsigned long update_crc_32( unsigned long crc, char c ) { + + unsigned long tmp, long_c; + + long_c = 0x000000ffL & (unsigned long) c; + + if ( ! crc_tab32_init ) init_crc32_tab(); + + tmp = crc ^ long_c; + crc = (crc >> 8) ^ crc_tab32[ tmp & 0xff ]; + + return crc; + +} /* update_crc_32 */ + + + + /*******************************************************************\ + * * + * static void init_crc16_tab( void ); * + * * + * The function init_crc16_tab() is used to fill the array * + * for calculation of the CRC-16 with values. * + * * + \*******************************************************************/ + +static void init_crc16_tab( void ) { + + int i, j; + unsigned short crc, c; + + for (i=0; i<256; i++) { + + crc = 0; + c = (unsigned short) i; + + for (j=0; j<8; j++) { + + if ( (crc ^ c) & 0x0001 ) crc = ( crc >> 1 ) ^ P_16; + else crc = crc >> 1; + + c = c >> 1; + } + + crc_tab16[i] = crc; + } + + crc_tab16_init = TRUE; + +} /* init_crc16_tab */ + + + + /*******************************************************************\ + * * + * static void init_crckermit_tab( void ); * + * * + * The function init_crckermit_tab() is used to fill the array * + * for calculation of the CRC Kermit with values. * + * * + \*******************************************************************/ + +static void init_crckermit_tab( void ) { + + int i, j; + unsigned short crc, c; + + for (i=0; i<256; i++) { + + crc = 0; + c = (unsigned short) i; + + for (j=0; j<8; j++) { + + if ( (crc ^ c) & 0x0001 ) crc = ( crc >> 1 ) ^ P_KERMIT; + else crc = crc >> 1; + + c = c >> 1; + } + + crc_tabkermit[i] = crc; + } + + crc_tabkermit_init = TRUE; + +} /* init_crckermit_tab */ + + + + /*******************************************************************\ + * * + * static void init_crcdnp_tab( void ); * + * * + * The function init_crcdnp_tab() is used to fill the array * + * for calculation of the CRC-DNP with values. * + * * + \*******************************************************************/ + +static void init_crcdnp_tab( void ) { + + int i, j; + unsigned short crc, c; + + for (i=0; i<256; i++) { + + crc = 0; + c = (unsigned short) i; + + for (j=0; j<8; j++) { + + if ( (crc ^ c) & 0x0001 ) crc = ( crc >> 1 ) ^ P_DNP; + else crc = crc >> 1; + + c = c >> 1; + } + + crc_tabdnp[i] = crc; + } + + crc_tabdnp_init = TRUE; + +} /* init_crcdnp_tab */ + + + + /*******************************************************************\ + * * + * static void init_crc32_tab( void ); * + * * + * The function init_crc32_tab() is used to fill the array * + * for calculation of the CRC-32 with values. * + * * + \*******************************************************************/ + +static void init_crc32_tab( void ) { + + int i, j; + unsigned long crc; + + for (i=0; i<256; i++) { + + crc = (unsigned long) i; + + for (j=0; j<8; j++) { + + if ( crc & 0x00000001L ) crc = ( crc >> 1 ) ^ P_32; + else crc = crc >> 1; + } + + crc_tab32[i] = crc; + } + + crc_tab32_init = TRUE; + +} /* init_crc32_tab */ + + + + /*******************************************************************\ + * * + * static void init_crcccitt_tab( void ); * + * * + * The function init_crcccitt_tab() is used to fill the array * + * for calculation of the CRC-CCITT with values. * + * * + \*******************************************************************/ + +static void init_crcccitt_tab( void ) { + + int i, j; + unsigned short crc, c; + + for (i=0; i<256; i++) { + + crc = 0; + c = ((unsigned short) i) << 8; + + for (j=0; j<8; j++) { + + if ( (crc ^ c) & 0x8000 ) crc = ( crc << 1 ) ^ P_CCITT; + else crc = crc << 1; + + c = c << 1; + } + + crc_tabccitt[i] = crc; + } + + crc_tabccitt_init = TRUE; + +} /* init_crcccitt_tab */ diff --git a/src/lib_crc.h b/src/lib_crc.h new file mode 100644 index 0000000..ea5ca76 --- /dev/null +++ b/src/lib_crc.h @@ -0,0 +1,66 @@ + /*******************************************************************\ + * * + * Library : lib_crc * + * File : lib_crc.h * + * Author : Lammert Bies 1999-2008 * + * E-mail : info@lammertbies.nl * + * Language : ANSI C * + * * + * * + * Description * + * =========== * + * * + * The file lib_crc.h contains public definitions and proto- * + * types for the CRC functions present in lib_crc.c. * + * * + * * + * Dependencies * + * ============ * + * * + * none * + * * + * * + * Modification history * + * ==================== * + * * + * Date Version Comment * + * * + * 2008-04-20 1.16 Added CRC-CCITT routine for Kermit * + * * + * 2007-04-01 1.15 Added CRC16 calculation for Modbus * + * * + * 2007-03-28 1.14 Added CRC16 routine for Sick devices * + * * + * 2005-12-17 1.13 Added CRC-CCITT with initial 0x1D0F * + * * + * 2005-02-14 1.12 Added CRC-CCITT with initial 0x0000 * + * * + * 2005-02-05 1.11 Fixed bug in CRC-DNP routine * + * * + * 2005-02-04 1.10 Added CRC-DNP routines * + * * + * 2005-01-07 1.02 Changes in tst_crc.c * + * * + * 1999-02-21 1.01 Added FALSE and TRUE mnemonics * + * * + * 1999-01-22 1.00 Initial source * + * * + \*******************************************************************/ + + + +#define CRC_VERSION "1.16" + + + +#define FALSE 0 +#define TRUE 1 + + + +unsigned short update_crc_16( unsigned short crc, char c ); +unsigned long update_crc_32( unsigned long crc, char c ); +unsigned short update_crc_ccitt( unsigned short crc, char c ); +unsigned short update_crc_dnp( unsigned short crc, char c ); +unsigned short update_crc_kermit( unsigned short crc, char c ); +unsigned short update_crc_sick( unsigned short crc, char c, char prev_byte ); diff --git a/src/repetitionrate.cpp b/src/repetitionrate.cpp new file mode 100644 index 0000000..970bcfc --- /dev/null +++ b/src/repetitionrate.cpp @@ -0,0 +1,196 @@ +/* + Copyright (C) 2016 Matthias P. Braendli (http://www.opendigitalradio.org) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Authors: + Matthias P. Braendli + +*/ + +#include "repetitionrate.hpp" +#include +#include +#include +#include +#include + +using namespace std; + +const double FRAME_DURATION = 24e-3; + +struct FIGTypeExt { + int figtype; + int figextension; + + bool operator<(const FIGTypeExt& other) const { + if (this->figtype == other.figtype) { + return this->figextension < other.figextension; + } + else { + return this->figtype < other.figtype ; + } + } +}; + +struct FIGRateInfo { + // List of frame numbers in which the FIG is present + vector frames_present; + + // List of frame numbers in which a complete DB for that FIG has been sent + vector frames_complete; + + // Which FIBs this FIG was seen in + set in_fib; +}; + + +static map fig_rates; + +static int current_frame_number = 0; +static int current_fib = 0; + +void rate_announce_fig(int figtype, int figextension, bool complete) +{ + FIGTypeExt f = {.figtype = figtype, .figextension = figextension}; + + if (fig_rates.count(f) == 0) { + FIGRateInfo rate; + fig_rates[f] = rate; + } + + FIGRateInfo& rate = fig_rates[f]; + + rate.frames_present.push_back(current_frame_number); + if (complete) { + rate.frames_complete.push_back(current_frame_number); + } + rate.in_fib.insert(current_fib); +} + +// Calculate the minimal, maximum and average repetition rate (FIGs per +// second). +void rate_min_max_avg( + const std::vector& fig_positions, + double* min, + double* max, + double* avg, + bool per_second) +{ + double avg_interval = + (double)(fig_positions.back() - fig_positions.front()) / + (double)(fig_positions.size() - 1); + + int min_delta = std::numeric_limits::max(); + int max_delta = 0; + for (size_t i = 1; i < fig_positions.size(); i++) { + const int delta = fig_positions[i] - fig_positions[i-1]; + + // Min + if (min_delta > delta) { + min_delta = delta; + } + + // Max + if (max_delta < delta) { + max_delta = delta; + } + } + + *avg = avg_interval; + *min = min_delta; + *max = max_delta; + + if (per_second) { + *avg = 1.0 / + (*avg * FRAME_DURATION); + *min = 1.0 / (*min * FRAME_DURATION); + *max = 1.0 / (*min * FRAME_DURATION); + } + +} + +void rate_display_header(bool per_second) +{ + if (per_second) { + printf("FIG carousel analysis. Format:\n" + " min, average, max FIGs per second (total count) - \n" + " min, average, max complete FIGs per second (total count)\n"); + } + else { + printf("FIG carousel analysis. Format:\n" + " min, average, max frames per FIG (total count) - \n" + " min, average, max frames per complete FIGs (total count)\n"); + } +} + +void rate_display_analysis(bool clear, bool per_second) +{ + for (auto& fig_rate : fig_rates) { + auto& frames_present = fig_rate.second.frames_present; + auto& frames_complete = fig_rate.second.frames_complete; + + double min = 0.0; + double max = 0.0; + double avg = 0.0; + + const size_t n_present = frames_present.size(); + const size_t n_complete = frames_complete.size(); + if (n_present >= 2) { + + rate_min_max_avg(frames_present, &min, &max, &avg, per_second); + + printf("FIG%2d/%2d %4.2f %4.2f %4.2f (%5zu)", + fig_rate.first.figtype, fig_rate.first.figextension, + min, avg, max, + n_present); + + if (n_complete >= 2) { + rate_min_max_avg(frames_complete, &min, &max, &avg, per_second); + + printf(" - %4.2f %4.2f %4.2f (%5zu)", + min, avg, max, + n_present); + } + else { + printf(" - None complete"); + } + } + else { + printf("FIG%2d/%2d 0", + fig_rate.first.figtype, fig_rate.first.figextension); + } + + printf(" - in FIB(s):"); + for (auto& fib : fig_rate.second.in_fib) { + printf(" %d", fib); + } + printf("\n"); + + } + + if (clear) { + fig_rates.clear(); + } +} + +void rate_new_fib(int fib) +{ + if (fib == 0) { + current_frame_number++; + } + + current_fib = fib; +} + diff --git a/src/repetitionrate.hpp b/src/repetitionrate.hpp new file mode 100644 index 0000000..41b5b40 --- /dev/null +++ b/src/repetitionrate.hpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2016 Matthias P. Braendli (http://www.opendigitalradio.org) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Authors: + Matthias P. Braendli + +*/ + +#pragma once +#include "utils.hpp" + +/* Tell the repetition rate analyser that we have received a given FIG. + * The complete flag should be set to true every time a complete + * set of information for that FIG has been received + */ +void rate_announce_fig(int figtype, int figextension, bool complete); + +/* Tell the repetition rate analyser that a new FIB starts. + */ +void rate_new_fib(int fib); + +/* Print small header to explain rate display + * per_second: if true, rates are calculated in FIGs per second. + * If false, rate is given in frames per FIG + */ +void rate_display_header(bool per_second); + +/* Print analysis. + * optionally clear all statistics + * per_second: if true, rates are calculated in FIGs per second. + * If false, rate is given in frames per FIG + */ +void rate_display_analysis(bool clear, bool per_second); + diff --git a/src/rsdecoder.cpp b/src/rsdecoder.cpp new file mode 100644 index 0000000..98ccd39 --- /dev/null +++ b/src/rsdecoder.cpp @@ -0,0 +1,83 @@ +/* + Copyright (C) 2015 Stefan Pöschel + + Copyright (C) 2015 Matthias P. Braendli (http://www.opendigitalradio.org) + + 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 . + */ + +#include +#include "rsdecoder.hpp" + +RSDecoder::RSDecoder() +{ + rs_handle = init_rs_char(8, 0x11D, 0, 1, 10, 135); + if(!rs_handle) + throw std::runtime_error("RSDecoder: error while init_rs_char"); +} + +RSDecoder::~RSDecoder() +{ + free_rs_char(rs_handle); +} + +int RSDecoder::DecodeSuperframe(std::vector &sf, int subch_index) +{ + int total_corr_count = 0; + bool uncorr_errors = false; + + std::vector errors_per_index(subch_index); + + // process all RS packets + for(int i = 0; i < subch_index; i++) { + for(int pos = 0; pos < 120; pos++) + rs_packet[pos] = sf[pos * subch_index + i]; + + // detect errors + int corr_count = decode_rs_char(rs_handle, rs_packet, corr_pos, 0); + errors_per_index[i] = corr_count; + + if(corr_count == -1) { + uncorr_errors = true; + } + else { + total_corr_count += corr_count; + } + + // correct errors + for(int j = 0; j < corr_count; j++) { + + int pos = corr_pos[j] - 135; + if(pos < 0) + continue; + + // fprintf(stderr, "j: %d, pos: %d, sf-index: %d\n", j, pos, pos * subch_index + i); + sf[pos * subch_index + i] = rs_packet[pos]; + } + } + + // output statistics + if (total_corr_count || uncorr_errors) { + printf("RS uncorrected errors:\n"); + for (size_t i = 0; i < errors_per_index.size(); i++) { + int e = errors_per_index[i]; + printf(" (%zu: %d)", i, e); + } + printf("\n"); + } + + return uncorr_errors ? -1 : total_corr_count; +} + + diff --git a/src/rsdecoder.hpp b/src/rsdecoder.hpp new file mode 100644 index 0000000..ca97c91 --- /dev/null +++ b/src/rsdecoder.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2015 Stefan Pöschel + + Copyright (C) 2015 Matthias P. Braendli (http://www.opendigitalradio.org) + + 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 . + */ + +#include +#include + +extern "C" { +#include +} + +class RSDecoder { + private: + void *rs_handle; + uint8_t rs_packet[120]; + int corr_pos[10]; + public: + RSDecoder(); + ~RSDecoder(); + + /* Correct errors using reed-solomon decoder. + * Returns number of errors corrected, or -1 if some errors could not + * be corrected + */ + int DecodeSuperframe(std::vector &sf, int subch_index); +}; + + diff --git a/src/tables.cpp b/src/tables.cpp new file mode 100644 index 0000000..ed3ecbe --- /dev/null +++ b/src/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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "tables.hpp" +#include +#include +#include + +static const std::vector 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/src/tables.hpp b/src/tables.hpp new file mode 100644 index 0000000..992aabd --- /dev/null +++ b/src/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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + 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/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..71df396 --- /dev/null +++ b/src/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 . + + etisnoop.cpp + Parse ETI NI G.703 file + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#include "utils.hpp" +#include + +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/src/utils.hpp b/src/utils.hpp new file mode 100644 index 0000000..057447f --- /dev/null +++ b/src/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 . + + etisnoop.cpp + Parse ETI NI G.703 file + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#pragma once + +#include +#include +#include + +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); + +// 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/src/watermarkdecoder.hpp b/src/watermarkdecoder.hpp new file mode 100644 index 0000000..57196e1 --- /dev/null +++ b/src/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 . + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#pragma once +#include +#include +#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 m_confind_bits; +}; + diff --git a/src/wavfile.c b/src/wavfile.c new file mode 100644 index 0000000..9ea231b --- /dev/null +++ b/src/wavfile.c @@ -0,0 +1,82 @@ +/* +A simple sound library for CSE 20211 by Douglas Thain +For course assignments, you should not change this file. +For complete documentation, see: +http://www.nd.edu/~dthain/courses/cse20211/fall2013/wavfile +*/ + +#include "wavfile.h" + +#include +#include +#include + +struct wavfile_header { + char riff_tag[4]; + int riff_length; + char wave_tag[4]; + char fmt_tag[4]; + int fmt_length; + short audio_format; + short num_channels; + int sample_rate; + int byte_rate; + short block_align; + short bits_per_sample; + char data_tag[4]; + int data_length; +}; + +FILE * wavfile_open( const char *filename, int rate ) +{ + struct wavfile_header header; + + int samples_per_second = rate; + int bits_per_sample = 16; + + strncpy(header.riff_tag,"RIFF",4); + strncpy(header.wave_tag,"WAVE",4); + strncpy(header.fmt_tag,"fmt ",4); + strncpy(header.data_tag,"data",4); + + header.riff_length = 0; + header.fmt_length = 16; + header.audio_format = 1; + header.num_channels = 2; + header.sample_rate = samples_per_second; + header.byte_rate = samples_per_second*(bits_per_sample/8); + header.block_align = bits_per_sample/8; + header.bits_per_sample = bits_per_sample; + header.data_length = 0; + + FILE * file = fopen(filename,"w+"); + if(!file) return 0; + + fwrite(&header,sizeof(header),1,file); + + fflush(file); + + return file; + +} + +void wavfile_write( FILE *file, short data[], int length ) +{ + fwrite(data,sizeof(short),length,file); +} + +void wavfile_close( FILE *file ) +{ + int file_length = ftell(file); + + int data_length = file_length - sizeof(struct wavfile_header); + fseek(file,sizeof(struct wavfile_header) - sizeof(int),SEEK_SET); + fwrite(&data_length,sizeof(data_length),1,file); + + int riff_length = file_length - 8; + fseek(file,4,SEEK_SET); + fwrite(&riff_length,sizeof(riff_length),1,file); + + fclose(file); +} + diff --git a/src/wavfile.h b/src/wavfile.h new file mode 100644 index 0000000..09e8371 --- /dev/null +++ b/src/wavfile.h @@ -0,0 +1,17 @@ +/* +A simple sound library for CSE 20211 by Douglas Thain +For course assignments, you should not change this file. +For complete documentation, see: +http://www.nd.edu/~dthain/courses/cse20211/fall2013/wavfile +*/ + +#ifndef WAVFILE_H +#define WAVFILE_H + +#include + +FILE * wavfile_open( const char *filename, int rate ); +void wavfile_write( FILE *file, short data[], int length ); +void wavfile_close( FILE * file ); + +#endif -- cgit v1.2.3