diff options
| author | Samuel Hunt <sam@maxxwave.co.uk> | 2025-12-30 20:39:49 +0000 |
|---|---|---|
| committer | Samuel Hunt <sam@maxxwave.co.uk> | 2025-12-30 20:39:49 +0000 |
| commit | f8b5402727b7e94aecbfb663a601577f97bae5b9 (patch) | |
| tree | 81afd3bad63d4aef0750ed3fb98bf6555d3e7918 /src | |
| parent | cf1ce1f08c0e49a0c6c818135971b201026258c2 (diff) | |
| download | dabmux-f8b5402727b7e94aecbfb663a601577f97bae5b9.tar.gz dabmux-f8b5402727b7e94aecbfb663a601577f97bae5b9.tar.bz2 dabmux-f8b5402727b7e94aecbfb663a601577f97bae5b9.zip | |
Added FIG0/20
Diffstat (limited to 'src')
| -rw-r--r-- | src/ConfigParser.cpp | 100 | ||||
| -rw-r--r-- | src/MuxElements.h | 59 | ||||
| -rw-r--r-- | src/fig/FIG0.h | 1 | ||||
| -rw-r--r-- | src/fig/FIG0_20.cpp | 221 | ||||
| -rw-r--r-- | src/fig/FIG0_20.h | 52 | ||||
| -rw-r--r-- | src/fig/FIGCarousel.cpp | 2 | ||||
| -rw-r--r-- | src/fig/FIGCarousel.h | 1 |
7 files changed, 435 insertions, 1 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index a845bef..d937166 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -373,6 +373,105 @@ static void parse_other_service_linking(ptree& pt, } // if other-services present } +// Parse the service-component-information section (FIG 0/20) +static void parse_service_component_information(ptree& pt, + std::shared_ptr<dabEnsemble> ensemble) +{ + auto pt_sci = pt.get_child_optional("service-component-information"); + if (pt_sci) + { + for (const auto& it_sci : *pt_sci) { + const string sci_uid = it_sci.first; + const ptree pt_entry = it_sci.second; + + auto sci = make_shared<ServiceComponentInformation>(); + + try { + // Required: Service ID + sci->SId = hexparse(pt_entry.get<string>("id")); + + // Required: change type + string change_str = pt_entry.get<string>("change"); + if (change_str == "identity") { + sci->change_flags = SCIChangeFlags::IdentityChange; + } + else if (change_str == "addition") { + sci->change_flags = SCIChangeFlags::Addition; + } + else if (change_str == "local_removal") { + sci->change_flags = SCIChangeFlags::LocalRemoval; + } + else if (change_str == "global_removal") { + sci->change_flags = SCIChangeFlags::GlobalRemoval; + } + else { + throw runtime_error("Invalid change type '" + change_str + + "' for SCI entry " + sci_uid + + " (valid: identity, addition, local_removal, global_removal)"); + } + + // Optional: SCIdS (default 0 = primary component) + sci->SCIdS = pt_entry.get<uint8_t>("scids", 0) & 0x0F; + + // Optional: programme flag (default true for audio services) + sci->isProgramme = pt_entry.get<bool>("programme", true); + + // Optional: part-time flag + sci->part_time = pt_entry.get<bool>("part_time", false); + + // Optional: SC description + auto pt_sc = pt_entry.get_child_optional("sc_description"); + if (pt_sc) { + sci->sc_flag = true; + sci->ca_flag = pt_sc->get<bool>("ca", false); + sci->ad_flag = pt_sc->get<bool>("data", false); // A/D flag: 0=audio, 1=data + sci->SCTy = pt_sc->get<uint8_t>("scty", 0) & 0x3F; + } + + // Optional: date-time of change (default: special value = already occurred) + auto pt_datetime = pt_entry.get_child_optional("datetime"); + if (pt_datetime) { + sci->date = pt_datetime->get<uint8_t>("date", 0x1F) & 0x1F; + sci->hour = pt_datetime->get<uint8_t>("hour", 0x1F) & 0x1F; + sci->minute = pt_datetime->get<uint8_t>("minute", 0x3F) & 0x3F; + sci->second = pt_datetime->get<uint8_t>("second", 0x3F) & 0x3F; + } + // else: default values already set to special value (0x1FFFFFF) + + // Optional: transfer SId (for identity changes or service moves) + string transfer_sid_str = pt_entry.get<string>("transfer_sid", ""); + if (not transfer_sid_str.empty()) { + sci->sid_flag = true; + sci->transfer_sid = hexparse(transfer_sid_str); + } + + // Optional: transfer EId (for service moves to another ensemble) + string transfer_eid_str = pt_entry.get<string>("transfer_eid", ""); + if (not transfer_eid_str.empty()) { + sci->eid_flag = true; + sci->transfer_eid = hexparse(transfer_eid_str); + } + + // Optional: active flag (default false - must be enabled via RC or config) + sci->set_active(pt_entry.get<bool>("active", false)); + + ensemble->sci_entries.push_back(sci); + + etiLog.level(info) << "SCI entry " << sci_uid << + " configured for SId 0x" << hex << sci->SId << dec; + } + catch (const ptree_error &e) { + throw runtime_error("Invalid configuration for SCI entry " + + sci_uid + ": " + e.what()); + } + catch (const std::exception &e) { + throw runtime_error("Error parsing SCI entry " + + sci_uid + ": " + e.what()); + } + } // for over sci entries + } // if service-component-information present +} + static void parse_general(ptree& pt, std::shared_ptr<dabEnsemble> ensemble) { @@ -906,6 +1005,7 @@ void parse_ptree( parse_linkage(pt, ensemble); parse_freq_info(pt, ensemble); parse_other_service_linking(pt, ensemble); + parse_service_component_information(pt, ensemble); } static Inputs::dab_input_zmq_config_t setup_zmq_input( diff --git a/src/MuxElements.h b/src/MuxElements.h index 1e9b707..bbd87ec 100644 --- a/src/MuxElements.h +++ b/src/MuxElements.h @@ -287,6 +287,63 @@ using vec_sp_service = std::vector<std::shared_ptr<DabService> >; using vec_sp_subchannel = std::vector<std::shared_ptr<DabSubchannel> >; +/* FIG 0/20 Service Component Information (SCI) + * Change flags values - see ETSI TS 103 176 clause 6 + */ +enum class SCIChangeFlags : uint8_t { + IdentityChange = 0, // 00: Service changing identity or moving ensemble + Addition = 1, // 01: Service being added + LocalRemoval = 2, // 10: Service removed from this ensemble only + GlobalRemoval = 3 // 11: Service removed from all ensembles +}; + +/* Service Component Information entry for FIG 0/20 + * See ETSI TS 103 176 clause 6 + */ +struct ServiceComponentInformation { + uint32_t SId = 0; // Service Identifier (16-bit for audio, 32-bit for data) + uint8_t SCIdS = 0; // Service Component Identifier within Service (4-bit) + SCIChangeFlags change_flags = SCIChangeFlags::Addition; + bool part_time = false; // P-T flag: true if part-time service + bool sc_flag = false; // SC flag: true if SC description present + + // SC description (only present if sc_flag is true) + bool ca_flag = false; // CA flag: Access control applied + bool ad_flag = false; // A/D flag: 0=ASCTy follows, 1=DSCTy follows + uint8_t SCTy = 0; // Service Component Type (6-bit) + + // Date-time of change (special value 0x1FFFFFF means "already occurred" or "unknown") + uint8_t date = 0x1F; // 5-bit, 0x1F = special value + uint8_t hour = 0x1F; // 5-bit, 0x1F = special value + uint8_t minute = 0x3F; // 6-bit, 0x3F = special value + uint8_t second = 0x3F; // 6-bit, 0x3F = special value + + // Transfer information (for service moves/identity changes) + bool sid_flag = false; // SId flag: Transfer SId present + bool eid_flag = false; // EId flag: Transfer EId present + uint32_t transfer_sid = 0; // Transfer Service Identifier + uint16_t transfer_eid = 0; // Transfer Ensemble Identifier + + bool isDateTimeSpecial() const { + return (date == 0x1F && hour == 0x1F && minute == 0x3F && second == 0x3F); + } + + void setDateTimeSpecial() { + date = 0x1F; hour = 0x1F; minute = 0x3F; second = 0x3F; + } + + bool isProgramme = true; + + bool is_active() const { return m_active; } + void set_active(bool a) { m_active = a; } + +private: + bool m_active = false; +}; + +using vec_sp_sci = std::vector<std::shared_ptr<ServiceComponentInformation> >; + + enum class TransmissionMode_e { TM_I, TM_II, @@ -349,6 +406,7 @@ class dabEnsemble : public RemoteControllable { std::vector<std::shared_ptr<LinkageSet> > linkagesets; std::vector<FrequencyInformation> frequency_information; std::vector<ServiceOtherEnsembleInfo> service_other_ensemble; + vec_sp_sci sci_entries; }; @@ -667,4 +725,3 @@ vec_sp_component::iterator getComponent( vec_sp_service::iterator getService( std::shared_ptr<DabComponent> component, vec_sp_service& services); - diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h index a779fa0..26d55ab 100644 --- a/src/fig/FIG0.h +++ b/src/fig/FIG0.h @@ -41,6 +41,7 @@ #include "fig/FIG0_17.h" #include "fig/FIG0_18.h" #include "fig/FIG0_19.h" +#include "fig/FIG0_20.h" #include "fig/FIG0_21.h" #include "fig/FIG0_24.h" diff --git a/src/fig/FIG0_20.cpp b/src/fig/FIG0_20.cpp new file mode 100644 index 0000000..86ac857 --- /dev/null +++ b/src/fig/FIG0_20.cpp @@ -0,0 +1,221 @@ +/* + 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) 2024 + 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/FIG0structs.h" +#include "fig/FIG0_20.h" +#include "utils.h" + +namespace FIC { + +// FIG 0/20 header structure for programme services (16-bit SId) +struct FIGtype0_20_programme_header { + uint8_t SId_high; + uint8_t SId_low; + uint8_t sc_flag:1; + uint8_t pt_flag:1; + uint8_t change_flags:2; + uint8_t SCIdS:4; +} PACKED; + +// FIG 0/20 header structure for data services (32-bit SId) +struct FIGtype0_20_data_header { + uint8_t SId_byte0; + uint8_t SId_byte1; + uint8_t SId_byte2; + uint8_t SId_byte3; + uint8_t sc_flag:1; + uint8_t pt_flag:1; + uint8_t change_flags:2; + uint8_t SCIdS:4; +} PACKED; + +// SC description structure (1 byte, only present if SC flag = 1) +struct FIGtype0_20_sc_desc { + uint8_t SCTy:6; + uint8_t ad_flag:1; + uint8_t ca_flag:1; +} PACKED; + +FIG0_20::FIG0_20(FIGRuntimeInformation *rti) : + m_rti(rti), + m_initialised(false) +{ } + +FillStatus FIG0_20::fill(uint8_t *buf, size_t max_size) +{ + FillStatus fs; + ssize_t remaining = max_size; + + auto ensemble = m_rti->ensemble; + + if (ensemble->sci_entries.empty()) { + fs.complete_fig_transmitted = true; + fs.num_bytes_written = 0; + return fs; + } + + if (not m_initialised) { + sci_it = ensemble->sci_entries.end(); + m_initialised = true; + } + + FIGtype0* fig0 = NULL; + + for (; sci_it != ensemble->sci_entries.end(); ++sci_it) { + auto& sci = *sci_it; + + if (!sci->is_active()) { + continue; + } + + // Calculate required size for this SCI entry + size_t required_size = 0; + + if (sci->isProgramme) { + required_size += 3; // 2 bytes SId + 1 byte flags + } else { + required_size += 5; // 4 bytes SId + 1 byte flags + } + + if (sci->sc_flag) { + required_size += 1; + } + + // Date-time field: 3 bytes + required_size += 3; + + // Transfer SId + if (sci->sid_flag) { + required_size += sci->isProgramme ? 2 : 4; + } + + // Transfer EId + if (sci->eid_flag) { + required_size += 2; + } + + if (fig0 == NULL) { + if (remaining < 2 + (ssize_t)required_size) { + break; + } + + fig0 = (FIGtype0*)buf; + fig0->FIGtypeNumber = 0; + fig0->Length = 1; + fig0->CN = 0; + fig0->OE = 0; + fig0->PD = sci->isProgramme ? 0 : 1; + fig0->Extension = 20; + + buf += 2; + remaining -= 2; + } + else if (remaining < (ssize_t)required_size) { + break; + } + + uint8_t* entry_start = buf; + + if (sci->isProgramme) { + auto header = (FIGtype0_20_programme_header*)buf; + header->SId_high = (sci->SId >> 8) & 0xFF; + header->SId_low = sci->SId & 0xFF; + header->SCIdS = sci->SCIdS & 0x0F; + header->change_flags = static_cast<uint8_t>(sci->change_flags) & 0x03; + header->pt_flag = sci->part_time ? 1 : 0; + header->sc_flag = sci->sc_flag ? 1 : 0; + buf += 3; + } + else { + auto header = (FIGtype0_20_data_header*)buf; + header->SId_byte0 = (sci->SId >> 24) & 0xFF; + header->SId_byte1 = (sci->SId >> 16) & 0xFF; + header->SId_byte2 = (sci->SId >> 8) & 0xFF; + header->SId_byte3 = sci->SId & 0xFF; + header->SCIdS = sci->SCIdS & 0x0F; + header->change_flags = static_cast<uint8_t>(sci->change_flags) & 0x03; + header->pt_flag = sci->part_time ? 1 : 0; + header->sc_flag = sci->sc_flag ? 1 : 0; + buf += 5; + } + + if (sci->sc_flag) { + auto sc_desc = (FIGtype0_20_sc_desc*)buf; + sc_desc->ca_flag = sci->ca_flag ? 1 : 0; + sc_desc->ad_flag = sci->ad_flag ? 1 : 0; + sc_desc->SCTy = sci->SCTy & 0x3F; + buf += 1; + } + + // Date-time field (3 bytes) + // Byte 0: date[4:0] + hour[4:2] + // Byte 1: hour[1:0] + minute[5:0] + // Byte 2: second[5:0] + sid_flag + eid_flag + uint8_t date_val = sci->date & 0x1F; + uint8_t hour_val = sci->hour & 0x1F; + uint8_t minute_val = sci->minute & 0x3F; + uint8_t second_val = sci->second & 0x3F; + + buf[0] = (date_val << 3) | (hour_val >> 2); + buf[1] = ((hour_val & 0x03) << 6) | minute_val; + buf[2] = (second_val << 2) | ((sci->sid_flag ? 1 : 0) << 1) | (sci->eid_flag ? 1 : 0); + buf += 3; + + if (sci->sid_flag) { + if (sci->isProgramme) { + buf[0] = (sci->transfer_sid >> 8) & 0xFF; + buf[1] = sci->transfer_sid & 0xFF; + buf += 2; + } + else { + buf[0] = (sci->transfer_sid >> 24) & 0xFF; + buf[1] = (sci->transfer_sid >> 16) & 0xFF; + buf[2] = (sci->transfer_sid >> 8) & 0xFF; + buf[3] = sci->transfer_sid & 0xFF; + buf += 4; + } + } + + if (sci->eid_flag) { + buf[0] = (sci->transfer_eid >> 8) & 0xFF; + buf[1] = sci->transfer_eid & 0xFF; + buf += 2; + } + + size_t entry_size = buf - entry_start; + fig0->Length += entry_size; + remaining -= entry_size; + } + + if (sci_it == ensemble->sci_entries.end()) { + sci_it = ensemble->sci_entries.begin(); + fs.complete_fig_transmitted = true; + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +} diff --git a/src/fig/FIG0_20.h b/src/fig/FIG0_20.h new file mode 100644 index 0000000..2ad6c94 --- /dev/null +++ b/src/fig/FIG0_20.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) 2024 + 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> + +namespace FIC { + +// FIG type 0/20 +// Service Component Information (SCI) +// See ETSI TS 103 176 clause 6 +class FIG0_20 : public IFIG +{ + public: + FIG0_20(FIGRuntimeInformation* rti); + virtual FillStatus fill(uint8_t *buf, size_t max_size); + virtual FIG_rate repetition_rate() const { return FIG_rate::B; } + + virtual int figtype() const { return 0; } + virtual int figextension() const { return 20; } + + private: + FIGRuntimeInformation *m_rti; + bool m_initialised; + vec_sp_sci::iterator sci_it; +}; + +} diff --git a/src/fig/FIGCarousel.cpp b/src/fig/FIGCarousel.cpp index 4c5c04c..3d627c9 100644 --- a/src/fig/FIGCarousel.cpp +++ b/src/fig/FIGCarousel.cpp @@ -89,6 +89,7 @@ FIGCarousel::FIGCarousel(std::shared_ptr<dabEnsemble> ensemble) : m_fig1_5(&m_rti), m_fig0_18(&m_rti), m_fig0_19(&m_rti), + m_fig0_20(&m_rti), m_fig0_21(&m_rti), m_fig0_24(&m_rti), m_fig2_0(&m_rti), @@ -130,6 +131,7 @@ FIGCarousel::FIGCarousel(std::shared_ptr<dabEnsemble> ensemble) : load_and_allocate(m_fig1_5, FIBAllocation::FIB_ANY); load_and_allocate(m_fig0_18, FIBAllocation::FIB_ANY); load_and_allocate(m_fig0_19, FIBAllocation::FIB_ANY); + load_and_allocate(m_fig0_20, FIBAllocation::FIB_ANY); load_and_allocate(m_fig0_21, FIBAllocation::FIB_ANY); load_and_allocate(m_fig0_24, FIBAllocation::FIB_ANY); diff --git a/src/fig/FIGCarousel.h b/src/fig/FIGCarousel.h index 00b6d18..b56bec4 100644 --- a/src/fig/FIGCarousel.h +++ b/src/fig/FIGCarousel.h @@ -114,6 +114,7 @@ class FIGCarousel { FIG1_5 m_fig1_5; FIG0_18 m_fig0_18; FIG0_19 m_fig0_19; + FIG0_20 m_fig0_20; FIG0_21 m_fig0_21; FIG0_24 m_fig0_24; FIG2_0 m_fig2_0; |
