From 9bcd309e3812263a11ee13d7999e85c4e6a48ebd Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 2 Oct 2016 12:37:44 +0200 Subject: Refactor handling of FIG state out of 0/19 We are going to need this for other FIGs too --- src/fig/FIG0.cpp | 104 +++++++++++++++++++++---------------------------------- src/fig/FIG0.h | 47 +++++++++++++++---------- 2 files changed, 69 insertions(+), 82 deletions(-) diff --git a/src/fig/FIG0.cpp b/src/fig/FIG0.cpp index 4233b5b..03fb00c 100644 --- a/src/fig/FIG0.cpp +++ b/src/fig/FIG0.cpp @@ -1258,24 +1258,27 @@ FillStatus FIG0_19::fill(uint8_t *buf, size_t max_size) { using namespace std; - update_state(); + auto ensemble = m_rti->ensemble; + + // We are called every 24ms, and must timeout after 2s + const int timeout = 2000/24; + + m_transition.update_state(timeout, ensemble->clusters); FillStatus fs; ssize_t remaining = max_size; - auto ensemble = m_rti->ensemble; - FIGtype0* fig0 = NULL; // Combine all clusters into one list set allclusters; - for (const auto& cluster : m_new_announcements) { + for (const auto& cluster : m_transition.new_entries) { allclusters.insert(cluster.first.get()); } - for (const auto& cluster : m_repeated_announcements) { + for (const auto& cluster : m_transition.repeated_entries) { allclusters.insert(cluster.get()); } - for (const auto& cluster : m_disabled_announcements) { + for (const auto& cluster : m_transition.disabled_entries) { allclusters.insert(cluster.first.get()); } @@ -1341,86 +1344,59 @@ FillStatus FIG0_19::fill(uint8_t *buf, size_t max_size) return fs; } -void FIG0_19::update_state() +template +void TransitionHandler::update_state(int timeout, std::vector > all_entries) { - auto ensemble = m_rti->ensemble; - - // We are called every 24ms, and must timeout after 2s - const int timeout = 2000/24; - -//#define DEBUG_FIG0_19 -#ifdef DEBUG_FIG0_19 - etiLog.level(info) << " FIG0/19 new"; - for (const auto& cluster : m_new_announcements) { - etiLog.level(info) << " " << cluster.first->tostring() - << ": " << cluster.second; - } - etiLog.level(info) << " FIG0/19 repeated"; - for (const auto& cluster : m_repeated_announcements) { - etiLog.level(info) << " " << cluster->tostring(); - } - etiLog.level(info) << " FIG0/19 disabled"; - for (const auto& cluster : m_disabled_announcements) { - etiLog.level(info) << " " << cluster.first->tostring() - << ": " << cluster.second; - } - - etiLog.level(info) << " FIG0/19 in ensemble"; -#endif - - for (const auto& cluster : ensemble->clusters) { -#ifdef DEBUG_FIG0_19 - etiLog.level(info) << " " << cluster->tostring(); -#endif - if (cluster->is_active()) { - if (m_repeated_announcements.count(cluster) > 0) { - // We are currently announcing this cluster + for (const auto& entry : all_entries) { + if (entry->is_active()) { + if (repeated_entries.count(entry) > 0) { + // We are currently announcing this entry continue; } - if (m_new_announcements.count(cluster) > 0) { - // We are currently announcing this cluster at a + if (new_entries.count(entry) > 0) { + // We are currently announcing this entry at a // fast rate. Handle timeout: - m_new_announcements[cluster] -= 1; - if (m_new_announcements[cluster] <= 0) { - m_repeated_announcements.insert(cluster); - m_new_announcements.erase(cluster); + new_entries[entry] -= 1; + if (new_entries[entry] <= 0) { + repeated_entries.insert(entry); + new_entries.erase(entry); } continue; } // unlikely - if (m_disabled_announcements.count(cluster) > 0) { - m_new_announcements[cluster] = timeout; - m_disabled_announcements.erase(cluster); + if (disabled_entries.count(entry) > 0) { + new_entries[entry] = timeout; + disabled_entries.erase(entry); continue; } - // It's a new announcement! - m_new_announcements[cluster] = timeout; + // It's a new entry! + new_entries[entry] = timeout; } else { // Not active - if (m_disabled_announcements.count(cluster) > 0) { - m_disabled_announcements[cluster] -= 1; - if (m_disabled_announcements[cluster] <= 0) { - m_disabled_announcements.erase(cluster); + if (disabled_entries.count(entry) > 0) { + disabled_entries[entry] -= 1; + if (disabled_entries[entry] <= 0) { + disabled_entries.erase(entry); } continue; } - if (m_repeated_announcements.count(cluster) > 0) { - // We are currently announcing this cluster - m_disabled_announcements[cluster] = timeout; - m_repeated_announcements.erase(cluster); + if (repeated_entries.count(entry) > 0) { + // We are currently announcing this entry + disabled_entries[entry] = timeout; + repeated_entries.erase(entry); continue; } // unlikely - if (m_new_announcements.count(cluster) > 0) { - // We are currently announcing this cluster at a + if (new_entries.count(entry) > 0) { + // We are currently announcing this entry at a // fast rate. We must stop announcing it - m_disabled_announcements[cluster] = timeout; - m_new_announcements.erase(cluster); + disabled_entries[entry] = timeout; + new_entries.erase(entry); continue; } } @@ -1429,8 +1405,8 @@ void FIG0_19::update_state() FIG_rate FIG0_19::repetition_rate(void) { - if ( m_new_announcements.size() > 0 or - m_disabled_announcements.size() ) { + if ( m_transition.new_entries.size() > 0 or + m_transition.disabled_entries.size() ) { return FIG_rate::A_B; } else { diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h index 95a8a85..84bde41 100644 --- a/src/fig/FIG0.h +++ b/src/fig/FIG0.h @@ -34,6 +34,34 @@ namespace FIC { +// Some FIGs need to adapt their rate or their contents depending +// on if some data entries are stable or currently undergoing a +// change. The TransitionHandler keeps track of which entries +// are in what state. +template +class TransitionHandler { + public: + // update_state must be called once per ETI frame, and will + // move entries from new to repeated to disabled depending + // on their is_active() return value. + void update_state(int timeout, std::vector > all_entries); + + // The FIG that needs the information about what state an entry is in + // can read from the following data structures. It shall not modify them. + + /* Map to frame count */ + std::map< + std::shared_ptr,int> new_entries; + + std::set< + std::shared_ptr > repeated_entries; + + /* Map to frame count */ + std::map< + std::shared_ptr,int> disabled_entries; +}; + + // FIG type 0/0, Multiplex Configuration Info (MCI), // Ensemble information class FIG0_0 : public IFIG @@ -260,24 +288,7 @@ class FIG0_19 : public IFIG private: FIGRuntimeInformation *m_rti; - void update_state(void); - - /* When a new announcement gets active, it is moved into the list - * of new announcements, and gets transmitted at a faster rate for - * two seconds. - * Same for recently disabled announcements. - */ - - /* Map of cluster to frame count */ - std::map< - std::shared_ptr,int> m_new_announcements; - - std::set< - std::shared_ptr > m_repeated_announcements; - - /* Map of cluster to frame count */ - std::map< - std::shared_ptr,int> m_disabled_announcements; + TransitionHandler m_transition; }; #ifdef _WIN32 -- cgit v1.2.3 From bbc588c731dc16c6245ad7063bac5d141086a315 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 30 Sep 2016 16:13:12 +0200 Subject: Move FIG0 structures to separate file (cherry picked from commit 0360110bfb7320f3310737b6b75da5f8cd96edb9) --- src/Makefile.am | 2 +- src/fig/FIG0.cpp | 3 +- src/fig/FIG0.h | 319 +--------------------------------------------- src/fig/FIG0structs.h | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.cpp | 1 + 5 files changed, 352 insertions(+), 320 deletions(-) create mode 100644 src/fig/FIG0structs.h diff --git a/src/Makefile.am b/src/Makefile.am index 996ebb8..55510ee 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -97,7 +97,7 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \ bridge.h bridge.c \ crc.h crc.c \ fig/FIG.h fig/FIG.cpp \ - fig/FIG0.cpp fig/FIG0.h \ + fig/FIG0.cpp fig/FIG0.h fig/FIG0structs.h \ fig/FIG1.cpp fig/FIG1.h \ fig/FIGCarousel.cpp fig/FIGCarousel.h \ mpeg.h mpeg.c \ diff --git a/src/fig/FIG0.cpp b/src/fig/FIG0.cpp index 03fb00c..08867c8 100644 --- a/src/fig/FIG0.cpp +++ b/src/fig/FIG0.cpp @@ -3,7 +3,7 @@ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2015, 2016 + Copyright (C) 2016 Matthias P. Braendli, matthias.braendli@mpb.li Implementation of FIG0 @@ -26,6 +26,7 @@ */ #include "fig/FIG0.h" +#include "fig/FIG0structs.h" #include "utils.h" namespace FIC { diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h index 84bde41..fe90d31 100644 --- a/src/fig/FIG0.h +++ b/src/fig/FIG0.h @@ -23,8 +23,7 @@ along with ODR-DabMux. If not, see . */ -#ifndef __FIG0_H_ -#define __FIG0_H_ +#pragma once #include #include @@ -291,321 +290,5 @@ class FIG0_19 : public IFIG TransitionHandler m_transition; }; -#ifdef _WIN32 -# pragma pack(push) -#endif - -struct FIGtype0 { - uint8_t Length:5; - uint8_t FIGtypeNumber:3; - uint8_t Extension:5; - uint8_t PD:1; - uint8_t OE:1; - uint8_t CN:1; -} PACKED; - - -struct FIGtype0_0 { - uint8_t Length:5; - uint8_t FIGtypeNumber:3; - uint8_t Extension:5; - uint8_t PD:1; - uint8_t OE:1; - uint8_t CN:1; - - uint16_t EId; - uint8_t CIFcnt_hight:5; - uint8_t Al:1; - uint8_t Change:2; - uint8_t CIFcnt_low:8; -} PACKED; - - -struct FIGtype0_2 { - uint8_t Length:5; - uint8_t FIGtypeNumber:3; - uint8_t Extension:5; - uint8_t PD:1; - uint8_t OE:1; - uint8_t CN:1; -} PACKED; - - -struct FIGtype0_2_Service { - uint16_t SId; - uint8_t NbServiceComp:4; - uint8_t CAId:3; - uint8_t Local_flag:1; -} PACKED; - - -struct FIGtype0_2_Service_data { - uint32_t SId; - uint8_t NbServiceComp:4; - uint8_t CAId:3; - uint8_t Local_flag:1; -} PACKED; - - -struct FIGtype0_2_audio_component { - uint8_t ASCTy:6; - uint8_t TMid:2; - uint8_t CA_flag:1; - uint8_t PS:1; - uint8_t SubChId:6; -} PACKED; - - -struct FIGtype0_2_data_component { - uint8_t DSCTy:6; - uint8_t TMid:2; - uint8_t CA_flag:1; - uint8_t PS:1; - uint8_t SubChId:6; -} PACKED; - - -struct FIGtype0_2_packet_component { - uint8_t SCId_high:6; - uint8_t TMid:2; - uint8_t CA_flag:1; - uint8_t PS:1; - uint8_t SCId_low:6; - void setSCId(uint16_t SCId) { - SCId_high = SCId >> 6; - SCId_low = SCId & 0x3f; - } -} PACKED; - - -/* Warning: When bit SCCA_flag is unset(0), the multiplexer R&S does not send - * the SCCA field. But, in the Factum ETI analyzer, if this field is not there, - * it is an error. - */ -struct FIGtype0_3 { - uint8_t SCId_high; - uint8_t SCCA_flag:1; - uint8_t rfa:3; - uint8_t SCId_low:4; - uint8_t DSCTy:6; - uint8_t rfu:1; - uint8_t DG_flag:1; - uint8_t Packet_address_high:2; - uint8_t SubChId:6; - uint8_t Packet_address_low; - uint16_t SCCA; - void setSCId(uint16_t SCId) { - SCId_high = SCId >> 4; - SCId_low = SCId & 0xf; - } - void setPacketAddress(uint16_t address) { - Packet_address_high = address >> 8; - Packet_address_low = address & 0xff; - } -} PACKED; - -struct FIGtype0_5_short { - uint8_t SubChId:6; - uint8_t rfu:1; - uint8_t LS:1; - - uint8_t language; -} PACKED; - -struct FIGtype0_8_short { - uint8_t SCIdS:4; - uint8_t rfa_1:3; - uint8_t ext:1; - uint8_t Id:6; - uint8_t MscFic:1; - uint8_t LS:1; - uint8_t rfa_2; -} PACKED; - - -struct FIGtype0_8_long { - uint8_t SCIdS:4; - uint8_t rfa_1:3; - uint8_t ext:1; - uint8_t SCId_high:4; - uint8_t rfa:3; - uint8_t LS:1; - uint8_t SCId_low; - uint8_t rfa_2; - void setSCId(uint16_t id) { - SCId_high = id >> 8; - SCId_low = id & 0xff; - } - uint16_t getSCid() { - return (SCId_high << 8) | SCId_low; - } -} PACKED; - - -struct FIGtype0_9 { - uint8_t Length:5; - uint8_t FIGtypeNumber:3; - uint8_t Extension:5; - uint8_t PD:1; - uint8_t OE:1; - uint8_t CN:1; - - uint8_t ensembleLto:6; - uint8_t rfa1:1; - uint8_t ext:1; - uint8_t ensembleEcc; - uint8_t tableId; -} PACKED; - - -struct FIGtype0_10_LongForm { - uint8_t Length:5; - uint8_t FIGtypeNumber:3; - - uint8_t Extension:5; - uint8_t PD:1; - uint8_t OE:1; - uint8_t CN:1; - - uint8_t MJD_high:7; - uint8_t RFU:1; - - uint8_t MJD_med; - - uint8_t Hours_high:3; - uint8_t UTC:1; - uint8_t ConfInd:1; - uint8_t LSI:1; - uint8_t MJD_low:2; - - uint8_t Minutes:6; - uint8_t Hours_low:2; - - uint8_t Milliseconds_high:2; - uint8_t Seconds:6; - - uint8_t Milliseconds_low; - - void setMJD(uint32_t date) { - MJD_high = (date >> 10) & 0x7f; - MJD_med = (date >> 2) & 0xff; - MJD_low = date & 0x03; - } - void setHours(uint16_t hours) { - Hours_high = (hours >> 2) & 0x07; - Hours_low = hours & 0x03; - } - - void setMilliseconds(uint16_t ms) { - Milliseconds_high = (ms >> 8) & 0x03; - Milliseconds_low = ms & 0xFF; - } -} PACKED; - - -struct FIGtype0_17 { - uint16_t SId; - - uint8_t rfa2_high:4; - uint8_t rfu1:2; - uint8_t rfa1:1; - uint8_t SD:1; // Static/Dynamic - - uint8_t IntCode:5; - uint8_t rfu2:1; - uint8_t rfa2_low:2; -} PACKED; - -struct FIGtype0_18 { - uint16_t SId; - uint16_t ASu; - uint8_t NumClusters:5; - uint8_t Rfa:3; - /* Followed by uint8_t Cluster IDs */ -} PACKED; - -struct FIGtype0_19 { - uint8_t ClusterId; - uint16_t ASw; - uint8_t SubChId:6; - uint8_t RegionFlag:1; // shall be zero - uint8_t NewFlag:1; - // Region and RFa not supported -} PACKED; - -struct FIGtype0_1 { - uint8_t Length:5; - uint8_t FIGtypeNumber:3; - uint8_t Extension:5; - uint8_t PD:1; - uint8_t OE:1; - uint8_t CN:1; -} PACKED; - - -struct FIG_01_SubChannel_ShortF { - uint8_t StartAdress_high:2; - uint8_t SubChId:6; - uint8_t StartAdress_low:8; - uint8_t TableIndex:6; - uint8_t TableSwitch:1; - uint8_t Short_Long_form:1; -} PACKED; - - -struct FIG_01_SubChannel_LongF { - uint8_t StartAdress_high:2; - uint8_t SubChId:6; - uint8_t StartAdress_low:8; - uint8_t Sub_ChannelSize_high:2; - uint8_t ProtectionLevel:2; - uint8_t Option:3; - uint8_t Short_Long_form:1; - uint8_t Sub_ChannelSize_low:8; -} PACKED; - - -// See EN 300 401, Clause 8.1.20 for the FIG0_13 description -struct FIG0_13_shortAppInfo { - uint16_t SId; - uint8_t No:4; - uint8_t SCIdS:4; -} PACKED; - - -struct FIG0_13_longAppInfo { - uint32_t SId; - uint8_t No:4; - uint8_t SCIdS:4; -} PACKED; - - -struct FIG0_13_app { - uint8_t typeHigh; - uint8_t length:5; - uint8_t typeLow:3; - void setType(uint16_t type) { - typeHigh = type >> 3; - typeLow = type & 0x1f; - } - uint16_t xpad; -} PACKED; - -#define FIG0_13_APPTYPE_SLIDESHOW 0x2 -#define FIG0_13_APPTYPE_WEBSITE 0x3 -#define FIG0_13_APPTYPE_TPEG 0x4 -#define FIG0_13_APPTYPE_DGPS 0x5 -#define FIG0_13_APPTYPE_TMC 0x6 -#define FIG0_13_APPTYPE_EPG 0x7 -#define FIG0_13_APPTYPE_DABJAVA 0x8 -#define FIG0_13_APPTYPE_JOURNALINE 0x441 - - -#ifdef _WIN32 -# pragma pack(pop) -#endif - } // namespace FIC -#endif // __FIG0_H_ - diff --git a/src/fig/FIG0structs.h b/src/fig/FIG0structs.h new file mode 100644 index 0000000..a1e79d3 --- /dev/null +++ b/src/fig/FIG0structs.h @@ -0,0 +1,347 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include +#include + +#include "fig/FIG.h" + +#ifdef _WIN32 +# pragma pack(push) +#endif + +struct FIGtype0 { + uint8_t Length:5; + uint8_t FIGtypeNumber:3; + uint8_t Extension:5; + uint8_t PD:1; + uint8_t OE:1; + uint8_t CN:1; +} PACKED; + + +struct FIGtype0_0 { + uint8_t Length:5; + uint8_t FIGtypeNumber:3; + uint8_t Extension:5; + uint8_t PD:1; + uint8_t OE:1; + uint8_t CN:1; + + uint16_t EId; + uint8_t CIFcnt_hight:5; + uint8_t Al:1; + uint8_t Change:2; + uint8_t CIFcnt_low:8; +} PACKED; + + +struct FIGtype0_2 { + uint8_t Length:5; + uint8_t FIGtypeNumber:3; + uint8_t Extension:5; + uint8_t PD:1; + uint8_t OE:1; + uint8_t CN:1; +} PACKED; + + +struct FIGtype0_2_Service { + uint16_t SId; + uint8_t NbServiceComp:4; + uint8_t CAId:3; + uint8_t Local_flag:1; +} PACKED; + + +struct FIGtype0_2_Service_data { + uint32_t SId; + uint8_t NbServiceComp:4; + uint8_t CAId:3; + uint8_t Local_flag:1; +} PACKED; + + +struct FIGtype0_2_audio_component { + uint8_t ASCTy:6; + uint8_t TMid:2; + uint8_t CA_flag:1; + uint8_t PS:1; + uint8_t SubChId:6; +} PACKED; + + +struct FIGtype0_2_data_component { + uint8_t DSCTy:6; + uint8_t TMid:2; + uint8_t CA_flag:1; + uint8_t PS:1; + uint8_t SubChId:6; +} PACKED; + + +struct FIGtype0_2_packet_component { + uint8_t SCId_high:6; + uint8_t TMid:2; + uint8_t CA_flag:1; + uint8_t PS:1; + uint8_t SCId_low:6; + void setSCId(uint16_t SCId) { + SCId_high = SCId >> 6; + SCId_low = SCId & 0x3f; + } +} PACKED; + + +/* Warning: When bit SCCA_flag is unset(0), the multiplexer R&S does not send + * the SCCA field. But, in the Factum ETI analyzer, if this field is not there, + * it is an error. + */ +struct FIGtype0_3 { + uint8_t SCId_high; + uint8_t SCCA_flag:1; + uint8_t rfa:3; + uint8_t SCId_low:4; + uint8_t DSCTy:6; + uint8_t rfu:1; + uint8_t DG_flag:1; + uint8_t Packet_address_high:2; + uint8_t SubChId:6; + uint8_t Packet_address_low; + uint16_t SCCA; + void setSCId(uint16_t SCId) { + SCId_high = SCId >> 4; + SCId_low = SCId & 0xf; + } + void setPacketAddress(uint16_t address) { + Packet_address_high = address >> 8; + Packet_address_low = address & 0xff; + } +} PACKED; + +struct FIGtype0_5_short { + uint8_t SubChId:6; + uint8_t rfu:1; + uint8_t LS:1; + + uint8_t language; +} PACKED; + +struct FIGtype0_8_short { + uint8_t SCIdS:4; + uint8_t rfa_1:3; + uint8_t ext:1; + uint8_t Id:6; + uint8_t MscFic:1; + uint8_t LS:1; + uint8_t rfa_2; +} PACKED; + + +struct FIGtype0_8_long { + uint8_t SCIdS:4; + uint8_t rfa_1:3; + uint8_t ext:1; + uint8_t SCId_high:4; + uint8_t rfa:3; + uint8_t LS:1; + uint8_t SCId_low; + uint8_t rfa_2; + void setSCId(uint16_t id) { + SCId_high = id >> 8; + SCId_low = id & 0xff; + } + uint16_t getSCid() { + return (SCId_high << 8) | SCId_low; + } +} PACKED; + + +struct FIGtype0_9 { + uint8_t Length:5; + uint8_t FIGtypeNumber:3; + uint8_t Extension:5; + uint8_t PD:1; + uint8_t OE:1; + uint8_t CN:1; + + uint8_t ensembleLto:6; + uint8_t rfa1:1; + uint8_t ext:1; + uint8_t ensembleEcc; + uint8_t tableId; +} PACKED; + + +struct FIGtype0_10_LongForm { + uint8_t Length:5; + uint8_t FIGtypeNumber:3; + + uint8_t Extension:5; + uint8_t PD:1; + uint8_t OE:1; + uint8_t CN:1; + + uint8_t MJD_high:7; + uint8_t RFU:1; + + uint8_t MJD_med; + + uint8_t Hours_high:3; + uint8_t UTC:1; + uint8_t ConfInd:1; + uint8_t LSI:1; + uint8_t MJD_low:2; + + uint8_t Minutes:6; + uint8_t Hours_low:2; + + uint8_t Milliseconds_high:2; + uint8_t Seconds:6; + + uint8_t Milliseconds_low; + + void setMJD(uint32_t date) { + MJD_high = (date >> 10) & 0x7f; + MJD_med = (date >> 2) & 0xff; + MJD_low = date & 0x03; + } + void setHours(uint16_t hours) { + Hours_high = (hours >> 2) & 0x07; + Hours_low = hours & 0x03; + } + + void setMilliseconds(uint16_t ms) { + Milliseconds_high = (ms >> 8) & 0x03; + Milliseconds_low = ms & 0xFF; + } +} PACKED; + + +struct FIGtype0_17 { + uint16_t SId; + + uint8_t rfa2_high:4; + uint8_t rfu1:2; + uint8_t rfa1:1; + uint8_t SD:1; // Static/Dynamic + + uint8_t IntCode:5; + uint8_t rfu2:1; + uint8_t rfa2_low:2; +} PACKED; + +struct FIGtype0_18 { + uint16_t SId; + uint16_t ASu; + uint8_t NumClusters:5; + uint8_t Rfa:3; + /* Followed by uint8_t Cluster IDs */ +} PACKED; + +struct FIGtype0_19 { + uint8_t ClusterId; + uint16_t ASw; + uint8_t SubChId:6; + uint8_t RegionFlag:1; // shall be zero + uint8_t NewFlag:1; + // Region and RFa not supported +} PACKED; + +struct FIGtype0_1 { + uint8_t Length:5; + uint8_t FIGtypeNumber:3; + uint8_t Extension:5; + uint8_t PD:1; + uint8_t OE:1; + uint8_t CN:1; +} PACKED; + + +struct FIG_01_SubChannel_ShortF { + uint8_t StartAdress_high:2; + uint8_t SubChId:6; + uint8_t StartAdress_low:8; + uint8_t TableIndex:6; + uint8_t TableSwitch:1; + uint8_t Short_Long_form:1; +} PACKED; + + +struct FIG_01_SubChannel_LongF { + uint8_t StartAdress_high:2; + uint8_t SubChId:6; + uint8_t StartAdress_low:8; + uint8_t Sub_ChannelSize_high:2; + uint8_t ProtectionLevel:2; + uint8_t Option:3; + uint8_t Short_Long_form:1; + uint8_t Sub_ChannelSize_low:8; +} PACKED; + + +// See EN 300 401, Clause 8.1.20 for the FIG0_13 description +struct FIG0_13_shortAppInfo { + uint16_t SId; + uint8_t No:4; + uint8_t SCIdS:4; +} PACKED; + + +struct FIG0_13_longAppInfo { + uint32_t SId; + uint8_t No:4; + uint8_t SCIdS:4; +} PACKED; + + +struct FIG0_13_app { + uint8_t typeHigh; + uint8_t length:5; + uint8_t typeLow:3; + void setType(uint16_t type) { + typeHigh = type >> 3; + typeLow = type & 0x1f; + } + uint16_t xpad; +} PACKED; + +#define FIG0_13_APPTYPE_SLIDESHOW 0x2 +#define FIG0_13_APPTYPE_WEBSITE 0x3 +#define FIG0_13_APPTYPE_TPEG 0x4 +#define FIG0_13_APPTYPE_DGPS 0x5 +#define FIG0_13_APPTYPE_TMC 0x6 +#define FIG0_13_APPTYPE_EPG 0x7 +#define FIG0_13_APPTYPE_DABJAVA 0x8 +#define FIG0_13_APPTYPE_JOURNALINE 0x441 + + +#ifdef _WIN32 +# pragma pack(pop) +#endif + diff --git a/src/utils.cpp b/src/utils.cpp index 98ec22d..02fbc3f 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -28,6 +28,7 @@ #include #include "DabMux.h" #include "utils.h" +#include "fig/FIG0structs.h" using namespace std; -- cgit v1.2.3 From 369874e1c38680d27f187aa0297b0ce00b3f9fa0 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 2 Oct 2016 14:31:31 +0200 Subject: Split FIG0 into several files --- src/Makefile.am | 15 +- src/fig/FIG0.cpp | 1419 ------------------------------------------- src/fig/FIG0.h | 279 +-------- src/fig/FIG0_0.cpp | 64 ++ src/fig/FIG0_0.h | 51 ++ src/fig/FIG0_1.cpp | 209 +++++++ src/fig/FIG0_1.h | 58 ++ src/fig/FIG0_10.cpp | 84 +++ src/fig/FIG0_10.h | 52 ++ src/fig/FIG0_13.cpp | 182 ++++++ src/fig/FIG0_13.h | 54 ++ src/fig/FIG0_17.cpp | 103 ++++ src/fig/FIG0_17.h | 53 ++ src/fig/FIG0_18.cpp | 106 ++++ src/fig/FIG0_18.h | 52 ++ src/fig/FIG0_19.cpp | 136 +++++ src/fig/FIG0_19.h | 53 ++ src/fig/FIG0_2.cpp | 266 ++++++++ src/fig/FIG0_2.h | 55 ++ src/fig/FIG0_3.cpp | 124 ++++ src/fig/FIG0_3.h | 54 ++ src/fig/FIG0_5.cpp | 113 ++++ src/fig/FIG0_5.h | 57 ++ src/fig/FIG0_8.cpp | 204 +++++++ src/fig/FIG0_8.h | 56 ++ src/fig/FIG0_9.cpp | 83 +++ src/fig/FIG0_9.h | 52 ++ src/fig/TransitionHandler.h | 119 ++++ 28 files changed, 2467 insertions(+), 1686 deletions(-) delete mode 100644 src/fig/FIG0.cpp create mode 100644 src/fig/FIG0_0.cpp create mode 100644 src/fig/FIG0_0.h create mode 100644 src/fig/FIG0_1.cpp create mode 100644 src/fig/FIG0_1.h create mode 100644 src/fig/FIG0_10.cpp create mode 100644 src/fig/FIG0_10.h create mode 100644 src/fig/FIG0_13.cpp create mode 100644 src/fig/FIG0_13.h create mode 100644 src/fig/FIG0_17.cpp create mode 100644 src/fig/FIG0_17.h create mode 100644 src/fig/FIG0_18.cpp create mode 100644 src/fig/FIG0_18.h create mode 100644 src/fig/FIG0_19.cpp create mode 100644 src/fig/FIG0_19.h create mode 100644 src/fig/FIG0_2.cpp create mode 100644 src/fig/FIG0_2.h create mode 100644 src/fig/FIG0_3.cpp create mode 100644 src/fig/FIG0_3.h create mode 100644 src/fig/FIG0_5.cpp create mode 100644 src/fig/FIG0_5.h create mode 100644 src/fig/FIG0_8.cpp create mode 100644 src/fig/FIG0_8.h create mode 100644 src/fig/FIG0_9.cpp create mode 100644 src/fig/FIG0_9.h create mode 100644 src/fig/TransitionHandler.h diff --git a/src/Makefile.am b/src/Makefile.am index 55510ee..3476bbd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -97,9 +97,22 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \ bridge.h bridge.c \ crc.h crc.c \ fig/FIG.h fig/FIG.cpp \ - fig/FIG0.cpp fig/FIG0.h fig/FIG0structs.h \ + fig/FIG0.h fig/FIG0structs.h \ + fig/FIG0_0.cpp fig/FIG0_0.h \ + fig/FIG0_1.cpp fig/FIG0_1.h \ + fig/FIG0_2.cpp fig/FIG0_2.h \ + fig/FIG0_3.cpp fig/FIG0_3.h \ + fig/FIG0_5.cpp fig/FIG0_5.h \ + fig/FIG0_8.cpp fig/FIG0_8.h \ + fig/FIG0_9.cpp fig/FIG0_9.h \ + fig/FIG0_10.cpp fig/FIG0_10.h \ + fig/FIG0_13.cpp fig/FIG0_13.h \ + fig/FIG0_17.cpp fig/FIG0_17.h \ + fig/FIG0_18.cpp fig/FIG0_18.h \ + fig/FIG0_19.cpp fig/FIG0_19.h \ fig/FIG1.cpp fig/FIG1.h \ fig/FIGCarousel.cpp fig/FIGCarousel.h \ + fig/TransitionHandler.h \ mpeg.h mpeg.c \ prbs.h prbs.c \ utils.cpp utils.h \ diff --git a/src/fig/FIG0.cpp b/src/fig/FIG0.cpp deleted file mode 100644 index 08867c8..0000000 --- a/src/fig/FIG0.cpp +++ /dev/null @@ -1,1419 +0,0 @@ -/* - Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011, 2012 Her Majesty the Queen in Right of Canada (Communications - Research Center Canada) - - Copyright (C) 2016 - Matthias P. Braendli, matthias.braendli@mpb.li - - Implementation of FIG0 - */ -/* - This file is part of ODR-DabMux. - - ODR-DabMux 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-DabMux 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-DabMux. If not, see . -*/ - -#include "fig/FIG0.h" -#include "fig/FIG0structs.h" -#include "utils.h" - -namespace FIC { - -//=========== FIG 0/0 =========== - -FillStatus FIG0_0::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - - if (max_size < 6) { - fs.num_bytes_written = 0; - return fs; - } - - FIGtype0_0 *fig0_0; - fig0_0 = (FIGtype0_0 *)buf; - - fig0_0->FIGtypeNumber = 0; - fig0_0->Length = 5; - fig0_0->CN = 0; - fig0_0->OE = 0; - fig0_0->PD = 0; - fig0_0->Extension = 0; - - fig0_0->EId = htons(m_rti->ensemble->id); - fig0_0->Change = 0; - fig0_0->Al = 0; - fig0_0->CIFcnt_hight = (m_rti->currentFrame / 250) % 20; - fig0_0->CIFcnt_low = (m_rti->currentFrame % 250); - - fs.complete_fig_transmitted = true; - fs.num_bytes_written = 6; - return fs; -} - - -//=========== FIG 0/1 =========== - -FIG0_1::FIG0_1(FIGRuntimeInformation *rti) : - m_rti(rti), - m_initialised(false), - m_watermarkSize(0), - m_watermarkPos(0) -{ - uint8_t buffer[sizeof(m_watermarkData) / 2]; - snprintf((char*)buffer, sizeof(buffer), - "%s %s, %s %s", - PACKAGE_NAME, -#if defined(GITVERSION) - GITVERSION, -#else - PACKAGE_VERSION, -#endif - __DATE__, __TIME__); - - memset(m_watermarkData, 0, sizeof(m_watermarkData)); - m_watermarkData[0] = 0x55; // Sync - m_watermarkData[1] = 0x55; - m_watermarkSize = 16; - for (unsigned i = 0; i < strlen((char*)buffer); ++i) { - for (int j = 0; j < 8; ++j) { - uint8_t bit = (buffer[m_watermarkPos >> 3] >> (7 - (m_watermarkPos & 0x07))) & 1; - m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07)); - ++m_watermarkSize; - bit = 1; - m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07)); - ++m_watermarkSize; - ++m_watermarkPos; - } - } - m_watermarkPos = 0; -} - -FillStatus FIG0_1::fill(uint8_t *buf, size_t max_size) -{ -#define FIG0_1_TRACE discard - - FillStatus fs; - size_t remaining = max_size; - - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill initialised=" << - (m_initialised ? 1 : 0); - - const int watermark_bit = (m_watermarkData[m_watermarkPos >> 3] >> - (7 - (m_watermarkPos & 0x07))) & 1; - - const bool iterate_forward = (watermark_bit == 1); - - if (not m_initialised) { - m_initialised = true; - - subchannels = m_rti->ensemble->subchannels; - - if (not iterate_forward) { - std::reverse(subchannels.begin(), subchannels.end()); - } - subchannelFIG0_1 = subchannels.begin(); - } - - if (max_size < 6) { - return fs; - } - - FIGtype0_1 *figtype0_1 = NULL; - - // Rotate through the subchannels until there is no more - // space in the FIG0/1 - for (; subchannelFIG0_1 != subchannels.end(); ++subchannelFIG0_1 ) { - size_t subch_iter_ix = std::distance(subchannels.begin(), subchannelFIG0_1); - - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill loop ix=" << subch_iter_ix; - - dabProtection* protection = &(*subchannelFIG0_1)->protection; - - if (figtype0_1 == NULL) { - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill header " << - (protection->form == UEP ? "UEP " : "EEP ") << remaining; - - if ( (protection->form == UEP && remaining < 2 + 3) || - (protection->form == EEP && remaining < 2 + 4) ) { - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill no space for header"; - break; - } - - figtype0_1 = (FIGtype0_1*)buf; - - figtype0_1->FIGtypeNumber = 0; - figtype0_1->Length = 1; - figtype0_1->CN = 0; - figtype0_1->OE = 0; - figtype0_1->PD = 0; - figtype0_1->Extension = 1; - buf += 2; - remaining -= 2; - } - else if ( (protection->form == UEP && remaining < 3) || - (protection->form == EEP && remaining < 4) ) { - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill no space for fig " << - (protection->form == UEP ? "UEP " : "EEP ") << remaining; - break; - } - - if (protection->form == UEP) { - FIG_01_SubChannel_ShortF *fig0_1subchShort = - (FIG_01_SubChannel_ShortF*)buf; - fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id; - - fig0_1subchShort->StartAdress_high = - (*subchannelFIG0_1)->startAddress / 256; - fig0_1subchShort->StartAdress_low = - (*subchannelFIG0_1)->startAddress % 256; - - fig0_1subchShort->Short_Long_form = 0; - fig0_1subchShort->TableSwitch = 0; - fig0_1subchShort->TableIndex = - protection->uep.tableIndex; - - buf += 3; - remaining -= 3; - figtype0_1->Length += 3; - - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill insert UEP id=" << - (int)fig0_1subchShort->SubChId << " rem=" << remaining - << " ix=" << subch_iter_ix; - } - else if (protection->form == EEP) { - FIG_01_SubChannel_LongF *fig0_1subchLong1 = - (FIG_01_SubChannel_LongF*)buf; - fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id; - - fig0_1subchLong1->StartAdress_high = - (*subchannelFIG0_1)->startAddress / 256; - fig0_1subchLong1->StartAdress_low = - (*subchannelFIG0_1)->startAddress % 256; - - fig0_1subchLong1->Short_Long_form = 1; - fig0_1subchLong1->Option = protection->eep.GetOption(); - fig0_1subchLong1->ProtectionLevel = - protection->level; - - fig0_1subchLong1->Sub_ChannelSize_high = - (*subchannelFIG0_1)->getSizeCu() / 256; - fig0_1subchLong1->Sub_ChannelSize_low = - (*subchannelFIG0_1)->getSizeCu() % 256; - - buf += 4; - remaining -= 4; - figtype0_1->Length += 4; - - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill insert EEP id=" << - (int)fig0_1subchLong1->SubChId << " rem=" << remaining - << " ix=" << subch_iter_ix; - } - } - - size_t subch_iter_ix = std::distance(subchannels.begin(), subchannelFIG0_1); - - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill loop out, rem=" << remaining - << " ix=" << subch_iter_ix; - - if (subchannelFIG0_1 == subchannels.end()) { - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill completed, rem=" << remaining; - m_initialised = false; - fs.complete_fig_transmitted = true; - - m_watermarkPos++; - if (m_watermarkPos == m_watermarkSize) { - m_watermarkPos = 0; - } - } - - fs.num_bytes_written = max_size - remaining; - etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill wrote " << fs.num_bytes_written; - return fs; -} - - -//=========== FIG 0/2 =========== - -FIG0_2::FIG0_2(FIGRuntimeInformation *rti) : - m_rti(rti), - m_initialised(false), - m_inserting_audio_not_data(false) -{ -} - -FillStatus FIG0_2::fill(uint8_t *buf, size_t max_size) -{ -#define FIG0_2_TRACE discard - using namespace std; - - FillStatus fs; - FIGtype0_2 *fig0_2 = NULL; - int cur = 0; - ssize_t remaining = max_size; - - const auto ensemble = m_rti->ensemble; - - etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill init " << (m_initialised ? 1 : 0) << - " ********************************"; - - for (const auto& s : ensemble->services) { - // Exclude Fidc type services, TODO unsupported - auto type = s->getType(ensemble); - if (type == subchannel_type_t::Fidc) { - throw invalid_argument("FIG0/2 does not support FIDC"); - } - } - - if (not m_initialised) { - m_audio_services.clear(); - copy_if(ensemble->services.begin(), - ensemble->services.end(), - back_inserter(m_audio_services), - [&](shared_ptr& s) { - return s->isProgramme(ensemble); - } ); - - m_data_services.clear(); - copy_if(ensemble->services.begin(), - ensemble->services.end(), - back_inserter(m_data_services), - [&](shared_ptr& s) { - return not s->isProgramme(ensemble); - } ); - - m_initialised = true; - m_inserting_audio_not_data = !m_inserting_audio_not_data; - - if (m_inserting_audio_not_data) { - serviceFIG0_2 = m_audio_services.begin(); - } - else { - serviceFIG0_2 = m_data_services.begin(); - } - - etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill we have " << - m_audio_services.size() << " audio and " << - m_data_services.size() << " data services. Inserting " << - (m_inserting_audio_not_data ? "AUDIO" : "DATA"); - } - - const auto last_service = m_inserting_audio_not_data ? - m_audio_services.end() : m_data_services.end(); - - // Rotate through the subchannels until there is no more - // space - for (; serviceFIG0_2 != last_service; ++serviceFIG0_2) { - const auto type = (*serviceFIG0_2)->getType(ensemble); - const auto isProgramme = (*serviceFIG0_2)->isProgramme(ensemble); - - etiLog.log(FIG0_2_TRACE, "FIG0_2::fill loop SId=%04x %s/%s", - (*serviceFIG0_2)->id, - m_inserting_audio_not_data ? "AUDIO" : "DATA", - type == subchannel_type_t::Audio ? "Audio" : - type == subchannel_type_t::Packet ? "Packet" : - type == subchannel_type_t::DataDmb ? "DataDmb" : - type == subchannel_type_t::Fidc ? "Fidc" : "?"); - - // filter out services which have no components - if ((*serviceFIG0_2)->nbComponent(ensemble->components) == 0) { - etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill no components "; - continue; - } - - ++cur; - - const int required_size = isProgramme ? - 3 + 2 * (*serviceFIG0_2)->nbComponent(ensemble->components) : - 5 + 2 * (*serviceFIG0_2)->nbComponent(ensemble->components); - - if (fig0_2 == NULL) { - etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill header"; - if (remaining < 2 + required_size) { - etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill header no place" << - " rem=" << remaining << " req=" << 2 + required_size; - break; - } - fig0_2 = (FIGtype0_2 *)buf; - - fig0_2->FIGtypeNumber = 0; - fig0_2->Length = 1; - fig0_2->CN = 0; - fig0_2->OE = 0; - fig0_2->PD = isProgramme ? 0 : 1; - fig0_2->Extension = 2; - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill no place" << - " rem=" << remaining << " req=" << required_size; - break; - } - - if (type == subchannel_type_t::Audio) { - auto fig0_2serviceAudio = (FIGtype0_2_Service*)buf; - - fig0_2serviceAudio->SId = htons((*serviceFIG0_2)->id); - fig0_2serviceAudio->Local_flag = 0; - fig0_2serviceAudio->CAId = 0; - fig0_2serviceAudio->NbServiceComp = - (*serviceFIG0_2)->nbComponent(ensemble->components); - buf += 3; - fig0_2->Length += 3; - remaining -= 3; - - etiLog.log(FIG0_2_TRACE, "FIG0_2::fill audio SId=%04x", - (*serviceFIG0_2)->id); - } - else { - auto fig0_2serviceData = (FIGtype0_2_Service_data*)buf; - - fig0_2serviceData->SId = htonl((*serviceFIG0_2)->id); - fig0_2serviceData->Local_flag = 0; - fig0_2serviceData->CAId = 0; - fig0_2serviceData->NbServiceComp = - (*serviceFIG0_2)->nbComponent(ensemble->components); - buf += 5; - fig0_2->Length += 5; - remaining -= 5; - - etiLog.log(FIG0_2_TRACE, "FIG0_2::fill data SId=%04x", - (*serviceFIG0_2)->id); - } - - int curCpnt = 0; - for (auto component = getComponent( - ensemble->components, (*serviceFIG0_2)->id ); - component != ensemble->components.end(); - component = getComponent( - ensemble->components, - (*serviceFIG0_2)->id, - component ) - ) { - auto subchannel = getSubchannel( - ensemble->subchannels, (*component)->subchId); - - etiLog.log(FIG0_2_TRACE, "FIG0_2::fill comp sub=%04x srv=%04x", - (*component)->subchId, (*component)->serviceId); - - if (subchannel == ensemble->subchannels.end()) { - etiLog.log(error, - "Subchannel %i does not exist for component " - "of service %i\n", - (*component)->subchId, (*component)->serviceId); - throw MuxInitException(); - } - - switch ((*subchannel)->type) { - case subchannel_type_t::Audio: - { - auto audio_description = (FIGtype0_2_audio_component*)buf; - audio_description->TMid = 0; - audio_description->ASCTy = (*component)->type; - audio_description->SubChId = (*subchannel)->id; - audio_description->PS = ((curCpnt == 0) ? 1 : 0); - audio_description->CA_flag = 0; - } - break; - case subchannel_type_t::DataDmb: - { - auto data_description = (FIGtype0_2_data_component*)buf; - data_description->TMid = 1; - data_description->DSCTy = (*component)->type; - data_description->SubChId = (*subchannel)->id; - data_description->PS = ((curCpnt == 0) ? 1 : 0); - data_description->CA_flag = 0; - } - break; - case subchannel_type_t::Packet: - { - auto packet_description = (FIGtype0_2_packet_component*)buf; - packet_description->TMid = 3; - packet_description->setSCId((*component)->packet.id); - packet_description->PS = ((curCpnt == 0) ? 1 : 0); - packet_description->CA_flag = 0; - } - break; - default: - etiLog.log(error, - "Component type not supported\n"); - throw MuxInitException(); - } - buf += 2; - fig0_2->Length += 2; - remaining -= 2; - if (remaining < 0) { - etiLog.log(error, - "Sorry, no space left in FIG 0/2 to insert " - "component %i of program service %i.\n", - curCpnt, cur); - throw MuxInitException(); - } - ++curCpnt; - - etiLog.log(FIG0_2_TRACE, "FIG0_2::fill comp length=%d", - fig0_2->Length); - } - } - - if (serviceFIG0_2 == last_service) { - etiLog.log(FIG0_2_TRACE, "FIG0_2::loop reached last"); - m_initialised = false; - fs.complete_fig_transmitted = !m_inserting_audio_not_data; - } - - etiLog.log(FIG0_2_TRACE, "FIG0_2::loop end complete=%d", - fs.complete_fig_transmitted ? 1 : 0); - - fs.num_bytes_written = max_size - remaining; - return fs; -} - - -//=========== FIG 0/3 =========== - -FIG0_3::FIG0_3(FIGRuntimeInformation *rti) : - m_rti(rti), - m_initialised(false) -{ -} - -FillStatus FIG0_3::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - ssize_t remaining = max_size; - auto ensemble = m_rti->ensemble; - - if (not m_initialised) { - componentFIG0_3 = m_rti->ensemble->components.end(); - m_initialised = true; - } - - FIGtype0 *fig0 = NULL; - - for (; componentFIG0_3 != ensemble->components.end(); - ++componentFIG0_3) { - auto subchannel = getSubchannel(ensemble->subchannels, - (*componentFIG0_3)->subchId); - - if (subchannel == ensemble->subchannels.end()) { - etiLog.log(error, - "Subchannel %i does not exist for component " - "of service %i\n", - (*componentFIG0_3)->subchId, (*componentFIG0_3)->serviceId); - throw MuxInitException(); - } - - if ((*subchannel)->type != subchannel_type_t::Packet) - continue; - - const int required_size = 5 + (m_rti->factumAnalyzer ? 2 : 0); - - if (fig0 == NULL) { - if (remaining < 2 + required_size) { - break; - } - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 0; - fig0->Extension = 3; - - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - break; - } - - /* Warning: When bit SCCA_flag is unset(0), the multiplexer - * R&S does not send the SCCA field. But, in the Factum ETI - * analyzer, if this field is not there, it is an error. - */ - FIGtype0_3 *fig0_3 = (FIGtype0_3*)buf; - fig0_3->setSCId((*componentFIG0_3)->packet.id); - fig0_3->rfa = 0; - fig0_3->SCCA_flag = 0; - // if 0, datagroups are used - fig0_3->DG_flag = !(*componentFIG0_3)->packet.datagroup; - fig0_3->rfu = 0; - fig0_3->DSCTy = (*componentFIG0_3)->type; - fig0_3->SubChId = (*subchannel)->id; - fig0_3->setPacketAddress((*componentFIG0_3)->packet.address); - if (m_rti->factumAnalyzer) { - fig0_3->SCCA = 0; - } - - fig0->Length += 5; - buf += 5; - remaining -= 5; - if (m_rti->factumAnalyzer) { - fig0->Length += 2; - buf += 2; - remaining -= 2; - } - } - - if (componentFIG0_3 == ensemble->components.end()) { - componentFIG0_3 = ensemble->components.begin(); - fs.complete_fig_transmitted = true; - } - - fs.num_bytes_written = max_size - remaining; - return fs; -} - -//=========== FIG 0/5 =========== - -FIG0_5::FIG0_5(FIGRuntimeInformation *rti) : - m_rti(rti), - m_initialised(false) -{ -} - -FillStatus FIG0_5::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - ssize_t remaining = max_size; - auto ensemble = m_rti->ensemble; - - if (not m_initialised) { - componentFIG0_5 = m_rti->ensemble->components.end(); - m_initialised = true; - } - - FIGtype0* fig0 = NULL; - - for (; componentFIG0_5 != ensemble->components.end(); - ++componentFIG0_5) { - - auto service = getService(*componentFIG0_5, - ensemble->services); - auto subchannel = getSubchannel(ensemble->subchannels, - (*componentFIG0_5)->subchId); - - if (subchannel == ensemble->subchannels.end()) { - etiLog.log(error, - "Subchannel %i does not exist for component " - "of service %i\n", - (*componentFIG0_5)->subchId, - (*componentFIG0_5)->serviceId); - throw MuxInitException(); - } - - if ( (*service)->language == 0) { - continue; - } - - const int required_size = 2; - - if (fig0 == NULL) { - if (remaining < 2 + required_size) { - break; - } - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 0; - fig0->Extension = 5; - - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - break; - } - - FIGtype0_5_short *fig0_5 = (FIGtype0_5_short*)buf; - - fig0_5->LS = 0; - fig0_5->rfu = 0; - fig0_5->SubChId = (*subchannel)->id; - fig0_5->language = (*service)->language; - - fig0->Length += 2; - buf += 2; - remaining -= 2; - } - - if (componentFIG0_5 == ensemble->components.end()) { - componentFIG0_5 = ensemble->components.begin(); - fs.complete_fig_transmitted = true; - } - - fs.num_bytes_written = max_size - remaining; - return fs; -} - -//=========== FIG 0/8 =========== - -FIG0_8::FIG0_8(FIGRuntimeInformation *rti) : - m_rti(rti), - m_initialised(false), - m_transmit_programme(false) -{ -} - -FillStatus FIG0_8::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - auto ensemble = m_rti->ensemble; - ssize_t remaining = max_size; - - if (not m_initialised) { - componentFIG0_8 = m_rti->ensemble->components.end(); - m_initialised = true; - } - - FIGtype0* fig0 = NULL; - - for (; componentFIG0_8 != ensemble->components.end(); - ++componentFIG0_8) { - - auto service = getService(*componentFIG0_8, - ensemble->services); - auto subchannel = getSubchannel(ensemble->subchannels, - (*componentFIG0_8)->subchId); - - if (subchannel == ensemble->subchannels.end()) { - etiLog.log(error, - "Subchannel %i does not exist for component " - "of service %i\n", - (*componentFIG0_8)->subchId, - (*componentFIG0_8)->serviceId); - throw MuxInitException(); - } - - if (m_transmit_programme and (*service)->isProgramme(ensemble)) { - const int required_size = - ((*subchannel)->type == subchannel_type_t::Packet ? 5 : 4); - - if (fig0 == NULL) { - if (remaining < 2 + required_size) { - break; - } - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 0; - fig0->Extension = 8; - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - break; - } - - if ((*subchannel)->type == subchannel_type_t::Packet) { // Data packet - buf[0] = ((*componentFIG0_8)->serviceId >> 8) & 0xFF; - buf[1] = ((*componentFIG0_8)->serviceId) & 0xFF; - fig0->Length += 2; - buf += 2; - remaining -= 2; - - FIGtype0_8_long* definition = (FIGtype0_8_long*)buf; - memset(definition, 0, 3); - definition->ext = 0; // no rfa - definition->SCIdS = (*componentFIG0_8)->SCIdS; - definition->LS = 1; - definition->setSCId((*componentFIG0_8)->packet.id); - fig0->Length += 3; - buf += 3; // 8 minus rfa - remaining -= 3; - } - else { // Audio, data stream or FIDC - buf[0] = ((*componentFIG0_8)->serviceId >> 8) & 0xFF; - buf[1] = ((*componentFIG0_8)->serviceId) & 0xFF; - - fig0->Length += 2; - buf += 2; - remaining -= 2; - - FIGtype0_8_short* definition = (FIGtype0_8_short*)buf; - memset(definition, 0, 2); - definition->ext = 0; // no rfa - definition->SCIdS = (*componentFIG0_8)->SCIdS; - definition->LS = 0; - definition->MscFic = 0; - definition->Id = (*componentFIG0_8)->subchId; - fig0->Length += 2; - buf += 2; // 4 minus rfa - remaining -= 2; - } - } - else if (!m_transmit_programme and !(*service)->isProgramme(ensemble)) { - // Data - const int required_size = - ((*subchannel)->type == subchannel_type_t::Packet ? 7 : 6); - - if (fig0 == NULL) { - if (remaining < 2 + required_size) { - break; - } - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 1; - fig0->Extension = 8; - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - break; - } - - if ((*subchannel)->type == subchannel_type_t::Packet) { // Data packet - buf[0] = ((*componentFIG0_8)->serviceId >> 8) & 0xFF; - buf[1] = ((*componentFIG0_8)->serviceId) & 0xFF; - fig0->Length += 4; - buf += 4; - remaining -= 4; - - FIGtype0_8_long* definition = (FIGtype0_8_long*)buf; - memset(definition, 0, 3); - definition->ext = 0; // no rfa - definition->SCIdS = (*componentFIG0_8)->SCIdS; - definition->LS = 1; - definition->setSCId((*componentFIG0_8)->packet.id); - fig0->Length += 3; - buf += 3; // 8 minus rfa - remaining -= 3; - } - else { // Audio, data stream or FIDC - buf[0] = ((*componentFIG0_8)->serviceId >> 8) & 0xFF; - buf[1] = ((*componentFIG0_8)->serviceId) & 0xFF; - fig0->Length += 4; - buf += 4; - remaining -= 4; - - FIGtype0_8_short* definition = (FIGtype0_8_short*)buf; - memset(definition, 0, 2); - definition->ext = 0; // no rfa - definition->SCIdS = (*componentFIG0_8)->SCIdS; - definition->LS = 0; - definition->MscFic = 0; - definition->Id = (*componentFIG0_8)->subchId; - fig0->Length += 2; - buf += 2; // 4 minus rfa - remaining -= 2; - } - } - } - - if (componentFIG0_8 == ensemble->components.end()) { - componentFIG0_8 = ensemble->components.begin(); - - // The full database is sent every second full loop - fs.complete_fig_transmitted = m_transmit_programme; - - m_transmit_programme = not m_transmit_programme; - // Alternate between data and and programme FIG0/13, - // do not mix fig0 with PD=0 with extension 13 stuff - // that actually needs PD=1, and vice versa - } - - fs.num_bytes_written = max_size - remaining; - return fs; -} - -//=========== FIG 0/9 =========== -FIG0_9::FIG0_9(FIGRuntimeInformation *rti) : - m_rti(rti) {} - -FillStatus FIG0_9::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - auto ensemble = m_rti->ensemble; - size_t remaining = max_size; - - if (remaining < 5) { - fs.num_bytes_written = 0; - return fs; - } - - auto fig0_9 = (FIGtype0_9*)buf; - fig0_9->FIGtypeNumber = 0; - fig0_9->Length = 4; - fig0_9->CN = 0; - fig0_9->OE = 0; - fig0_9->PD = 0; - fig0_9->Extension = 9; - - fig0_9->ext = 0; - fig0_9->rfa1 = 0; // Had a different meaning in EN 300 401 V1.4.1 - - if (ensemble->lto_auto) { - time_t now = time(NULL); - struct tm* ltime = localtime(&now); - time_t now2 = timegm(ltime); - ensemble->lto = (now2 - now) / 1800; - } - - if (ensemble->lto >= 0) { - fig0_9->ensembleLto = ensemble->lto; - } - else { - /* Convert to 1-complement representation */ - fig0_9->ensembleLto = (-ensemble->lto) | (1<<5); - } - - fig0_9->ensembleEcc = ensemble->ecc; - fig0_9->tableId = ensemble->international_table; - buf += 5; - remaining -= 5; - - /* No extended field, no support for services with different ECC */ - - fs.num_bytes_written = max_size - remaining; - fs.complete_fig_transmitted = true; - return fs; -} - -//=========== FIG 0/10 =========== - -FIG0_10::FIG0_10(FIGRuntimeInformation *rti) : - m_rti(rti) -{ -} - -FillStatus FIG0_10::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - auto ensemble = m_rti->ensemble; - size_t remaining = max_size; - - if (remaining < 8) { - fs.num_bytes_written = 0; - return fs; - } - - //Time and country identifier - auto fig0_10 = (FIGtype0_10_LongForm*)buf; - - fig0_10->FIGtypeNumber = 0; - fig0_10->Length = 7; - fig0_10->CN = 0; - fig0_10->OE = 0; - fig0_10->PD = 0; - fig0_10->Extension = 10; - buf += 2; - remaining -= 2; - - tm* timeData; - time_t dab_time_seconds = 0; - uint32_t dab_time_millis = 0; - get_dab_time(&dab_time_seconds, &dab_time_millis); - timeData = gmtime(&dab_time_seconds); - - fig0_10->RFU = 0; - fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900, - timeData->tm_mon + 1, - timeData->tm_mday)); - fig0_10->LSI = 0; - fig0_10->ConfInd = 1; - fig0_10->UTC = 1; - fig0_10->setHours(timeData->tm_hour); - fig0_10->Minutes = timeData->tm_min; - fig0_10->Seconds = timeData->tm_sec; - fig0_10->setMilliseconds(dab_time_millis); - buf += 6; - remaining -= 6; - - fs.num_bytes_written = max_size - remaining; - fs.complete_fig_transmitted = true; - return fs; -} - -//=========== FIG 0/13 =========== - -FIG0_13::FIG0_13(FIGRuntimeInformation *rti) : - m_rti(rti), - m_initialised(false), - m_transmit_programme(false) -{ -} - -FillStatus FIG0_13::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - auto ensemble = m_rti->ensemble; - ssize_t remaining = max_size; - - if (not m_initialised) { - componentFIG0_13 = m_rti->ensemble->components.end(); - m_initialised = true; - } - - FIGtype0* fig0 = NULL; - - for (; componentFIG0_13 != ensemble->components.end(); - ++componentFIG0_13) { - - auto subchannel = getSubchannel(ensemble->subchannels, - (*componentFIG0_13)->subchId); - - if (subchannel == ensemble->subchannels.end()) { - etiLog.log(error, - "Subchannel %i does not exist for component " - "of service %i\n", - (*componentFIG0_13)->subchId, - (*componentFIG0_13)->serviceId); - throw MuxInitException(); - } - - if ( m_transmit_programme && - (*subchannel)->type == subchannel_type_t::Audio && - (*componentFIG0_13)->audio.uaType != 0xffff) { - - const int required_size = 3+4+11; - - if (fig0 == NULL) { - if (remaining < 2 + required_size) { - break; - } - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 0; - fig0->Extension = 13; - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - break; - } - - FIG0_13_shortAppInfo* info = (FIG0_13_shortAppInfo*)buf; - info->SId = htonl((*componentFIG0_13)->serviceId) >> 16; - info->SCIdS = (*componentFIG0_13)->SCIdS; - info->No = 1; - buf += 3; - remaining -= 3; - fig0->Length += 3; - - FIG0_13_app* app = (FIG0_13_app*)buf; - app->setType((*componentFIG0_13)->audio.uaType); - app->length = 2; - app->xpad = htons(0x0c3c); - /* xpad meaning - CA = 0 - CAOrg = 0 - Rfu = 0 - AppTy(5) = 12 (MOT, start of X-PAD data group) - DG = 0 (MSC data groups used) - Rfu = 0 - DSCTy(6) = 60 (MOT) - */ - - buf += 2 + app->length; - remaining -= 2 + app->length; - fig0->Length += 2 + app->length; - } - else if (!m_transmit_programme && - (*subchannel)->type == subchannel_type_t::Packet && - (*componentFIG0_13)->packet.appType != 0xffff) { - - const int required_size = 5+2+2; - - if (fig0 == NULL) { - if (remaining < 2 + required_size) { - break; - } - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 1; - fig0->Extension = 13; - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - break; - } - - FIG0_13_longAppInfo* info = (FIG0_13_longAppInfo*)buf; - info->SId = htonl((*componentFIG0_13)->serviceId); - info->SCIdS = (*componentFIG0_13)->SCIdS; - info->No = 1; - buf += 5; - remaining -= 5; - fig0->Length += 5; - - FIG0_13_app* app = (FIG0_13_app*)buf; - app->setType((*componentFIG0_13)->packet.appType); - if (app->typeLow == FIG0_13_APPTYPE_EPG) { - app->length = 2; - app->xpad = htons(0x0100); - /* xpad used to hold two bytes of EPG profile information - 01 = basic profile - 00 = list terminator */ - } - else { - app->length = 0; - } - buf += 2 + app->length; - remaining -= 2 + app->length; - fig0->Length += 2 + app->length; - } - } - - if (componentFIG0_13 == ensemble->components.end()) { - componentFIG0_13 = ensemble->components.begin(); - - // The full database is sent every second full loop - fs.complete_fig_transmitted = m_transmit_programme; - - m_transmit_programme = not m_transmit_programme; - // Alternate between data and and programme FIG0/13, - // do not mix fig0 with PD=0 with extension 13 stuff - // that actually needs PD=1, and vice versa - } - - fs.num_bytes_written = max_size - remaining; - return fs; -} - -//=========== FIG 0/17 PTy =========== - -FIG0_17::FIG0_17(FIGRuntimeInformation *rti) : - m_rti(rti), - m_initialised(false) -{ -} - -FillStatus FIG0_17::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - ssize_t remaining = max_size; - - if (not m_initialised) { - serviceFIG0_17 = m_rti->ensemble->services.end(); - m_initialised = true; - } - - auto ensemble = m_rti->ensemble; - - FIGtype0* fig0 = NULL; - - for (; serviceFIG0_17 != ensemble->services.end(); - ++serviceFIG0_17) { - - if ((*serviceFIG0_17)->pty == 0) { - continue; - } - - const int required_size = 4; - - - if (fig0 == NULL) { - if (remaining < 2 + required_size) { - break; - } - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 0; - fig0->Extension = 17; - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - break; - } - - auto fig0_17 = (FIGtype0_17*)buf; - fig0_17->SId = htons((*serviceFIG0_17)->id); - fig0_17->SD = 1; // We only transmit dynamic PTy - fig0_17->rfa1 = 0; - fig0_17->rfu1 = 0; - fig0_17->rfa2_low = 0; - fig0_17->rfa2_high = 0; - fig0_17->rfu2 = 0; - fig0_17->IntCode = (*serviceFIG0_17)->pty; - - fig0->Length += 4; - buf += 4; - remaining -= 4; - } - - if (serviceFIG0_17 == ensemble->services.end()) { - serviceFIG0_17 = ensemble->services.begin(); - fs.complete_fig_transmitted = true; - } - - fs.num_bytes_written = max_size - remaining; - return fs; -} - -//=========== FIG 0/18 =========== - -FIG0_18::FIG0_18(FIGRuntimeInformation *rti) : - m_rti(rti), - m_initialised(false) -{ -} - -FillStatus FIG0_18::fill(uint8_t *buf, size_t max_size) -{ - FillStatus fs; - ssize_t remaining = max_size; - - if (not m_initialised) { - service = m_rti->ensemble->services.end(); - m_initialised = true; - } - - auto ensemble = m_rti->ensemble; - - FIGtype0* fig0 = NULL; - - for (; service != ensemble->services.end(); - ++service) { - - if ( (*service)->ASu == 0 ) { - continue; - } - - const ssize_t numclusters = (*service)->clusters.size(); - - const int required_size = 5 + numclusters; - - - if (fig0 == NULL) { - if (remaining < 2 + required_size) { - break; - } - - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 0; - fig0->Extension = 18; - buf += 2; - remaining -= 2; - } - else if (remaining < required_size) { - break; - } - - auto programme = (FIGtype0_18*)buf; - programme->SId = htons((*service)->id); - programme->ASu = htons((*service)->ASu); - programme->Rfa = 0; - programme->NumClusters = numclusters; - buf += 5; - - for (uint8_t cluster : (*service)->clusters) { - *(buf++) = cluster; - } - - fig0->Length += 5 + numclusters; - remaining -= 5 + numclusters; - } - - if (service == ensemble->services.end()) { - service = ensemble->services.begin(); - fs.complete_fig_transmitted = true; - } - - fs.num_bytes_written = max_size - remaining; - return fs; -} - -//=========== FIG 0/19 =========== - -FIG0_19::FIG0_19(FIGRuntimeInformation *rti) : - m_rti(rti) -{ } - -FillStatus FIG0_19::fill(uint8_t *buf, size_t max_size) -{ - using namespace std; - - auto ensemble = m_rti->ensemble; - - // We are called every 24ms, and must timeout after 2s - const int timeout = 2000/24; - - m_transition.update_state(timeout, ensemble->clusters); - - FillStatus fs; - ssize_t remaining = max_size; - - FIGtype0* fig0 = NULL; - - // Combine all clusters into one list - set allclusters; - for (const auto& cluster : m_transition.new_entries) { - allclusters.insert(cluster.first.get()); - } - for (const auto& cluster : m_transition.repeated_entries) { - allclusters.insert(cluster.get()); - } - for (const auto& cluster : m_transition.disabled_entries) { - allclusters.insert(cluster.first.get()); - } - - const int length_0_19 = 4; - fs.complete_fig_transmitted = true; - for (auto& cluster : allclusters) { - - if (fig0 == NULL) { - if (remaining < 2 + length_0_19) { - fs.complete_fig_transmitted = false; - break; - } - - fig0 = (FIGtype0*)buf; - fig0->FIGtypeNumber = 0; - fig0->Length = 1; - fig0->CN = 0; - fig0->OE = 0; - fig0->PD = 0; - fig0->Extension = 19; - buf += 2; - remaining -= 2; - } - else if (remaining < length_0_19) { - fs.complete_fig_transmitted = false; - break; - } - - - auto fig0_19 = (FIGtype0_19*)buf; - fig0_19->ClusterId = cluster->cluster_id; - if (cluster->is_active()) { - fig0_19->ASw = htons(cluster->flags); - } - else { - fig0_19->ASw = 0; - } - fig0_19->NewFlag = 1; - fig0_19->RegionFlag = 0; - fig0_19->SubChId = 0; - bool found = false; - - for (const auto& subchannel : ensemble->subchannels) { - if (subchannel->uid == cluster->subchanneluid) { - fig0_19->SubChId = subchannel->id; - found = true; - break; - } - } - if (not found) { - etiLog.level(warn) << "FIG0/19: could not find subchannel " << - cluster->subchanneluid << " for cluster " << - (int)cluster->cluster_id; - continue; - } - - fig0->Length += length_0_19; - buf += length_0_19; - remaining -= length_0_19; - } - - fs.num_bytes_written = max_size - remaining; - return fs; -} - -template -void TransitionHandler::update_state(int timeout, std::vector > all_entries) -{ - for (const auto& entry : all_entries) { - if (entry->is_active()) { - if (repeated_entries.count(entry) > 0) { - // We are currently announcing this entry - continue; - } - - if (new_entries.count(entry) > 0) { - // We are currently announcing this entry at a - // fast rate. Handle timeout: - new_entries[entry] -= 1; - if (new_entries[entry] <= 0) { - repeated_entries.insert(entry); - new_entries.erase(entry); - } - continue; - } - - // unlikely - if (disabled_entries.count(entry) > 0) { - new_entries[entry] = timeout; - disabled_entries.erase(entry); - continue; - } - - // It's a new entry! - new_entries[entry] = timeout; - } - else { // Not active - if (disabled_entries.count(entry) > 0) { - disabled_entries[entry] -= 1; - if (disabled_entries[entry] <= 0) { - disabled_entries.erase(entry); - } - continue; - } - - if (repeated_entries.count(entry) > 0) { - // We are currently announcing this entry - disabled_entries[entry] = timeout; - repeated_entries.erase(entry); - continue; - } - - // unlikely - if (new_entries.count(entry) > 0) { - // We are currently announcing this entry at a - // fast rate. We must stop announcing it - disabled_entries[entry] = timeout; - new_entries.erase(entry); - continue; - } - } - } -} - -FIG_rate FIG0_19::repetition_rate(void) -{ - if ( m_transition.new_entries.size() > 0 or - m_transition.disabled_entries.size() ) { - return FIG_rate::A_B; - } - else { - return FIG_rate::B; - } -} - -} // namespace FIC - diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h index fe90d31..927efc1 100644 --- a/src/fig/FIG0.h +++ b/src/fig/FIG0.h @@ -25,270 +25,17 @@ #pragma once -#include -#include -#include - -#include "fig/FIG.h" - -namespace FIC { - -// Some FIGs need to adapt their rate or their contents depending -// on if some data entries are stable or currently undergoing a -// change. The TransitionHandler keeps track of which entries -// are in what state. -template -class TransitionHandler { - public: - // update_state must be called once per ETI frame, and will - // move entries from new to repeated to disabled depending - // on their is_active() return value. - void update_state(int timeout, std::vector > all_entries); - - // The FIG that needs the information about what state an entry is in - // can read from the following data structures. It shall not modify them. - - /* Map to frame count */ - std::map< - std::shared_ptr,int> new_entries; - - std::set< - std::shared_ptr > repeated_entries; - - /* Map to frame count */ - std::map< - std::shared_ptr,int> disabled_entries; -}; - - -// FIG type 0/0, Multiplex Configuration Info (MCI), -// Ensemble information -class FIG0_0 : public IFIG -{ - public: - FIG0_0(FIGRuntimeInformation* rti) : - m_rti(rti) {} - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::FIG0_0; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 0; } - - private: - FIGRuntimeInformation *m_rti; -}; - -// FIG type 0/1, MIC, Sub-Channel Organization, -// one instance of the part for each subchannel -class FIG0_1 : public IFIG -{ - public: - FIG0_1(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::A; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 1; } - - private: - FIGRuntimeInformation *m_rti; - bool m_initialised; - std::vector subchannels; - std::vector::iterator subchannelFIG0_1; - - uint8_t m_watermarkData[128]; - size_t m_watermarkSize; - size_t m_watermarkPos; -}; - -// FIG type 0/2, MCI, Service Organization, one instance of -// FIGtype0_2_Service for each subchannel -class FIG0_2 : public IFIG -{ - public: - FIG0_2(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::A; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 2; } - - private: - FIGRuntimeInformation *m_rti; - bool m_initialised; - bool m_inserting_audio_not_data; - std::vector > m_audio_services; - std::vector > m_data_services; - std::vector >::iterator serviceFIG0_2; -}; - -// FIG type 0/3 -// The Extension 3 of FIG type 0 (FIG 0/3) gives additional information about -// the service component description in packet mode. -class FIG0_3 : public IFIG -{ - public: - FIG0_3(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::A; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 3; } - - private: - FIGRuntimeInformation *m_rti; - bool m_initialised; - std::vector::iterator componentFIG0_3; -}; - -// FIG type 0/5 -// The service component language feature is used to signal a language -// associated with a service component; the spoken language of an audio -// component or the language of the content of a data component. This -// information can be used for user selection or display. The feature is -// encoded in Extension 5 of FIG type 0 (FIG 0/5). -class FIG0_5 : public IFIG -{ - public: - FIG0_5(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 5; } - - private: - FIGRuntimeInformation *m_rti; - bool m_initialised; - std::vector::iterator componentFIG0_5; -}; - -// FIG type 0/8 -// The Extension 8 of FIG type 0 (FIG 0/8) provides information to link -// together the service component description that is valid within the ensemble -// to a service component description that is valid in other ensembles -class FIG0_8 : public IFIG -{ - public: - FIG0_8(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 8; } - - private: - FIGRuntimeInformation *m_rti; - bool m_initialised; - bool m_transmit_programme; - std::vector::iterator componentFIG0_8; -}; - -// FIG type 0/9 -// The Country, LTO and International table feature defines the local time -// offset, the International Table and the Extended Country Code (ECC) -class FIG0_9 : public IFIG -{ - public: - FIG0_9(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 9; } - - private: - FIGRuntimeInformation *m_rti; -}; - - -// FIG type 0/10 -// The date and time feature is used to signal a location-independent timing -// reference in UTC format. -class FIG0_10 : public IFIG -{ - public: - FIG0_10(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 10; } - - private: - FIGRuntimeInformation *m_rti; -}; - -// FIG type 0/13 -// User Application Information -class FIG0_13 : public IFIG -{ - public: - FIG0_13(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 13; } - - private: - FIGRuntimeInformation *m_rti; - bool m_initialised; - bool m_transmit_programme; - std::vector::iterator componentFIG0_13; -}; - -// FIG type 0/17 -// Programme Type (PTy) -class FIG0_17 : public IFIG -{ - public: - FIG0_17(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 17; } - - private: - FIGRuntimeInformation *m_rti; - bool m_initialised; - std::vector >::iterator serviceFIG0_17; -}; - -// FIG type 0/18 -class FIG0_18 : public IFIG -{ - public: - FIG0_18(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 18; } - - private: - FIGRuntimeInformation *m_rti; - bool m_initialised; - std::vector >::iterator service; -}; - -// FIG type 0/19 -class FIG0_19 : public IFIG -{ - public: - FIG0_19(FIGRuntimeInformation* rti); - virtual FillStatus fill(uint8_t *buf, size_t max_size); - virtual FIG_rate repetition_rate(void); - - virtual const int figtype(void) const { return 0; } - virtual const int figextension(void) const { return 19; } - - private: - FIGRuntimeInformation *m_rti; - - TransitionHandler m_transition; -}; - -} // namespace FIC +#include "fig/FIG0structs.h" +#include "fig/FIG0_0.h" +#include "fig/FIG0_1.h" +#include "fig/FIG0_2.h" +#include "fig/FIG0_3.h" +#include "fig/FIG0_5.h" +#include "fig/FIG0_8.h" +#include "fig/FIG0_9.h" +#include "fig/FIG0_10.h" +#include "fig/FIG0_13.h" +#include "fig/FIG0_17.h" +#include "fig/FIG0_18.h" +#include "fig/FIG0_19.h" diff --git a/src/fig/FIG0_0.cpp b/src/fig/FIG0_0.cpp new file mode 100644 index 0000000..b4b0b3a --- /dev/null +++ b/src/fig/FIG0_0.cpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_0.h" +#include "utils.h" + +namespace FIC { + +//=========== FIG 0/0 =========== + +FillStatus FIG0_0::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + + if (max_size < 6) { + fs.num_bytes_written = 0; + return fs; + } + + FIGtype0_0 *fig0_0; + fig0_0 = (FIGtype0_0 *)buf; + + fig0_0->FIGtypeNumber = 0; + fig0_0->Length = 5; + fig0_0->CN = 0; + fig0_0->OE = 0; + fig0_0->PD = 0; + fig0_0->Extension = 0; + + fig0_0->EId = htons(m_rti->ensemble->id); + fig0_0->Change = 0; + fig0_0->Al = 0; + fig0_0->CIFcnt_hight = (m_rti->currentFrame / 250) % 20; + fig0_0->CIFcnt_low = (m_rti->currentFrame % 250); + + fs.complete_fig_transmitted = true; + fs.num_bytes_written = 6; + return fs; +} + +} + diff --git a/src/fig/FIG0_0.h b/src/fig/FIG0_0.h new file mode 100644 index 0000000..a5310c8 --- /dev/null +++ b/src/fig/FIG0_0.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/0, Multiplex Configuration Info (MCI), +// Ensemble information +class FIG0_0 : public IFIG +{ + public: + FIG0_0(FIGRuntimeInformation* rti) : + m_rti(rti) {} + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::FIG0_0; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 0; } + + private: + FIGRuntimeInformation *m_rti; +}; + +} diff --git a/src/fig/FIG0_1.cpp b/src/fig/FIG0_1.cpp new file mode 100644 index 0000000..6ed40dd --- /dev/null +++ b/src/fig/FIG0_1.cpp @@ -0,0 +1,209 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_1.h" +#include "utils.h" + +namespace FIC { + +FIG0_1::FIG0_1(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false), + m_watermarkSize(0), + m_watermarkPos(0) +{ + uint8_t buffer[sizeof(m_watermarkData) / 2]; + snprintf((char*)buffer, sizeof(buffer), + "%s %s, %s %s", + PACKAGE_NAME, +#if defined(GITVERSION) + GITVERSION, +#else + PACKAGE_VERSION, +#endif + __DATE__, __TIME__); + + memset(m_watermarkData, 0, sizeof(m_watermarkData)); + m_watermarkData[0] = 0x55; // Sync + m_watermarkData[1] = 0x55; + m_watermarkSize = 16; + for (unsigned i = 0; i < strlen((char*)buffer); ++i) { + for (int j = 0; j < 8; ++j) { + uint8_t bit = (buffer[m_watermarkPos >> 3] >> (7 - (m_watermarkPos & 0x07))) & 1; + m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07)); + ++m_watermarkSize; + bit = 1; + m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07)); + ++m_watermarkSize; + ++m_watermarkPos; + } + } + m_watermarkPos = 0; +} + +FillStatus FIG0_1::fill(uint8_t *buf, size_t max_size) +{ +#define FIG0_1_TRACE discard + + FillStatus fs; + size_t remaining = max_size; + + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill initialised=" << + (m_initialised ? 1 : 0); + + const int watermark_bit = (m_watermarkData[m_watermarkPos >> 3] >> + (7 - (m_watermarkPos & 0x07))) & 1; + + const bool iterate_forward = (watermark_bit == 1); + + if (not m_initialised) { + m_initialised = true; + + subchannels = m_rti->ensemble->subchannels; + + if (not iterate_forward) { + std::reverse(subchannels.begin(), subchannels.end()); + } + subchannelFIG0_1 = subchannels.begin(); + } + + if (max_size < 6) { + return fs; + } + + FIGtype0_1 *figtype0_1 = NULL; + + // Rotate through the subchannels until there is no more + // space in the FIG0/1 + for (; subchannelFIG0_1 != subchannels.end(); ++subchannelFIG0_1 ) { + size_t subch_iter_ix = std::distance(subchannels.begin(), subchannelFIG0_1); + + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill loop ix=" << subch_iter_ix; + + dabProtection* protection = &(*subchannelFIG0_1)->protection; + + if (figtype0_1 == NULL) { + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill header " << + (protection->form == UEP ? "UEP " : "EEP ") << remaining; + + if ( (protection->form == UEP && remaining < 2 + 3) || + (protection->form == EEP && remaining < 2 + 4) ) { + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill no space for header"; + break; + } + + figtype0_1 = (FIGtype0_1*)buf; + + figtype0_1->FIGtypeNumber = 0; + figtype0_1->Length = 1; + figtype0_1->CN = 0; + figtype0_1->OE = 0; + figtype0_1->PD = 0; + figtype0_1->Extension = 1; + buf += 2; + remaining -= 2; + } + else if ( (protection->form == UEP && remaining < 3) || + (protection->form == EEP && remaining < 4) ) { + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill no space for fig " << + (protection->form == UEP ? "UEP " : "EEP ") << remaining; + break; + } + + if (protection->form == UEP) { + FIG_01_SubChannel_ShortF *fig0_1subchShort = + (FIG_01_SubChannel_ShortF*)buf; + fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id; + + fig0_1subchShort->StartAdress_high = + (*subchannelFIG0_1)->startAddress / 256; + fig0_1subchShort->StartAdress_low = + (*subchannelFIG0_1)->startAddress % 256; + + fig0_1subchShort->Short_Long_form = 0; + fig0_1subchShort->TableSwitch = 0; + fig0_1subchShort->TableIndex = + protection->uep.tableIndex; + + buf += 3; + remaining -= 3; + figtype0_1->Length += 3; + + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill insert UEP id=" << + (int)fig0_1subchShort->SubChId << " rem=" << remaining + << " ix=" << subch_iter_ix; + } + else if (protection->form == EEP) { + FIG_01_SubChannel_LongF *fig0_1subchLong1 = + (FIG_01_SubChannel_LongF*)buf; + fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id; + + fig0_1subchLong1->StartAdress_high = + (*subchannelFIG0_1)->startAddress / 256; + fig0_1subchLong1->StartAdress_low = + (*subchannelFIG0_1)->startAddress % 256; + + fig0_1subchLong1->Short_Long_form = 1; + fig0_1subchLong1->Option = protection->eep.GetOption(); + fig0_1subchLong1->ProtectionLevel = + protection->level; + + fig0_1subchLong1->Sub_ChannelSize_high = + (*subchannelFIG0_1)->getSizeCu() / 256; + fig0_1subchLong1->Sub_ChannelSize_low = + (*subchannelFIG0_1)->getSizeCu() % 256; + + buf += 4; + remaining -= 4; + figtype0_1->Length += 4; + + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill insert EEP id=" << + (int)fig0_1subchLong1->SubChId << " rem=" << remaining + << " ix=" << subch_iter_ix; + } + } + + size_t subch_iter_ix = std::distance(subchannels.begin(), subchannelFIG0_1); + + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill loop out, rem=" << remaining + << " ix=" << subch_iter_ix; + + if (subchannelFIG0_1 == subchannels.end()) { + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill completed, rem=" << remaining; + m_initialised = false; + fs.complete_fig_transmitted = true; + + m_watermarkPos++; + if (m_watermarkPos == m_watermarkSize) { + m_watermarkPos = 0; + } + } + + fs.num_bytes_written = max_size - remaining; + etiLog.level(FIG0_1_TRACE) << "FIG0_1::fill wrote " << fs.num_bytes_written; + return fs; +} + +} diff --git a/src/fig/FIG0_1.h b/src/fig/FIG0_1.h new file mode 100644 index 0000000..6da4aec --- /dev/null +++ b/src/fig/FIG0_1.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/1, MIC, Sub-Channel Organization, +// one instance of the part for each subchannel +class FIG0_1 : public IFIG +{ + public: + FIG0_1(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::A; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 1; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + std::vector subchannels; + std::vector::iterator subchannelFIG0_1; + + uint8_t m_watermarkData[128]; + size_t m_watermarkSize; + size_t m_watermarkPos; +}; + +} diff --git a/src/fig/FIG0_10.cpp b/src/fig/FIG0_10.cpp new file mode 100644 index 0000000..e4ff710 --- /dev/null +++ b/src/fig/FIG0_10.cpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_10.h" +#include "utils.h" + +namespace FIC { + +FIG0_10::FIG0_10(FIGRuntimeInformation *rti) : + m_rti(rti) +{ +} + +FillStatus FIG0_10::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + auto ensemble = m_rti->ensemble; + size_t remaining = max_size; + + if (remaining < 8) { + fs.num_bytes_written = 0; + return fs; + } + + //Time and country identifier + auto fig0_10 = (FIGtype0_10_LongForm*)buf; + + fig0_10->FIGtypeNumber = 0; + fig0_10->Length = 7; + fig0_10->CN = 0; + fig0_10->OE = 0; + fig0_10->PD = 0; + fig0_10->Extension = 10; + buf += 2; + remaining -= 2; + + tm* timeData; + time_t dab_time_seconds = 0; + uint32_t dab_time_millis = 0; + get_dab_time(&dab_time_seconds, &dab_time_millis); + timeData = gmtime(&dab_time_seconds); + + fig0_10->RFU = 0; + fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900, + timeData->tm_mon + 1, + timeData->tm_mday)); + fig0_10->LSI = 0; + fig0_10->ConfInd = 1; + fig0_10->UTC = 1; + fig0_10->setHours(timeData->tm_hour); + fig0_10->Minutes = timeData->tm_min; + fig0_10->Seconds = timeData->tm_sec; + fig0_10->setMilliseconds(dab_time_millis); + buf += 6; + remaining -= 6; + + fs.num_bytes_written = max_size - remaining; + fs.complete_fig_transmitted = true; + return fs; +} + +} diff --git a/src/fig/FIG0_10.h b/src/fig/FIG0_10.h new file mode 100644 index 0000000..2991189 --- /dev/null +++ b/src/fig/FIG0_10.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/10 +// The date and time feature is used to signal a location-independent timing +// reference in UTC format. +class FIG0_10 : public IFIG +{ + public: + FIG0_10(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 10; } + + private: + FIGRuntimeInformation *m_rti; +}; + +} diff --git a/src/fig/FIG0_13.cpp b/src/fig/FIG0_13.cpp new file mode 100644 index 0000000..51704f4 --- /dev/null +++ b/src/fig/FIG0_13.cpp @@ -0,0 +1,182 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_13.h" +#include "utils.h" + +namespace FIC { + +FIG0_13::FIG0_13(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false), + m_transmit_programme(false) +{ +} + +FillStatus FIG0_13::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + auto ensemble = m_rti->ensemble; + ssize_t remaining = max_size; + + if (not m_initialised) { + componentFIG0_13 = m_rti->ensemble->components.end(); + m_initialised = true; + } + + FIGtype0* fig0 = NULL; + + for (; componentFIG0_13 != ensemble->components.end(); + ++componentFIG0_13) { + + auto subchannel = getSubchannel(ensemble->subchannels, + (*componentFIG0_13)->subchId); + + if (subchannel == ensemble->subchannels.end()) { + etiLog.log(error, + "Subchannel %i does not exist for component " + "of service %i\n", + (*componentFIG0_13)->subchId, + (*componentFIG0_13)->serviceId); + throw MuxInitException(); + } + + if ( m_transmit_programme && + (*subchannel)->type == subchannel_type_t::Audio && + (*componentFIG0_13)->audio.uaType != 0xffff) { + + const int required_size = 3+4+11; + + if (fig0 == NULL) { + if (remaining < 2 + required_size) { + break; + } + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 0; + fig0->Extension = 13; + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + break; + } + + FIG0_13_shortAppInfo* info = (FIG0_13_shortAppInfo*)buf; + info->SId = htonl((*componentFIG0_13)->serviceId) >> 16; + info->SCIdS = (*componentFIG0_13)->SCIdS; + info->No = 1; + buf += 3; + remaining -= 3; + fig0->Length += 3; + + FIG0_13_app* app = (FIG0_13_app*)buf; + app->setType((*componentFIG0_13)->audio.uaType); + app->length = 2; + app->xpad = htons(0x0c3c); + /* xpad meaning + CA = 0 + CAOrg = 0 + Rfu = 0 + AppTy(5) = 12 (MOT, start of X-PAD data group) + DG = 0 (MSC data groups used) + Rfu = 0 + DSCTy(6) = 60 (MOT) + */ + + buf += 2 + app->length; + remaining -= 2 + app->length; + fig0->Length += 2 + app->length; + } + else if (!m_transmit_programme && + (*subchannel)->type == subchannel_type_t::Packet && + (*componentFIG0_13)->packet.appType != 0xffff) { + + const int required_size = 5+2+2; + + if (fig0 == NULL) { + if (remaining < 2 + required_size) { + break; + } + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 1; + fig0->Extension = 13; + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + break; + } + + FIG0_13_longAppInfo* info = (FIG0_13_longAppInfo*)buf; + info->SId = htonl((*componentFIG0_13)->serviceId); + info->SCIdS = (*componentFIG0_13)->SCIdS; + info->No = 1; + buf += 5; + remaining -= 5; + fig0->Length += 5; + + FIG0_13_app* app = (FIG0_13_app*)buf; + app->setType((*componentFIG0_13)->packet.appType); + if (app->typeLow == FIG0_13_APPTYPE_EPG) { + app->length = 2; + app->xpad = htons(0x0100); + /* xpad used to hold two bytes of EPG profile information + 01 = basic profile + 00 = list terminator */ + } + else { + app->length = 0; + } + buf += 2 + app->length; + remaining -= 2 + app->length; + fig0->Length += 2 + app->length; + } + } + + if (componentFIG0_13 == ensemble->components.end()) { + componentFIG0_13 = ensemble->components.begin(); + + // The full database is sent every second full loop + fs.complete_fig_transmitted = m_transmit_programme; + + m_transmit_programme = not m_transmit_programme; + // Alternate between data and and programme FIG0/13, + // do not mix fig0 with PD=0 with extension 13 stuff + // that actually needs PD=1, and vice versa + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +} diff --git a/src/fig/FIG0_13.h b/src/fig/FIG0_13.h new file mode 100644 index 0000000..4c29278 --- /dev/null +++ b/src/fig/FIG0_13.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/13 +// User Application Information +class FIG0_13 : public IFIG +{ + public: + FIG0_13(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 13; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + bool m_transmit_programme; + std::vector::iterator componentFIG0_13; +}; + +} diff --git a/src/fig/FIG0_17.cpp b/src/fig/FIG0_17.cpp new file mode 100644 index 0000000..2f5f03e --- /dev/null +++ b/src/fig/FIG0_17.cpp @@ -0,0 +1,103 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_17.h" +#include "utils.h" + +namespace FIC { + +FIG0_17::FIG0_17(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false) +{ +} + +FillStatus FIG0_17::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + ssize_t remaining = max_size; + + if (not m_initialised) { + serviceFIG0_17 = m_rti->ensemble->services.end(); + m_initialised = true; + } + + auto ensemble = m_rti->ensemble; + + FIGtype0* fig0 = NULL; + + for (; serviceFIG0_17 != ensemble->services.end(); + ++serviceFIG0_17) { + + if ((*serviceFIG0_17)->pty == 0) { + continue; + } + + const int required_size = 4; + + + if (fig0 == NULL) { + if (remaining < 2 + required_size) { + break; + } + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 0; + fig0->Extension = 17; + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + break; + } + + auto fig0_17 = (FIGtype0_17*)buf; + fig0_17->SId = htons((*serviceFIG0_17)->id); + fig0_17->SD = 1; // We only transmit dynamic PTy + fig0_17->rfa1 = 0; + fig0_17->rfu1 = 0; + fig0_17->rfa2_low = 0; + fig0_17->rfa2_high = 0; + fig0_17->rfu2 = 0; + fig0_17->IntCode = (*serviceFIG0_17)->pty; + + fig0->Length += 4; + buf += 4; + remaining -= 4; + } + + if (serviceFIG0_17 == ensemble->services.end()) { + serviceFIG0_17 = ensemble->services.begin(); + fs.complete_fig_transmitted = true; + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +} diff --git a/src/fig/FIG0_17.h b/src/fig/FIG0_17.h new file mode 100644 index 0000000..f2861d8 --- /dev/null +++ b/src/fig/FIG0_17.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/17 +// Programme Type (PTy) +class FIG0_17 : public IFIG +{ + public: + FIG0_17(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 17; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + std::vector >::iterator serviceFIG0_17; +}; + +} diff --git a/src/fig/FIG0_18.cpp b/src/fig/FIG0_18.cpp new file mode 100644 index 0000000..9edc3d5 --- /dev/null +++ b/src/fig/FIG0_18.cpp @@ -0,0 +1,106 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_18.h" +#include "utils.h" + +namespace FIC { + +FIG0_18::FIG0_18(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false) +{ +} + +FillStatus FIG0_18::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + ssize_t remaining = max_size; + + if (not m_initialised) { + service = m_rti->ensemble->services.end(); + m_initialised = true; + } + + auto ensemble = m_rti->ensemble; + + FIGtype0* fig0 = NULL; + + for (; service != ensemble->services.end(); + ++service) { + + if ( (*service)->ASu == 0 ) { + continue; + } + + const ssize_t numclusters = (*service)->clusters.size(); + + const int required_size = 5 + numclusters; + + + if (fig0 == NULL) { + if (remaining < 2 + required_size) { + break; + } + + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 0; + fig0->Extension = 18; + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + break; + } + + auto programme = (FIGtype0_18*)buf; + programme->SId = htons((*service)->id); + programme->ASu = htons((*service)->ASu); + programme->Rfa = 0; + programme->NumClusters = numclusters; + buf += 5; + + for (uint8_t cluster : (*service)->clusters) { + *(buf++) = cluster; + } + + fig0->Length += 5 + numclusters; + remaining -= 5 + numclusters; + } + + if (service == ensemble->services.end()) { + service = ensemble->services.begin(); + fs.complete_fig_transmitted = true; + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +} diff --git a/src/fig/FIG0_18.h b/src/fig/FIG0_18.h new file mode 100644 index 0000000..7712efb --- /dev/null +++ b/src/fig/FIG0_18.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/18 +class FIG0_18 : public IFIG +{ + public: + FIG0_18(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 18; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + std::vector >::iterator service; +}; + +} diff --git a/src/fig/FIG0_19.cpp b/src/fig/FIG0_19.cpp new file mode 100644 index 0000000..8b9aca8 --- /dev/null +++ b/src/fig/FIG0_19.cpp @@ -0,0 +1,136 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_19.h" +#include "utils.h" + +namespace FIC { + +FIG0_19::FIG0_19(FIGRuntimeInformation *rti) : + m_rti(rti) +{ } + +FillStatus FIG0_19::fill(uint8_t *buf, size_t max_size) +{ + using namespace std; + + auto ensemble = m_rti->ensemble; + + // We are called every 24ms, and must timeout after 2s + const int timeout = 2000/24; + + m_transition.update_state(timeout, ensemble->clusters); + + FillStatus fs; + ssize_t remaining = max_size; + + FIGtype0* fig0 = NULL; + + // Combine all clusters into one list + set allclusters; + for (const auto& cluster : m_transition.new_entries) { + allclusters.insert(cluster.first.get()); + } + for (const auto& cluster : m_transition.repeated_entries) { + allclusters.insert(cluster.get()); + } + for (const auto& cluster : m_transition.disabled_entries) { + allclusters.insert(cluster.first.get()); + } + + const int length_0_19 = 4; + fs.complete_fig_transmitted = true; + for (auto& cluster : allclusters) { + + if (fig0 == NULL) { + if (remaining < 2 + length_0_19) { + fs.complete_fig_transmitted = false; + break; + } + + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 0; + fig0->Extension = 19; + buf += 2; + remaining -= 2; + } + else if (remaining < length_0_19) { + fs.complete_fig_transmitted = false; + break; + } + + + auto fig0_19 = (FIGtype0_19*)buf; + fig0_19->ClusterId = cluster->cluster_id; + if (cluster->is_active()) { + fig0_19->ASw = htons(cluster->flags); + } + else { + fig0_19->ASw = 0; + } + fig0_19->NewFlag = 1; + fig0_19->RegionFlag = 0; + fig0_19->SubChId = 0; + bool found = false; + + for (const auto& subchannel : ensemble->subchannels) { + if (subchannel->uid == cluster->subchanneluid) { + fig0_19->SubChId = subchannel->id; + found = true; + break; + } + } + if (not found) { + etiLog.level(warn) << "FIG0/19: could not find subchannel " << + cluster->subchanneluid << " for cluster " << + (int)cluster->cluster_id; + continue; + } + + fig0->Length += length_0_19; + buf += length_0_19; + remaining -= length_0_19; + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +FIG_rate FIG0_19::repetition_rate(void) +{ + if ( m_transition.new_entries.size() > 0 or + m_transition.disabled_entries.size() ) { + return FIG_rate::A_B; + } + else { + return FIG_rate::B; + } +} + +} diff --git a/src/fig/FIG0_19.h b/src/fig/FIG0_19.h new file mode 100644 index 0000000..ce76f30 --- /dev/null +++ b/src/fig/FIG0_19.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" +#include "fig/TransitionHandler.h" + +namespace FIC { + +// FIG type 0/19 +class FIG0_19 : public IFIG +{ + public: + FIG0_19(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void); + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 19; } + + private: + FIGRuntimeInformation *m_rti; + + TransitionHandler m_transition; +}; + +} diff --git a/src/fig/FIG0_2.cpp b/src/fig/FIG0_2.cpp new file mode 100644 index 0000000..04bff60 --- /dev/null +++ b/src/fig/FIG0_2.cpp @@ -0,0 +1,266 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_2.h" +#include "utils.h" + +namespace FIC { + +FIG0_2::FIG0_2(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false), + m_inserting_audio_not_data(false) +{ +} + +FillStatus FIG0_2::fill(uint8_t *buf, size_t max_size) +{ +#define FIG0_2_TRACE discard + using namespace std; + + FillStatus fs; + FIGtype0_2 *fig0_2 = NULL; + int cur = 0; + ssize_t remaining = max_size; + + const auto ensemble = m_rti->ensemble; + + etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill init " << (m_initialised ? 1 : 0) << + " ********************************"; + + for (const auto& s : ensemble->services) { + // Exclude Fidc type services, TODO unsupported + auto type = s->getType(ensemble); + if (type == subchannel_type_t::Fidc) { + throw invalid_argument("FIG0/2 does not support FIDC"); + } + } + + if (not m_initialised) { + m_audio_services.clear(); + copy_if(ensemble->services.begin(), + ensemble->services.end(), + back_inserter(m_audio_services), + [&](shared_ptr& s) { + return s->isProgramme(ensemble); + } ); + + m_data_services.clear(); + copy_if(ensemble->services.begin(), + ensemble->services.end(), + back_inserter(m_data_services), + [&](shared_ptr& s) { + return not s->isProgramme(ensemble); + } ); + + m_initialised = true; + m_inserting_audio_not_data = !m_inserting_audio_not_data; + + if (m_inserting_audio_not_data) { + serviceFIG0_2 = m_audio_services.begin(); + } + else { + serviceFIG0_2 = m_data_services.begin(); + } + + etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill we have " << + m_audio_services.size() << " audio and " << + m_data_services.size() << " data services. Inserting " << + (m_inserting_audio_not_data ? "AUDIO" : "DATA"); + } + + const auto last_service = m_inserting_audio_not_data ? + m_audio_services.end() : m_data_services.end(); + + // Rotate through the subchannels until there is no more + // space + for (; serviceFIG0_2 != last_service; ++serviceFIG0_2) { + const auto type = (*serviceFIG0_2)->getType(ensemble); + const auto isProgramme = (*serviceFIG0_2)->isProgramme(ensemble); + + etiLog.log(FIG0_2_TRACE, "FIG0_2::fill loop SId=%04x %s/%s", + (*serviceFIG0_2)->id, + m_inserting_audio_not_data ? "AUDIO" : "DATA", + type == subchannel_type_t::Audio ? "Audio" : + type == subchannel_type_t::Packet ? "Packet" : + type == subchannel_type_t::DataDmb ? "DataDmb" : + type == subchannel_type_t::Fidc ? "Fidc" : "?"); + + // filter out services which have no components + if ((*serviceFIG0_2)->nbComponent(ensemble->components) == 0) { + etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill no components "; + continue; + } + + ++cur; + + const int required_size = isProgramme ? + 3 + 2 * (*serviceFIG0_2)->nbComponent(ensemble->components) : + 5 + 2 * (*serviceFIG0_2)->nbComponent(ensemble->components); + + if (fig0_2 == NULL) { + etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill header"; + if (remaining < 2 + required_size) { + etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill header no place" << + " rem=" << remaining << " req=" << 2 + required_size; + break; + } + fig0_2 = (FIGtype0_2 *)buf; + + fig0_2->FIGtypeNumber = 0; + fig0_2->Length = 1; + fig0_2->CN = 0; + fig0_2->OE = 0; + fig0_2->PD = isProgramme ? 0 : 1; + fig0_2->Extension = 2; + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + etiLog.level(FIG0_2_TRACE) << "FIG0_2::fill no place" << + " rem=" << remaining << " req=" << required_size; + break; + } + + if (type == subchannel_type_t::Audio) { + auto fig0_2serviceAudio = (FIGtype0_2_Service*)buf; + + fig0_2serviceAudio->SId = htons((*serviceFIG0_2)->id); + fig0_2serviceAudio->Local_flag = 0; + fig0_2serviceAudio->CAId = 0; + fig0_2serviceAudio->NbServiceComp = + (*serviceFIG0_2)->nbComponent(ensemble->components); + buf += 3; + fig0_2->Length += 3; + remaining -= 3; + + etiLog.log(FIG0_2_TRACE, "FIG0_2::fill audio SId=%04x", + (*serviceFIG0_2)->id); + } + else { + auto fig0_2serviceData = (FIGtype0_2_Service_data*)buf; + + fig0_2serviceData->SId = htonl((*serviceFIG0_2)->id); + fig0_2serviceData->Local_flag = 0; + fig0_2serviceData->CAId = 0; + fig0_2serviceData->NbServiceComp = + (*serviceFIG0_2)->nbComponent(ensemble->components); + buf += 5; + fig0_2->Length += 5; + remaining -= 5; + + etiLog.log(FIG0_2_TRACE, "FIG0_2::fill data SId=%04x", + (*serviceFIG0_2)->id); + } + + int curCpnt = 0; + for (auto component = getComponent( + ensemble->components, (*serviceFIG0_2)->id ); + component != ensemble->components.end(); + component = getComponent( + ensemble->components, + (*serviceFIG0_2)->id, + component ) + ) { + auto subchannel = getSubchannel( + ensemble->subchannels, (*component)->subchId); + + etiLog.log(FIG0_2_TRACE, "FIG0_2::fill comp sub=%04x srv=%04x", + (*component)->subchId, (*component)->serviceId); + + if (subchannel == ensemble->subchannels.end()) { + etiLog.log(error, + "Subchannel %i does not exist for component " + "of service %i\n", + (*component)->subchId, (*component)->serviceId); + throw MuxInitException(); + } + + switch ((*subchannel)->type) { + case subchannel_type_t::Audio: + { + auto audio_description = (FIGtype0_2_audio_component*)buf; + audio_description->TMid = 0; + audio_description->ASCTy = (*component)->type; + audio_description->SubChId = (*subchannel)->id; + audio_description->PS = ((curCpnt == 0) ? 1 : 0); + audio_description->CA_flag = 0; + } + break; + case subchannel_type_t::DataDmb: + { + auto data_description = (FIGtype0_2_data_component*)buf; + data_description->TMid = 1; + data_description->DSCTy = (*component)->type; + data_description->SubChId = (*subchannel)->id; + data_description->PS = ((curCpnt == 0) ? 1 : 0); + data_description->CA_flag = 0; + } + break; + case subchannel_type_t::Packet: + { + auto packet_description = (FIGtype0_2_packet_component*)buf; + packet_description->TMid = 3; + packet_description->setSCId((*component)->packet.id); + packet_description->PS = ((curCpnt == 0) ? 1 : 0); + packet_description->CA_flag = 0; + } + break; + default: + etiLog.log(error, + "Component type not supported\n"); + throw MuxInitException(); + } + buf += 2; + fig0_2->Length += 2; + remaining -= 2; + if (remaining < 0) { + etiLog.log(error, + "Sorry, no space left in FIG 0/2 to insert " + "component %i of program service %i.\n", + curCpnt, cur); + throw MuxInitException(); + } + ++curCpnt; + + etiLog.log(FIG0_2_TRACE, "FIG0_2::fill comp length=%d", + fig0_2->Length); + } + } + + if (serviceFIG0_2 == last_service) { + etiLog.log(FIG0_2_TRACE, "FIG0_2::loop reached last"); + m_initialised = false; + fs.complete_fig_transmitted = !m_inserting_audio_not_data; + } + + etiLog.log(FIG0_2_TRACE, "FIG0_2::loop end complete=%d", + fs.complete_fig_transmitted ? 1 : 0); + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +} diff --git a/src/fig/FIG0_2.h b/src/fig/FIG0_2.h new file mode 100644 index 0000000..4190922 --- /dev/null +++ b/src/fig/FIG0_2.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { +// FIG type 0/2, MCI, Service Organization, one instance of +// FIGtype0_2_Service for each subchannel +class FIG0_2 : public IFIG +{ + public: + FIG0_2(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::A; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 2; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + bool m_inserting_audio_not_data; + std::vector > m_audio_services; + std::vector > m_data_services; + std::vector >::iterator serviceFIG0_2; +}; + +} diff --git a/src/fig/FIG0_3.cpp b/src/fig/FIG0_3.cpp new file mode 100644 index 0000000..2e84e20 --- /dev/null +++ b/src/fig/FIG0_3.cpp @@ -0,0 +1,124 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_3.h" +#include "utils.h" + +namespace FIC { + +FIG0_3::FIG0_3(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false) +{ +} + +FillStatus FIG0_3::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + ssize_t remaining = max_size; + auto ensemble = m_rti->ensemble; + + if (not m_initialised) { + componentFIG0_3 = m_rti->ensemble->components.end(); + m_initialised = true; + } + + FIGtype0 *fig0 = NULL; + + for (; componentFIG0_3 != ensemble->components.end(); + ++componentFIG0_3) { + auto subchannel = getSubchannel(ensemble->subchannels, + (*componentFIG0_3)->subchId); + + if (subchannel == ensemble->subchannels.end()) { + etiLog.log(error, + "Subchannel %i does not exist for component " + "of service %i\n", + (*componentFIG0_3)->subchId, (*componentFIG0_3)->serviceId); + throw MuxInitException(); + } + + if ((*subchannel)->type != subchannel_type_t::Packet) + continue; + + const int required_size = 5 + (m_rti->factumAnalyzer ? 2 : 0); + + if (fig0 == NULL) { + if (remaining < 2 + required_size) { + break; + } + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 0; + fig0->Extension = 3; + + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + break; + } + + /* Warning: When bit SCCA_flag is unset(0), the multiplexer + * R&S does not send the SCCA field. But, in the Factum ETI + * analyzer, if this field is not there, it is an error. + */ + FIGtype0_3 *fig0_3 = (FIGtype0_3*)buf; + fig0_3->setSCId((*componentFIG0_3)->packet.id); + fig0_3->rfa = 0; + fig0_3->SCCA_flag = 0; + // if 0, datagroups are used + fig0_3->DG_flag = !(*componentFIG0_3)->packet.datagroup; + fig0_3->rfu = 0; + fig0_3->DSCTy = (*componentFIG0_3)->type; + fig0_3->SubChId = (*subchannel)->id; + fig0_3->setPacketAddress((*componentFIG0_3)->packet.address); + if (m_rti->factumAnalyzer) { + fig0_3->SCCA = 0; + } + + fig0->Length += 5; + buf += 5; + remaining -= 5; + if (m_rti->factumAnalyzer) { + fig0->Length += 2; + buf += 2; + remaining -= 2; + } + } + + if (componentFIG0_3 == ensemble->components.end()) { + componentFIG0_3 = ensemble->components.begin(); + fs.complete_fig_transmitted = true; + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +} diff --git a/src/fig/FIG0_3.h b/src/fig/FIG0_3.h new file mode 100644 index 0000000..7b77086 --- /dev/null +++ b/src/fig/FIG0_3.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/3 +// The Extension 3 of FIG type 0 (FIG 0/3) gives additional information about +// the service component description in packet mode. +class FIG0_3 : public IFIG +{ + public: + FIG0_3(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::A; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 3; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + std::vector::iterator componentFIG0_3; +}; + +} diff --git a/src/fig/FIG0_5.cpp b/src/fig/FIG0_5.cpp new file mode 100644 index 0000000..8650d29 --- /dev/null +++ b/src/fig/FIG0_5.cpp @@ -0,0 +1,113 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_5.h" +#include "utils.h" + +namespace FIC { + +FIG0_5::FIG0_5(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false) +{ +} + +FillStatus FIG0_5::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + ssize_t remaining = max_size; + auto ensemble = m_rti->ensemble; + + if (not m_initialised) { + componentFIG0_5 = m_rti->ensemble->components.end(); + m_initialised = true; + } + + FIGtype0* fig0 = NULL; + + for (; componentFIG0_5 != ensemble->components.end(); + ++componentFIG0_5) { + + auto service = getService(*componentFIG0_5, + ensemble->services); + auto subchannel = getSubchannel(ensemble->subchannels, + (*componentFIG0_5)->subchId); + + if (subchannel == ensemble->subchannels.end()) { + etiLog.log(error, + "Subchannel %i does not exist for component " + "of service %i\n", + (*componentFIG0_5)->subchId, + (*componentFIG0_5)->serviceId); + throw MuxInitException(); + } + + if ( (*service)->language == 0) { + continue; + } + + const int required_size = 2; + + if (fig0 == NULL) { + if (remaining < 2 + required_size) { + break; + } + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 0; + fig0->Extension = 5; + + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + break; + } + + FIGtype0_5_short *fig0_5 = (FIGtype0_5_short*)buf; + + fig0_5->LS = 0; + fig0_5->rfu = 0; + fig0_5->SubChId = (*subchannel)->id; + fig0_5->language = (*service)->language; + + fig0->Length += 2; + buf += 2; + remaining -= 2; + } + + if (componentFIG0_5 == ensemble->components.end()) { + componentFIG0_5 = ensemble->components.begin(); + fs.complete_fig_transmitted = true; + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +} diff --git a/src/fig/FIG0_5.h b/src/fig/FIG0_5.h new file mode 100644 index 0000000..2fc92e7 --- /dev/null +++ b/src/fig/FIG0_5.h @@ -0,0 +1,57 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/5 +// The service component language feature is used to signal a language +// associated with a service component; the spoken language of an audio +// component or the language of the content of a data component. This +// information can be used for user selection or display. The feature is +// encoded in Extension 5 of FIG type 0 (FIG 0/5). +class FIG0_5 : public IFIG +{ + public: + FIG0_5(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 5; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + std::vector::iterator componentFIG0_5; +}; + +} diff --git a/src/fig/FIG0_8.cpp b/src/fig/FIG0_8.cpp new file mode 100644 index 0000000..2d25547 --- /dev/null +++ b/src/fig/FIG0_8.cpp @@ -0,0 +1,204 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_8.h" +#include "utils.h" + +namespace FIC { + +FIG0_8::FIG0_8(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false), + m_transmit_programme(false) +{ +} + +FillStatus FIG0_8::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + auto ensemble = m_rti->ensemble; + ssize_t remaining = max_size; + + if (not m_initialised) { + componentFIG0_8 = m_rti->ensemble->components.end(); + m_initialised = true; + } + + FIGtype0* fig0 = NULL; + + for (; componentFIG0_8 != ensemble->components.end(); + ++componentFIG0_8) { + + auto service = getService(*componentFIG0_8, + ensemble->services); + auto subchannel = getSubchannel(ensemble->subchannels, + (*componentFIG0_8)->subchId); + + if (subchannel == ensemble->subchannels.end()) { + etiLog.log(error, + "Subchannel %i does not exist for component " + "of service %i\n", + (*componentFIG0_8)->subchId, + (*componentFIG0_8)->serviceId); + throw MuxInitException(); + } + + if (m_transmit_programme and (*service)->isProgramme(ensemble)) { + const int required_size = + ((*subchannel)->type == subchannel_type_t::Packet ? 5 : 4); + + if (fig0 == NULL) { + if (remaining < 2 + required_size) { + break; + } + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 0; + fig0->Extension = 8; + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + break; + } + + if ((*subchannel)->type == subchannel_type_t::Packet) { // Data packet + buf[0] = ((*componentFIG0_8)->serviceId >> 8) & 0xFF; + buf[1] = ((*componentFIG0_8)->serviceId) & 0xFF; + fig0->Length += 2; + buf += 2; + remaining -= 2; + + FIGtype0_8_long* definition = (FIGtype0_8_long*)buf; + memset(definition, 0, 3); + definition->ext = 0; // no rfa + definition->SCIdS = (*componentFIG0_8)->SCIdS; + definition->LS = 1; + definition->setSCId((*componentFIG0_8)->packet.id); + fig0->Length += 3; + buf += 3; // 8 minus rfa + remaining -= 3; + } + else { // Audio, data stream or FIDC + buf[0] = ((*componentFIG0_8)->serviceId >> 8) & 0xFF; + buf[1] = ((*componentFIG0_8)->serviceId) & 0xFF; + + fig0->Length += 2; + buf += 2; + remaining -= 2; + + FIGtype0_8_short* definition = (FIGtype0_8_short*)buf; + memset(definition, 0, 2); + definition->ext = 0; // no rfa + definition->SCIdS = (*componentFIG0_8)->SCIdS; + definition->LS = 0; + definition->MscFic = 0; + definition->Id = (*componentFIG0_8)->subchId; + fig0->Length += 2; + buf += 2; // 4 minus rfa + remaining -= 2; + } + } + else if (!m_transmit_programme and !(*service)->isProgramme(ensemble)) { + // Data + const int required_size = + ((*subchannel)->type == subchannel_type_t::Packet ? 7 : 6); + + if (fig0 == NULL) { + if (remaining < 2 + required_size) { + break; + } + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = 1; + fig0->Extension = 8; + buf += 2; + remaining -= 2; + } + else if (remaining < required_size) { + break; + } + + if ((*subchannel)->type == subchannel_type_t::Packet) { // Data packet + buf[0] = ((*componentFIG0_8)->serviceId >> 8) & 0xFF; + buf[1] = ((*componentFIG0_8)->serviceId) & 0xFF; + fig0->Length += 4; + buf += 4; + remaining -= 4; + + FIGtype0_8_long* definition = (FIGtype0_8_long*)buf; + memset(definition, 0, 3); + definition->ext = 0; // no rfa + definition->SCIdS = (*componentFIG0_8)->SCIdS; + definition->LS = 1; + definition->setSCId((*componentFIG0_8)->packet.id); + fig0->Length += 3; + buf += 3; // 8 minus rfa + remaining -= 3; + } + else { // Audio, data stream or FIDC + buf[0] = ((*componentFIG0_8)->serviceId >> 8) & 0xFF; + buf[1] = ((*componentFIG0_8)->serviceId) & 0xFF; + fig0->Length += 4; + buf += 4; + remaining -= 4; + + FIGtype0_8_short* definition = (FIGtype0_8_short*)buf; + memset(definition, 0, 2); + definition->ext = 0; // no rfa + definition->SCIdS = (*componentFIG0_8)->SCIdS; + definition->LS = 0; + definition->MscFic = 0; + definition->Id = (*componentFIG0_8)->subchId; + fig0->Length += 2; + buf += 2; // 4 minus rfa + remaining -= 2; + } + } + } + + if (componentFIG0_8 == ensemble->components.end()) { + componentFIG0_8 = ensemble->components.begin(); + + // The full database is sent every second full loop + fs.complete_fig_transmitted = m_transmit_programme; + + m_transmit_programme = not m_transmit_programme; + // Alternate between data and and programme FIG0/13, + // do not mix fig0 with PD=0 with extension 13 stuff + // that actually needs PD=1, and vice versa + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +} diff --git a/src/fig/FIG0_8.h b/src/fig/FIG0_8.h new file mode 100644 index 0000000..600f386 --- /dev/null +++ b/src/fig/FIG0_8.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/8 +// The Extension 8 of FIG type 0 (FIG 0/8) provides information to link +// together the service component description that is valid within the ensemble +// to a service component description that is valid in other ensembles +class FIG0_8 : public IFIG +{ + public: + FIG0_8(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 8; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + bool m_transmit_programme; + std::vector::iterator componentFIG0_8; +}; + +} diff --git a/src/fig/FIG0_9.cpp b/src/fig/FIG0_9.cpp new file mode 100644 index 0000000..00c2995 --- /dev/null +++ b/src/fig/FIG0_9.cpp @@ -0,0 +1,83 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#include "fig/FIG0_9.h" +#include "utils.h" + +namespace FIC { + +FIG0_9::FIG0_9(FIGRuntimeInformation *rti) : + m_rti(rti) {} + +FillStatus FIG0_9::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + auto ensemble = m_rti->ensemble; + size_t remaining = max_size; + + if (remaining < 5) { + fs.num_bytes_written = 0; + return fs; + } + + auto fig0_9 = (FIGtype0_9*)buf; + fig0_9->FIGtypeNumber = 0; + fig0_9->Length = 4; + fig0_9->CN = 0; + fig0_9->OE = 0; + fig0_9->PD = 0; + fig0_9->Extension = 9; + + fig0_9->ext = 0; + fig0_9->rfa1 = 0; // Had a different meaning in EN 300 401 V1.4.1 + + if (ensemble->lto_auto) { + time_t now = time(NULL); + struct tm* ltime = localtime(&now); + time_t now2 = timegm(ltime); + ensemble->lto = (now2 - now) / 1800; + } + + if (ensemble->lto >= 0) { + fig0_9->ensembleLto = ensemble->lto; + } + else { + /* Convert to 1-complement representation */ + fig0_9->ensembleLto = (-ensemble->lto) | (1<<5); + } + + fig0_9->ensembleEcc = ensemble->ecc; + fig0_9->tableId = ensemble->international_table; + buf += 5; + remaining -= 5; + + /* No extended field, no support for services with different ECC */ + + fs.num_bytes_written = max_size - remaining; + fs.complete_fig_transmitted = true; + return fs; +} + +} diff --git a/src/fig/FIG0_9.h b/src/fig/FIG0_9.h new file mode 100644 index 0000000..57b2f7b --- /dev/null +++ b/src/fig/FIG0_9.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include + +#include "fig/FIG0structs.h" + +namespace FIC { + +// FIG type 0/9 +// The Country, LTO and International table feature defines the local time +// offset, the International Table and the Extended Country Code (ECC) +class FIG0_9 : public IFIG +{ + public: + FIG0_9(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate(void) { return FIG_rate::B; } + + virtual const int figtype(void) const { return 0; } + virtual const int figextension(void) const { return 9; } + + private: + FIGRuntimeInformation *m_rti; +}; + +} diff --git a/src/fig/TransitionHandler.h b/src/fig/TransitionHandler.h new file mode 100644 index 0000000..59bbe3c --- /dev/null +++ b/src/fig/TransitionHandler.h @@ -0,0 +1,119 @@ +/* + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Her Majesty the Queen in Right of Canada (Communications + Research Center Canada) + + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see . +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace FIC { + +// Some FIGs need to adapt their rate or their contents depending +// on if some data entries are stable or currently undergoing a +// change. The TransitionHandler keeps track of which entries +// are in what state. +template +class TransitionHandler { + public: + // update_state must be called once per ETI frame, and will + // move entries from new to repeated to disabled depending + // on their is_active() return value. + void update_state(int timeout, std::vector > all_entries) + { + for (const auto& entry : all_entries) { + if (entry->is_active()) { + if (repeated_entries.count(entry) > 0) { + // We are currently announcing this entry + continue; + } + + if (new_entries.count(entry) > 0) { + // We are currently announcing this entry at a + // fast rate. Handle timeout: + new_entries[entry] -= 1; + if (new_entries[entry] <= 0) { + repeated_entries.insert(entry); + new_entries.erase(entry); + } + continue; + } + + // unlikely + if (disabled_entries.count(entry) > 0) { + new_entries[entry] = timeout; + disabled_entries.erase(entry); + continue; + } + + // It's a new entry! + new_entries[entry] = timeout; + } + else { // Not active + if (disabled_entries.count(entry) > 0) { + disabled_entries[entry] -= 1; + if (disabled_entries[entry] <= 0) { + disabled_entries.erase(entry); + } + continue; + } + + if (repeated_entries.count(entry) > 0) { + // We are currently announcing this entry + disabled_entries[entry] = timeout; + repeated_entries.erase(entry); + continue; + } + + // unlikely + if (new_entries.count(entry) > 0) { + // We are currently announcing this entry at a + // fast rate. We must stop announcing it + disabled_entries[entry] = timeout; + new_entries.erase(entry); + continue; + } + } + } + } + // The FIG that needs the information about what state an entry is in + // can read from the following data structures. It shall not modify them. + + /* Map to frame count */ + std::map< + std::shared_ptr,int> new_entries; + + std::set< + std::shared_ptr > repeated_entries; + + /* Map to frame count */ + std::map< + std::shared_ptr,int> disabled_entries; +}; + +} // namespace FIC + -- cgit v1.2.3