summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am16
-rw-r--r--src/fig/FIG0.cpp1617
-rw-r--r--src/fig/FIG0.h319
-rw-r--r--src/fig/FIG0_0.cpp64
-rw-r--r--src/fig/FIG0_0.h51
-rw-r--r--src/fig/FIG0_1.cpp209
-rw-r--r--src/fig/FIG0_1.h58
-rw-r--r--src/fig/FIG0_10.cpp84
-rw-r--r--src/fig/FIG0_10.h52
-rw-r--r--src/fig/FIG0_13.cpp182
-rw-r--r--src/fig/FIG0_13.h54
-rw-r--r--src/fig/FIG0_17.cpp103
-rw-r--r--src/fig/FIG0_17.h53
-rw-r--r--src/fig/FIG0_18.cpp106
-rw-r--r--src/fig/FIG0_18.h52
-rw-r--r--src/fig/FIG0_19.cpp136
-rw-r--r--src/fig/FIG0_19.h53
-rw-r--r--src/fig/FIG0_2.cpp266
-rw-r--r--src/fig/FIG0_2.h55
-rw-r--r--src/fig/FIG0_3.cpp124
-rw-r--r--src/fig/FIG0_3.h54
-rw-r--r--src/fig/FIG0_5.cpp113
-rw-r--r--src/fig/FIG0_5.h57
-rw-r--r--src/fig/FIG0_6.cpp204
-rw-r--r--src/fig/FIG0_6.h85
-rw-r--r--src/fig/FIG0_8.cpp204
-rw-r--r--src/fig/FIG0_8.h56
-rw-r--r--src/fig/FIG0_9.cpp83
-rw-r--r--src/fig/FIG0_9.h52
-rw-r--r--src/fig/TransitionHandler.h119
30 files changed, 2758 insertions, 1923 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 55510ee..5edd0a4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -97,9 +97,23 @@ 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_6.cpp fig/FIG0_6.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 1e179cf..0000000
--- a/src/fig/FIG0.cpp
+++ /dev/null
@@ -1,1617 +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 <http://www.gnu.org/licenses/>.
-*/
-
-#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<DabService>& s) {
- return s->isProgramme(ensemble);
- } );
-
- m_data_services.clear();
- copy_if(ensemble->services.begin(),
- ensemble->services.end(),
- back_inserter(m_data_services),
- [&](shared_ptr<DabService>& 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/6 ===========
-
-FIG0_6::FIG0_6(FIGRuntimeInformation *rti) :
- m_rti(rti),
- m_initialised(false)
-{
-}
-
-FillStatus FIG0_6::fill(uint8_t *buf, size_t max_size)
-{
- FillStatus fs;
- ssize_t remaining = max_size;
- auto ensemble = m_rti->ensemble;
-
- if (not m_initialised) {
- linkageSetFIG0_6 = m_rti->ensemble->linkagesets.end();
- m_initialised = true;
- }
-
- FIGtype0* fig0 = NULL;
-
- for (; linkageSetFIG0_6 != ensemble->linkagesets.end();
- ++linkageSetFIG0_6) {
-
- const bool PD = false;
- const bool ILS = (*linkageSetFIG0_6)->international;
-
- // need to add key service to num_ids
- const size_t num_ids = 1 + (*linkageSetFIG0_6)->id_list.size();
-
- const size_t headersize = sizeof(struct FIGtype0_6_header);
- const int required_size = sizeof(struct FIGtype0_6) +
- (num_ids > 0 ?
- headersize + (PD == 0 ? (ILS == 0 ? 2*num_ids : 3*num_ids) : 4*num_ids) :
- 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 = PD;
- fig0->Extension = 5;
-
- buf += 2;
- remaining -= 2;
- }
- else if (remaining < required_size) {
- break;
- }
-
- FIGtype0_6 *fig0_6 = (FIGtype0_6*)buf;
-
- fig0_6->IdListFlag = (num_ids > 0);
- fig0_6->LA = (*linkageSetFIG0_6)->active;
- fig0_6->SH = (*linkageSetFIG0_6)->hard;
- fig0_6->ILS = ILS;
- fig0_6->LSN = (*linkageSetFIG0_6)->lsn;
-
- fig0->Length += sizeof(struct FIGtype0_6);
- buf += sizeof(struct FIGtype0_6);
- remaining -= sizeof(struct FIGtype0_6);
-
- if (num_ids > 0) {
- FIGtype0_6_header *header = (FIGtype0_6_header*)buf;
- header->rfu = 0;
- if (num_ids > 0x0F) {
- etiLog.log(error, "Too large number of links for linkage set 0x%04x",
- (*linkageSetFIG0_6)->lsn);
- throw MuxInitException();
- }
-
- header->IdLQ = 0; // TODO not only DAB
- header->rfa = 0;
- header->num_ids = num_ids;
-
- fig0->Length += headersize;
- buf += headersize;
- remaining -= headersize;
-
- // TODO insert key service first
- const std::string keyserviceuid =(*linkageSetFIG0_6)->keyservice;
- const auto& keyservice = std::find_if(
- ensemble->services.begin(),
- ensemble->services.end(),
- [&](const std::shared_ptr<DabService> srv) {
- return srv->uid == keyserviceuid;
- });
-
- if (keyservice == ensemble->services.end()) {
- etiLog.log(error, "Invalid key service %s in linkage set 0x%04x",
- keyserviceuid.c_str(), (*linkageSetFIG0_6)->lsn);
- throw MuxInitException();
- }
- for (const auto& l : (*linkageSetFIG0_6)->id_list) {
- if (l.type != ServiceLinkType::DAB) {
- etiLog.log(error, "TODO only DAB links supported. (linkage set 0x%04x)",
- (*linkageSetFIG0_6)->lsn);
- throw MuxInitException();
- }
- }
-
- if (not PD and not ILS) {
- buf[0] = (*keyservice)->id >> 8;
- buf[1] = (*keyservice)->id & 0xFF;
- fig0->Length += 2;
- buf += 2;
- remaining -= 2;
-
- for (const auto& l : (*linkageSetFIG0_6)->id_list) {
- buf[0] = l.id >> 8;
- buf[1] = l.id & 0xFF;
- fig0->Length += 2;
- buf += 2;
- remaining -= 2;
- }
- }
- if (not PD and ILS) {
- buf[0] = ensemble->ecc;
- buf[1] = (*keyservice)->id >> 8;
- buf[2] = (*keyservice)->id & 0xFF;
- fig0->Length += 3;
- buf += 3;
- remaining -= 3;
-
- for (const auto& l : (*linkageSetFIG0_6)->id_list) {
- buf[0] = l.ecc;
- buf[1] = l.id >> 8;
- buf[2] = l.id & 0xFF;
- fig0->Length += 3;
- buf += 3;
- remaining -= 3;
- }
- }
- else { // PD == true
- // TODO if IdLQ is 11, MSB shall be zero
- buf[0] = (*keyservice)->id >> 24;
- buf[1] = (*keyservice)->id >> 16;
- buf[2] = (*keyservice)->id >> 8;
- buf[3] = (*keyservice)->id & 0xFF;
- fig0->Length += 4;
- buf += 4;
- remaining -= 4;
-
- for (const auto& l : (*linkageSetFIG0_6)->id_list) {
- buf[0] = l.id >> 24;
- buf[1] = l.id >> 16;
- buf[2] = l.id >> 8;
- buf[3] = l.id & 0xFF;
- fig0->Length += 4;
- buf += 4;
- remaining -= 4;
- }
- }
- }
-
- fig0->Length += required_size;
- buf += required_size;
- remaining -= required_size;
- }
-
- if (linkageSetFIG0_6 == ensemble->linkagesets.end()) {
- linkageSetFIG0_6 = ensemble->linkagesets.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;
-
- update_state();
-
- FillStatus fs;
- ssize_t remaining = max_size;
-
- auto ensemble = m_rti->ensemble;
-
- FIGtype0* fig0 = NULL;
-
- // Combine all clusters into one list
- set<AnnouncementCluster*> allclusters;
- for (const auto& cluster : m_new_announcements) {
- allclusters.insert(cluster.first.get());
- }
- for (const auto& cluster : m_repeated_announcements) {
- allclusters.insert(cluster.get());
- }
- for (const auto& cluster : m_disabled_announcements) {
- 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;
-}
-
-void FIG0_19::update_state()
-{
- 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
- continue;
- }
-
- if (m_new_announcements.count(cluster) > 0) {
- // We are currently announcing this cluster 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);
- }
- continue;
- }
-
- // unlikely
- if (m_disabled_announcements.count(cluster) > 0) {
- m_new_announcements[cluster] = timeout;
- m_disabled_announcements.erase(cluster);
- continue;
- }
-
- // It's a new announcement!
- m_new_announcements[cluster] = 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);
- }
- continue;
- }
-
- if (m_repeated_announcements.count(cluster) > 0) {
- // We are currently announcing this cluster
- m_disabled_announcements[cluster] = timeout;
- m_repeated_announcements.erase(cluster);
- continue;
- }
-
- // unlikely
- if (m_new_announcements.count(cluster) > 0) {
- // We are currently announcing this cluster at a
- // fast rate. We must stop announcing it
- m_disabled_announcements[cluster] = timeout;
- m_new_announcements.erase(cluster);
- continue;
- }
- }
- }
-}
-
-FIG_rate FIG0_19::repetition_rate(void)
-{
- if ( m_new_announcements.size() > 0 or
- m_disabled_announcements.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 0109219..446e1ac 100644
--- a/src/fig/FIG0.h
+++ b/src/fig/FIG0.h
@@ -25,309 +25,18 @@
#pragma once
-#include <cstdint>
-#include <map>
-#include <set>
-#include <vector>
-#include <list>
-
-#include "fig/FIG.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;
-};
-
-// 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<DabSubchannel*> subchannels;
- std::vector<DabSubchannel*>::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<std::shared_ptr<DabService> > m_audio_services;
- std::vector<std::shared_ptr<DabService> > m_data_services;
- std::vector<std::shared_ptr<DabService> >::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<DabComponent*>::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<DabComponent*>::iterator componentFIG0_5;
-};
-
-// FIG type 0/6
-// Service Linking
-//
-// This feature shall use the SIV signalling (see clause 5.2.2.1). The database
-// shall be divided by use of a database key. Changes to the database shall be
-// signalled using the CEI. The first service in the list of services in each
-// part of the database, as divided by the database key, shall be a service
-// carried in the ensemble. This service is called the key service.
-//
-// The database key comprises the OE and P/D flags and the S/H, ILS, and LSN
-// fields.
-class FIG0_6 : public IFIG
-{
- public:
- FIG0_6(FIGRuntimeInformation* rti);
- virtual FillStatus fill(uint8_t *buf, size_t max_size);
- virtual FIG_rate repetition_rate(void) { return FIG_rate::E; }
-
- virtual const int figtype(void) const { return 0; }
- virtual const int figextension(void) const { return 6; }
-
- private:
- FIGRuntimeInformation *m_rti;
- bool m_initialised;
- std::list<std::shared_ptr<LinkageSet> >::iterator linkageSetFIG0_6;
-};
-
-// FIG0/6 needs a change indicator, which is a short-form FIG (i.e. without the list)
-// and with C/N 1. Since this has another rate, it's implemented in another class.
-//
-// This is signalled once per second for a period of five seconds
-// (TS 103 176 5.2.4.3).
-class FIG0_6_CEI : public IFIG
-{
- public:
- FIG0_6_CEI(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 6; }
-
- private:
- FIGRuntimeInformation *m_rti;
- bool m_initialised;
- std::list<std::shared_ptr<LinkageSet> >::iterator linkageSetFIG0_6;
-};
-
-// 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<DabComponent*>::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<DabComponent*>::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<std::shared_ptr<DabService> >::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<std::shared_ptr<DabService> >::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;
-
- 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<AnnouncementCluster>,int> m_new_announcements;
-
- std::set<
- std::shared_ptr<AnnouncementCluster> > m_repeated_announcements;
-
- /* Map of cluster to frame count */
- std::map<
- std::shared_ptr<AnnouncementCluster>,int> m_disabled_announcements;
-};
-
-} // 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_6.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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<DabSubchannel*> subchannels;
+ std::vector<DabSubchannel*>::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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<DabComponent*>::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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<std::shared_ptr<DabService> >::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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<std::shared_ptr<DabService> >::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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<AnnouncementCluster*> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<AnnouncementCluster> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<DabService>& s) {
+ return s->isProgramme(ensemble);
+ } );
+
+ m_data_services.clear();
+ copy_if(ensemble->services.begin(),
+ ensemble->services.end(),
+ back_inserter(m_data_services),
+ [&](shared_ptr<DabService>& 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<std::shared_ptr<DabService> > m_audio_services;
+ std::vector<std::shared_ptr<DabService> > m_data_services;
+ std::vector<std::shared_ptr<DabService> >::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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<DabComponent*>::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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<DabComponent*>::iterator componentFIG0_5;
+};
+
+}
diff --git a/src/fig/FIG0_6.cpp b/src/fig/FIG0_6.cpp
new file mode 100644
index 0000000..f404a47
--- /dev/null
+++ b/src/fig/FIG0_6.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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "fig/FIG0_6.h"
+#include "utils.h"
+
+namespace FIC {
+
+FIG0_6::FIG0_6(FIGRuntimeInformation *rti) :
+ m_rti(rti),
+ m_initialised(false)
+{
+}
+
+FillStatus FIG0_6::fill(uint8_t *buf, size_t max_size)
+{
+ FillStatus fs;
+ ssize_t remaining = max_size;
+ auto ensemble = m_rti->ensemble;
+
+ if (not m_initialised) {
+ linkageSetFIG0_6 = m_rti->ensemble->linkagesets.end();
+ m_initialised = true;
+ }
+
+ FIGtype0* fig0 = NULL;
+
+ for (; linkageSetFIG0_6 != ensemble->linkagesets.end();
+ ++linkageSetFIG0_6) {
+
+ const bool PD = false;
+ const bool ILS = (*linkageSetFIG0_6)->international;
+
+ // need to add key service to num_ids
+ const size_t num_ids = 1 + (*linkageSetFIG0_6)->id_list.size();
+
+ const size_t headersize = sizeof(struct FIGtype0_6_header);
+ const int required_size = sizeof(struct FIGtype0_6) +
+ (num_ids > 0 ?
+ headersize + (PD == 0 ? (ILS == 0 ? 2*num_ids : 3*num_ids) : 4*num_ids) :
+ 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 = PD;
+ fig0->Extension = 5;
+
+ buf += 2;
+ remaining -= 2;
+ }
+ else if (remaining < required_size) {
+ break;
+ }
+
+ FIGtype0_6 *fig0_6 = (FIGtype0_6*)buf;
+
+ fig0_6->IdListFlag = (num_ids > 0);
+ fig0_6->LA = (*linkageSetFIG0_6)->active;
+ fig0_6->SH = (*linkageSetFIG0_6)->hard;
+ fig0_6->ILS = ILS;
+ fig0_6->LSN = (*linkageSetFIG0_6)->lsn;
+
+ fig0->Length += sizeof(struct FIGtype0_6);
+ buf += sizeof(struct FIGtype0_6);
+ remaining -= sizeof(struct FIGtype0_6);
+
+ if (num_ids > 0) {
+ FIGtype0_6_header *header = (FIGtype0_6_header*)buf;
+ header->rfu = 0;
+ if (num_ids > 0x0F) {
+ etiLog.log(error, "Too large number of links for linkage set 0x%04x",
+ (*linkageSetFIG0_6)->lsn);
+ throw MuxInitException();
+ }
+
+ header->IdLQ = 0; // TODO not only DAB
+ header->rfa = 0;
+ header->num_ids = num_ids;
+
+ fig0->Length += headersize;
+ buf += headersize;
+ remaining -= headersize;
+
+ // TODO insert key service first
+ const std::string keyserviceuid =(*linkageSetFIG0_6)->keyservice;
+ const auto& keyservice = std::find_if(
+ ensemble->services.begin(),
+ ensemble->services.end(),
+ [&](const std::shared_ptr<DabService> srv) {
+ return srv->uid == keyserviceuid;
+ });
+
+ if (keyservice == ensemble->services.end()) {
+ etiLog.log(error, "Invalid key service %s in linkage set 0x%04x",
+ keyserviceuid.c_str(), (*linkageSetFIG0_6)->lsn);
+ throw MuxInitException();
+ }
+ for (const auto& l : (*linkageSetFIG0_6)->id_list) {
+ if (l.type != ServiceLinkType::DAB) {
+ etiLog.log(error, "TODO only DAB links supported. (linkage set 0x%04x)",
+ (*linkageSetFIG0_6)->lsn);
+ throw MuxInitException();
+ }
+ }
+
+ if (not PD and not ILS) {
+ buf[0] = (*keyservice)->id >> 8;
+ buf[1] = (*keyservice)->id & 0xFF;
+ fig0->Length += 2;
+ buf += 2;
+ remaining -= 2;
+
+ for (const auto& l : (*linkageSetFIG0_6)->id_list) {
+ buf[0] = l.id >> 8;
+ buf[1] = l.id & 0xFF;
+ fig0->Length += 2;
+ buf += 2;
+ remaining -= 2;
+ }
+ }
+ if (not PD and ILS) {
+ buf[0] = ensemble->ecc;
+ buf[1] = (*keyservice)->id >> 8;
+ buf[2] = (*keyservice)->id & 0xFF;
+ fig0->Length += 3;
+ buf += 3;
+ remaining -= 3;
+
+ for (const auto& l : (*linkageSetFIG0_6)->id_list) {
+ buf[0] = l.ecc;
+ buf[1] = l.id >> 8;
+ buf[2] = l.id & 0xFF;
+ fig0->Length += 3;
+ buf += 3;
+ remaining -= 3;
+ }
+ }
+ else { // PD == true
+ // TODO if IdLQ is 11, MSB shall be zero
+ buf[0] = (*keyservice)->id >> 24;
+ buf[1] = (*keyservice)->id >> 16;
+ buf[2] = (*keyservice)->id >> 8;
+ buf[3] = (*keyservice)->id & 0xFF;
+ fig0->Length += 4;
+ buf += 4;
+ remaining -= 4;
+
+ for (const auto& l : (*linkageSetFIG0_6)->id_list) {
+ buf[0] = l.id >> 24;
+ buf[1] = l.id >> 16;
+ buf[2] = l.id >> 8;
+ buf[3] = l.id & 0xFF;
+ fig0->Length += 4;
+ buf += 4;
+ remaining -= 4;
+ }
+ }
+ }
+
+ fig0->Length += required_size;
+ buf += required_size;
+ remaining -= required_size;
+ }
+
+ if (linkageSetFIG0_6 == ensemble->linkagesets.end()) {
+ linkageSetFIG0_6 = ensemble->linkagesets.begin();
+ fs.complete_fig_transmitted = true;
+ }
+
+ fs.num_bytes_written = max_size - remaining;
+ return fs;
+}
+
+}
+
diff --git a/src/fig/FIG0_6.h b/src/fig/FIG0_6.h
new file mode 100644
index 0000000..db55c06
--- /dev/null
+++ b/src/fig/FIG0_6.h
@@ -0,0 +1,85 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <list>
+#include <memory>
+
+#include "fig/FIG0structs.h"
+#include "fig/TransitionHandler.h"
+
+namespace FIC {
+
+// FIG type 0/6
+// Service Linking
+//
+// This feature shall use the SIV signalling (see clause 5.2.2.1). The database
+// shall be divided by use of a database key. Changes to the database shall be
+// signalled using the CEI. The first service in the list of services in each
+// part of the database, as divided by the database key, shall be a service
+// carried in the ensemble. This service is called the key service.
+//
+// The database key comprises the OE and P/D flags and the S/H, ILS, and LSN
+// fields.
+class FIG0_6 : public IFIG
+{
+ public:
+ FIG0_6(FIGRuntimeInformation* rti);
+ virtual FillStatus fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::E; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 6; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ std::list<std::shared_ptr<LinkageSet> >::iterator linkageSetFIG0_6;
+};
+
+// FIG0/6 needs a change indicator, which is a short-form FIG (i.e. without the list)
+// and with C/N 1. Since this has another rate, it's implemented in another class.
+//
+// This is signalled once per second for a period of five seconds
+// (TS 103 176 5.2.4.3).
+class FIG0_6_CEI : public IFIG
+{
+ public:
+ FIG0_6_CEI(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 6; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ std::list<std::shared_ptr<LinkageSet> >::iterator linkageSetFIG0_6;
+};
+
+}
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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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<DabComponent*>::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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <map>
+#include <set>
+#include <vector>
+#include <memory>
+
+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 T>
+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<std::shared_ptr<T> > 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<T>,int> new_entries;
+
+ std::set<
+ std::shared_ptr<T> > repeated_entries;
+
+ /* Map to frame count */
+ std::map<
+ std::shared_ptr<T>,int> disabled_entries;
+};
+
+} // namespace FIC
+