diff options
-rw-r--r-- | doc/servicelinking.mux | 166 | ||||
-rw-r--r-- | src/ConfigParser.cpp | 88 | ||||
-rw-r--r-- | src/DabMux.cpp | 2 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/MuxElements.cpp | 56 | ||||
-rw-r--r-- | src/MuxElements.h | 55 | ||||
-rw-r--r-- | src/RemoteControl.h | 3 | ||||
-rw-r--r-- | src/fig/FIG0.h | 1 | ||||
-rw-r--r-- | src/fig/FIG0_6.cpp | 353 | ||||
-rw-r--r-- | src/fig/FIG0_6.h | 98 | ||||
-rw-r--r-- | src/fig/FIG0structs.h | 26 | ||||
-rw-r--r-- | src/fig/FIGCarousel.cpp | 4 | ||||
-rw-r--r-- | src/fig/FIGCarousel.h | 2 | ||||
-rw-r--r-- | src/utils.cpp | 39 |
14 files changed, 891 insertions, 3 deletions
diff --git a/doc/servicelinking.mux b/doc/servicelinking.mux new file mode 100644 index 0000000..38c59ee --- /dev/null +++ b/doc/servicelinking.mux @@ -0,0 +1,166 @@ +; This is an example configuration file that illustrates +; how to define service linking + +; More information about the usage of the tools is available +; in the guide, which can be found on the +; www.opendigitalradio.org website. +; +general { + dabmode 1 + nbframes 10000 + + syslog false + tist false + managementport 12720 +} + +remotecontrol { + telnetport 12721 +} + +; Service linking sets +linking { + ; Every child section declares one linkage sets according to + ; TS 103 176 Clause 5.2.3 "Linkage sets". This information will + ; be encoded in FIG 0/6 + set-fu { + ; Linkage Set Number is a 12-bit number that identifies the linkage set + ; in a country (requires coordination between multiplex operators in a country) + ; (mandatory) + lsn 0xabc + + ; whether this link is active or a "potential future link or de-activated link" + ; This field is also part of the remote control. (default false) + active true + + ; Hard link means that all services carry the same programme, soft links means + ; that the programmes are related in some way. (default true) + hard true + + ; Whether this linkage set affects only one country or several. Linkage sets that + ; include AMSS or DRM services need to have this set to true. (default false) + international false + + ; Every linkage set has to contain a service from the current ensemble (mandatory) + keyservice srv-fu + + ; List of services to be included (mandatory) + list { + ; Every service has a uid that can be used as a human-readable description + + ; The first example is a link to a DAB service on another ensemble. + fu-on-my-friends-mux { + ; Possible options: dab, fm, drm, amss (mandatory) + type dab + + ; if type is dab, the id is a DAB service ID (mandatory) + id 0x8daf + + ; Since this link set has international false, we do not need to specify + ; the ECC. With internation true, the following would be needed + ; (mandatory if internation true) + ;ecc 0xec + } + + ; The second example is a link to an FM transmission + fu-on-fm { + ; Possible options: dab, fm, drm, amss + type fm + + ; if type is fm, the id is a PI-code + id 0x1A2B + + ; Also here, ECC declaration is not required + } + } + } + + ; And now an international true to test more options + set-ri { + lsn 0xdef + active false + hard soft + international true + keyservice srv-ri + + list { + ri-on-drm { + type drm + id 0x1298 + ecc 0xec + } + + ri-on-amss { + type amss + id 0x1A2B + ecc 0xea + } + + ri-on-fm { + type fm + id 0x4C5D + ecc 0x4f + } + } + } +} + +; For information about the ensemble, service, subchannels, components and outputs, +; please see doc/example.mux and doc/advanced.mux +ensemble { + id 0x4fff + ecc 0xec + + local-time-offset auto + label "OpenDigitalRadio" + shortlabel "ODR" +} + +services { + srv-fu { + id 0x8daa + label "Funk" + } + srv-ri { + id 0x8dab + label "Rick" + } +} + +subchannels { + sub-fu { + type dabplus + inputfile "tcp://*:9000" + bitrate 96 + id 1 + protection 3 + zmq-buffer 40 + zmq-prebuffering 20 + } + sub-ri { + type dabplus + inputfile "tcp://*:9001" + bitrate 96 + id 2 + protection 3 + zmq-buffer 40 + zmq-prebuffering 20 + } +} + +components { + comp-fu { + service srv-fu + subchannel sub-fu + } + + comp-ri { + service srv-ri + subchannel sub-ri + } +} + +outputs { + file "file://./test.eti?type=raw" +} + diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 8e5fed1..fa12521 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -147,6 +147,92 @@ static uint16_t get_announcement_flag_from_ptree( return flags; } +// Parse the linkage section +static void parse_linkage(boost::property_tree::ptree& pt, + std::shared_ptr<dabEnsemble> ensemble) +{ + using boost::property_tree::ptree; + using boost::property_tree::ptree_error; + + auto pt_linking = pt.get_child_optional("linking"); + if (pt_linking) + { + for (const auto& it : *pt_linking) { + const string setuid = it.first; + const ptree pt_set = it.second; + + uint16_t lsn = hexparse(pt_set.get("lsn", "0")); + if (lsn == 0) { + etiLog.level(error) << "LSN for linking set " << + setuid << " invalid or missing"; + throw runtime_error("Invalid service linking definition"); + } + + bool active = pt_set.get("active", false); + bool hard = pt_set.get("hard", true); + bool international = pt_set.get("international", false); + + string service_uid = pt_set.get("keyservice", ""); + if (service_uid.empty()) { + etiLog.level(error) << "Key Service for linking set " << + setuid << " invalid or missing"; + throw runtime_error("Invalid service linking definition"); + } + + auto linkageset = make_shared<LinkageSet>(setuid, lsn, hard, international); + linkageset->data.active = active; + linkageset->data.keyservice = service_uid; // TODO check if it exists + + auto pt_list = pt_set.get_child_optional("list"); + if (not pt_list) { + etiLog.level(error) << "list missing in linking set " << + setuid; + throw runtime_error("Invalid service linking definition"); + } + + for (const auto& it : *pt_list) { + const string linkuid = it.first; + const ptree pt_link = it.second; + + ServiceLink link; + + string link_type = pt_link.get("type", ""); + if (link_type == "dab") link.type = ServiceLinkType::DAB; + else if (link_type == "fm") link.type = ServiceLinkType::FM; + else if (link_type == "drm") link.type = ServiceLinkType::DRM; + else if (link_type == "amss") link.type = ServiceLinkType::AMSS; + else { + etiLog.level(error) << "Invalid type " << link_type << + " for link " << linkuid; + throw runtime_error("Invalid service linking definition"); + } + + link.id = hexparse(pt_link.get("id", "0")); + if (link.id == 0) { + etiLog.level(error) << "id for link " << + linkuid << " invalid or missing"; + throw runtime_error("Invalid service linking definition"); + } + + if (international) { + link.ecc = hexparse(pt_link.get("ecc", "0")); + if (link.ecc == 0) { + etiLog.level(error) << "ecc for link " << + linkuid << " invalid or missing"; + throw runtime_error("Invalid service linking definition"); + } + } + else { + link.ecc = 0; + } + + linkageset->data.id_list.push_back(link); + } + ensemble->linkagesets.push_back(linkageset); + } + } +} + void parse_ptree( boost::property_tree::ptree& pt, std::shared_ptr<dabEnsemble> ensemble) @@ -559,6 +645,8 @@ void parse_ptree( ensemble->components.push_back(component); } + + parse_linkage(pt, ensemble); } static dab_input_zmq_config_t setup_zmq_input( diff --git a/src/DabMux.cpp b/src/DabMux.cpp index f72ea8d..2af58c5 100644 --- a/src/DabMux.cpp +++ b/src/DabMux.cpp @@ -7,7 +7,7 @@ Matthias P. Braendli, matthias.braendli@mpb.li http://www.opendigitalradio.org - */ + */ /* This file is part of ODR-DabMux. diff --git a/src/Makefile.am b/src/Makefile.am index dfcdb12..1d33231 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -102,6 +102,7 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.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 \ diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp index db59078..2215bda 100644 --- a/src/MuxElements.cpp +++ b/src/MuxElements.cpp @@ -662,4 +662,60 @@ unsigned short DabSubchannel::getSizeDWord() const return (bitrate * 3) >> 3; } +LinkageSet::LinkageSet(string name, uint16_t lsn, bool hard, bool international) : + RemoteControllable(name) +{ + data.lsn = lsn; + data.active = false; + data.hard = hard; + data.international = international; + RC_ADD_PARAMETER(active, "Activate this linkage set [0 or 1]"); +} + +void LinkageSet::set_parameter(const string& parameter, const string& value) +{ + if (parameter == "active") { + stringstream ss; + ss << value; + ss >> data.active; + } + else { + stringstream ss; + ss << "Parameter '" << parameter << + "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss.str()); + } +} + +const string LinkageSet::get_parameter(const string& parameter) const +{ + stringstream ss; + if (parameter == "active") { + ss << data.active; + } + else { + ss << "Parameter '" << parameter << + "' is not exported by controllable " << get_rc_name(); + throw ParameterError(ss.str()); + } + return ss.str(); +} + +LinkageSetData LinkageSetData::filter_type(ServiceLinkType type) +{ + LinkageSetData lsd; + + lsd.lsn = lsn; + lsd.active = active; + lsd.hard = hard; + lsd.international = international; + lsd.keyservice = keyservice; + for (const auto& link : id_list) { + if (link.type == type) { + lsd.id_list.push_back(link); + } + } + + return lsd; +} diff --git a/src/MuxElements.h b/src/MuxElements.h index 7056121..79100a9 100644 --- a/src/MuxElements.h +++ b/src/MuxElements.h @@ -191,8 +191,9 @@ class DabLabel class DabService; class DabComponent; - class DabSubchannel; +class LinkageSet; + class dabEnsemble : public RemoteControllable { public: dabEnsemble() @@ -228,6 +229,7 @@ class dabEnsemble : public RemoteControllable { std::vector<DabSubchannel*> subchannels; std::vector<std::shared_ptr<AnnouncementCluster> > clusters; + std::vector<std::shared_ptr<LinkageSet> > linkagesets; }; @@ -421,6 +423,57 @@ class DabService : public RemoteControllable DabService(const DabService& other); }; +enum class ServiceLinkType {DAB, FM, DRM, AMSS}; + +/* Represent one link inside a linkage set */ +struct ServiceLink { + ServiceLinkType type; + uint16_t id; + uint8_t ecc; +}; + +/* Keep the data out of LinkageSet to make it copyable */ +struct LinkageSetData { + std::list<ServiceLink> id_list; + + /* Linkage Set Number is a 12-bit number that identifies the linkage + * set in a country (requires coordination between multiplex operators + * in a country) + */ + uint16_t lsn; + + bool active; // Remote-controllable + bool hard; + bool international; + + std::string keyservice; // TODO replace by pointer to service + + /* Return a LinkageSetData with id_list filtered to include + * only those links of a given type + */ + LinkageSetData filter_type(ServiceLinkType type); +}; + +/* Represents a linkage set linkage sets according to + * TS 103 176 Clause 5.2.3 "Linkage sets". This information will + * be encoded in FIG 0/6. + */ +class LinkageSet : public RemoteControllable { + public: + LinkageSet(std::string name, uint16_t lsn, bool hard, bool international); + + LinkageSetData data; + + bool is_active(void) const { return data.active; } + private: + /* Remote control */ + virtual void set_parameter(const std::string& parameter, + const std::string& value); + + /* Getting a parameter always returns a string. */ + virtual const std::string get_parameter(const std::string& parameter) const; +}; + std::vector<DabSubchannel*>::iterator getSubchannel( std::vector<DabSubchannel*>& subchannels, int id); diff --git a/src/RemoteControl.h b/src/RemoteControl.h index 1c830aa..d8b3b6b 100644 --- a/src/RemoteControl.h +++ b/src/RemoteControl.h @@ -6,8 +6,9 @@ Copyright (C) 2016 Matthias P. Braendli, matthias.braendli@mpb.li + http://www.opendigitalradio.org + This module adds remote-control capability to some of the dabmux modules. - see testremotecontrol/test.cpp for an example of how to use this. */ /* This file is part of ODR-DabMux. diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h index 927efc1..446e1ac 100644 --- a/src/fig/FIG0.h +++ b/src/fig/FIG0.h @@ -31,6 +31,7 @@ #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" diff --git a/src/fig/FIG0_6.cpp b/src/fig/FIG0_6.cpp new file mode 100644 index 0000000..411d47c --- /dev/null +++ b/src/fig/FIG0_6.cpp @@ -0,0 +1,353 @@ +/* + 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) { + update(); + m_initialised = true; + } + + FIGtype0* fig0 = NULL; + + for (; linkageSetFIG0_6 != linkageSubsets.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) + headersize + + (num_ids > 0 ? + (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 = 6; + + 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->setLSN(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(); + } + + // update() guarantees us that all entries in a linkage set + // have the same type + for (const auto& l : linkageSetFIG0_6->id_list) { + if (l.type != linkageSetFIG0_6->id_list.front().type) { + etiLog.log(error, "INTERNAL ERROR: invalid linkage subset 0x%04x", + linkageSetFIG0_6->lsn); + throw std::runtime_error("INTERNAL ERROR"); + } + } + + switch (linkageSetFIG0_6->id_list.front().type) { + case ServiceLinkType::DAB: header->IdLQ = FIG0_6_IDLQ_DAB; break; + case ServiceLinkType::FM: header->IdLQ = FIG0_6_IDLQ_RDS; break; + case ServiceLinkType::DRM: header->IdLQ = FIG0_6_IDLQ_DRM_AMSS; break; + case ServiceLinkType::AMSS: header->IdLQ = FIG0_6_IDLQ_DRM_AMSS; break; + } + header->rfa = 0; + header->num_ids = num_ids; + + fig0->Length += headersize; + buf += headersize; + remaining -= headersize; + + 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(); + } + + 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; + } + } + else 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; + } + } + } + } + + if (linkageSetFIG0_6 == linkageSubsets.end()) { + update(); + fs.complete_fig_transmitted = true; + } + + fs.num_bytes_written = max_size - remaining; + return fs; +} + +void FIG0_6::update() +{ + linkageSubsets.clear(); + + // TODO check if AMSS and DRM have to be put into a single subset + + for (const auto& linkageset : m_rti->ensemble->linkagesets) { + const auto lsn = linkageset->data.lsn; + + for (const auto& link : linkageset->data.id_list) { + const auto type = link.type; + + const auto subset = + std::find_if(linkageSubsets.begin(), linkageSubsets.end(), + [&](const LinkageSetData& l) { + return not l.id_list.empty() and + l.lsn == lsn and l.id_list.front().type == type; + }); + + if (subset == linkageSubsets.end()) { + // A subset with that LSN and type does not exist yet + linkageSubsets.push_back( linkageset->data.filter_type(type) ); + } + } + } + + linkageSetFIG0_6 = linkageSubsets.begin(); + +#if 0 + etiLog.log(info, " Linkage Sets"); + for (const auto& lsd : linkageSubsets) { + + etiLog.log(info, " LSN 0x%04x", lsd.lsn); + etiLog.level(info) << " active " << (lsd.active ? "true" : "false"); + etiLog.level(info) << " " << (lsd.hard ? "hard" : "soft"); + etiLog.level(info) << " international " << (lsd.international ? "true" : "false"); + etiLog.level(info) << " key service " << lsd.keyservice; + + etiLog.level(info) << " ID list"; + for (const auto& link : lsd.id_list) { + switch (link.type) { + case ServiceLinkType::DAB: + etiLog.level(info) << " type DAB"; + break; + case ServiceLinkType::FM: + etiLog.level(info) << " type FM"; + break; + case ServiceLinkType::DRM: + etiLog.level(info) << " type DRM"; + break; + case ServiceLinkType::AMSS: + etiLog.level(info) << " type AMSS"; + break; + } + etiLog.log(info, " id 0x%04x", link.id); + if (lsd.international) { + etiLog.log(info, " ecc 0x%04x", link.ecc); + } + } + } +#endif +} + + +FIG0_6_CEI::FIG0_6_CEI(FIGRuntimeInformation *rti) : + m_rti(rti) +{ +} + +FillStatus FIG0_6_CEI::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 5s + const int timeout = 5000/24; + + m_transition.update_state(timeout, ensemble->linkagesets); + + FillStatus fs; + ssize_t remaining = max_size; + + FIGtype0* fig0 = NULL; + + // Combine all links into one list + set<LinkageSet*> alllinks; + for (const auto& l : m_transition.new_entries) { + alllinks.insert(l.first.get()); + } + for (const auto& l : m_transition.repeated_entries) { + alllinks.insert(l.get()); + } + for (const auto& l : m_transition.disabled_entries) { + alllinks.insert(l.first.get()); + } + + for (auto& link : alllinks) { + const bool PD = false; + const bool ILS = link->data.international; + + // The CEI does not send list contents + const size_t num_ids = 0; + + const size_t headersize = sizeof(struct FIGtype0_6_header); + const int required_size = sizeof(struct FIGtype0_6) + headersize + + (num_ids > 0 ? + (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 = 1; // This is a CEI + fig0->OE = 0; + fig0->PD = PD; + fig0->Extension = 6; + + 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 = link->data.active; + fig0_6->SH = link->data.hard; + fig0_6->ILS = ILS; + fig0_6->setLSN(link->data.lsn); + + fig0->Length += sizeof(struct FIGtype0_6); + buf += sizeof(struct FIGtype0_6); + remaining -= sizeof(struct FIGtype0_6); + } + + fs.complete_fig_transmitted = true; + return fs; +} + +} + diff --git a/src/fig/FIG0_6.h b/src/fig/FIG0_6.h new file mode 100644 index 0000000..e970102 --- /dev/null +++ b/src/fig/FIG0_6.h @@ -0,0 +1,98 @@ +/* + 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 <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; + + /* Update the linkageSubsets */ + void update(void); + + /* A LinkageSet can contain links of different types + * (DAB, FM, DRM, AMSS), but the FIG needs to send + * different FIGs for each of those, because the IdLQ flag + * is common to a list. + * + * We reorganise all LinkageSets into subsets that have + * the same type. + */ + std::vector<LinkageSetData> linkageSubsets; + std::vector<LinkageSetData>::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; + + TransitionHandler<LinkageSet> m_transition; +}; + +} diff --git a/src/fig/FIG0structs.h b/src/fig/FIG0structs.h index a1e79d3..3b9bfbf 100644 --- a/src/fig/FIG0structs.h +++ b/src/fig/FIG0structs.h @@ -152,6 +152,32 @@ struct FIGtype0_5_short { uint8_t language; } PACKED; +struct FIGtype0_6 { + uint8_t LSN_high:4; // Linkage Set Number + uint8_t ILS:1; // 0=national / 1=international + uint8_t SH:1; // 0=Soft link / 1=Hard link + uint8_t LA:1; // Linkage actuator + uint8_t IdListFlag:1; // Marks the presence of the list + + uint8_t LSN_low; // Linkage Set Number + + void setLSN(uint16_t LSN) { + LSN_high = LSN >> 8; + LSN_low = LSN & 0xff; + } +} PACKED; + +#define FIG0_6_IDLQ_DAB 0x0 +#define FIG0_6_IDLQ_RDS 0x1 +#define FIG0_6_IDLQ_DRM_AMSS 0x3 + +struct FIGtype0_6_header { + uint8_t num_ids:4; // number of Ids to follow in the list + uint8_t rfa:1; // must be 0 + uint8_t IdLQ:2; // Identifier List Qualifier, see above defines + uint8_t rfu:1; // must be 0 +} PACKED; + struct FIGtype0_8_short { uint8_t SCIdS:4; uint8_t rfa_1:3; diff --git a/src/fig/FIGCarousel.cpp b/src/fig/FIGCarousel.cpp index 63226e0..2fc6718 100644 --- a/src/fig/FIGCarousel.cpp +++ b/src/fig/FIGCarousel.cpp @@ -64,6 +64,8 @@ FIGCarousel::FIGCarousel(std::shared_ptr<dabEnsemble> ensemble) : m_fig0_2(&m_rti), m_fig0_3(&m_rti), m_fig0_5(&m_rti), + m_fig0_6(&m_rti), + m_fig0_6_cei(&m_rti), m_fig0_17(&m_rti), m_fig0_8(&m_rti), m_fig1_0(&m_rti), @@ -94,6 +96,8 @@ FIGCarousel::FIGCarousel(std::shared_ptr<dabEnsemble> ensemble) : load_and_allocate(m_fig0_2, FIBAllocation::FIB_ANY); load_and_allocate(m_fig0_3, FIBAllocation::FIB_ANY); load_and_allocate(m_fig0_5, FIBAllocation::FIB_ANY); + load_and_allocate(m_fig0_6, FIBAllocation::FIB_ANY); + load_and_allocate(m_fig0_6_cei, FIBAllocation::FIB_ANY); load_and_allocate(m_fig0_8, FIBAllocation::FIB_ANY); load_and_allocate(m_fig0_13, FIBAllocation::FIB_ANY); diff --git a/src/fig/FIGCarousel.h b/src/fig/FIGCarousel.h index b46f4a6..209fe19 100644 --- a/src/fig/FIGCarousel.h +++ b/src/fig/FIGCarousel.h @@ -89,6 +89,8 @@ class FIGCarousel { FIG0_2 m_fig0_2; FIG0_3 m_fig0_3; FIG0_5 m_fig0_5; + FIG0_6 m_fig0_6; + FIG0_6_CEI m_fig0_6_cei; FIG0_17 m_fig0_17; FIG0_8 m_fig0_8; FIG1_0 m_fig1_0; diff --git a/src/utils.cpp b/src/utils.cpp index 9976e88..0399467 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -392,6 +392,43 @@ void printSubchannels(vector<DabSubchannel*>& subchannels) } } +static void printLinking(const shared_ptr<dabEnsemble> ensemble) +{ + etiLog.log(info, " Linkage Sets"); + for (const auto& linkageset : ensemble->linkagesets) { + const auto& lsd = linkageset->data; + + etiLog.level(info) << " set " << linkageset->get_rc_name(); + etiLog.log(info, " LSN 0x%04x", lsd.lsn); + etiLog.level(info) << " active " << (lsd.active ? "true" : "false"); + etiLog.level(info) << " " << (lsd.hard ? "hard" : "soft"); + etiLog.level(info) << " international " << (lsd.international ? "true" : "false"); + etiLog.level(info) << " key service " << lsd.keyservice; + + etiLog.level(info) << " ID list"; + for (const auto& link : lsd.id_list) { + switch (link.type) { + case ServiceLinkType::DAB: + etiLog.level(info) << " type DAB"; + break; + case ServiceLinkType::FM: + etiLog.level(info) << " type FM"; + break; + case ServiceLinkType::DRM: + etiLog.level(info) << " type DRM"; + break; + case ServiceLinkType::AMSS: + etiLog.level(info) << " type AMSS"; + break; + } + etiLog.log(info, " id 0x%04x", link.id); + if (lsd.international) { + etiLog.log(info, " ecc 0x%04x", link.ecc); + } + } + } +} + void printEnsemble(const shared_ptr<dabEnsemble> ensemble) { etiLog.log(info, "Ensemble"); @@ -424,5 +461,7 @@ void printEnsemble(const shared_ptr<dabEnsemble> ensemble) etiLog.level(info) << cluster->tostring(); } } + + printLinking(ensemble); } |