diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ConfigParser.cpp | 86 | ||||
| -rw-r--r-- | src/DabMux.cpp | 2 | ||||
| -rw-r--r-- | src/Makefile.am | 1 | ||||
| -rw-r--r-- | src/MuxElements.cpp | 25 | ||||
| -rw-r--r-- | src/MuxElements.h | 49 | ||||
| -rw-r--r-- | src/RemoteControl.h | 3 | ||||
| -rw-r--r-- | src/fig/FIG0.h | 1 | ||||
| -rw-r--r-- | src/fig/FIG0_6.cpp | 270 | ||||
| -rw-r--r-- | src/fig/FIG0_6.h | 82 | ||||
| -rw-r--r-- | src/fig/FIG0structs.h | 26 | ||||
| -rw-r--r-- | src/fig/FIGCarousel.cpp | 2 | ||||
| -rw-r--r-- | src/fig/FIGCarousel.h | 1 | ||||
| -rw-r--r-- | src/utils.cpp | 38 | 
13 files changed, 583 insertions, 3 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 0f05076..7219663 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -116,6 +116,90 @@ 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 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->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->id_list.push_back(link); +            } +            ensemble->linkagesets.push_back(linkageset); +        } +    } +} +  void parse_ptree(          boost::property_tree::ptree& pt,          std::shared_ptr<dabEnsemble> ensemble) @@ -528,6 +612,8 @@ void parse_ptree(          ensemble->components.push_back(component);      } + +    parse_linkage(pt, ensemble);  }  static Inputs::dab_input_zmq_config_t setup_zmq_input( diff --git a/src/DabMux.cpp b/src/DabMux.cpp index 3927420..04d1980 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 17e68fe..de2aa37 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -86,6 +86,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 79aabf6..9dbe5fc 100644 --- a/src/MuxElements.cpp +++ b/src/MuxElements.cpp @@ -662,4 +662,29 @@ unsigned short DabSubchannel::getSizeDWord() const      return (bitrate * 3) >> 3;  } +LinkageSet::LinkageSet(const std::string& name, +        uint16_t lsn, +        bool hard, +        bool international) : +    lsn(lsn), +    active(true), +    hard(hard), +    international(international) +{} + + +LinkageSet LinkageSet::filter_type(const ServiceLinkType type) +{ +    LinkageSet lsd(m_name, lsn, hard, international); + +    lsd.active = active; +    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 7324cdc..968fb14 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,51 @@ 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; +}; + +/* 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: +        LinkageSet(const std::string& name, +                uint16_t lsn, +                bool hard, +                bool international); + +        std::string get_name(void) const { return m_name; } + +        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; // TODO: Remote-controllable +        bool hard; +        bool international; + +        std::string keyservice; // TODO replace by pointer to service + +        /* Return a LinkageSet with id_list filtered to include +         * only those links of a given type +         */ +        LinkageSet filter_type(const ServiceLinkType type); + +    private: +        std::string m_name; +}; +  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..5174ec2 --- /dev/null +++ b/src/fig/FIG0_6.cpp @@ -0,0 +1,270 @@ +/* +   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->lsn; + +        for (const auto& link : linkageset->id_list) { +            const auto type = link.type; + +            const auto subset = +                std::find_if(linkageSubsets.begin(), linkageSubsets.end(), +                    [&](const LinkageSet& 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->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 +} + +} + diff --git a/src/fig/FIG0_6.h b/src/fig/FIG0_6.h new file mode 100644 index 0000000..e7c81c5 --- /dev/null +++ b/src/fig/FIG0_6.h @@ -0,0 +1,82 @@ +/* +   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" + +namespace FIC { + +/* TODO + * This FIG code is unable to transmit the CEI to announce + * activation/deactivation of linkage sets. + * The TransitionHandler.h would be useful for that purpose + */ + +// 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<LinkageSet> linkageSubsets; +        std::vector<LinkageSet>::iterator linkageSetFIG0_6; +}; + +} 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..ac2a80b 100644 --- a/src/fig/FIGCarousel.cpp +++ b/src/fig/FIGCarousel.cpp @@ -64,6 +64,7 @@ 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_17(&m_rti),      m_fig0_8(&m_rti),      m_fig1_0(&m_rti), @@ -94,6 +95,7 @@ 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_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..f52f266 100644 --- a/src/fig/FIGCarousel.h +++ b/src/fig/FIGCarousel.h @@ -89,6 +89,7 @@ class FIGCarousel {          FIG0_2 m_fig0_2;          FIG0_3 m_fig0_3;          FIG0_5 m_fig0_5; +        FIG0_6 m_fig0_6;          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 cf57170..4c08e8b 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -366,6 +366,42 @@ void printSubchannels(vector<DabSubchannel*>& subchannels)      }  } +static void printLinking(const shared_ptr<dabEnsemble> ensemble) +{ +    etiLog.log(info, " Linkage Sets"); +    for (const auto& ls : ensemble->linkagesets) { + +        etiLog.level(info) << "  set " << ls->get_name(); +        etiLog.log(info,      "   LSN 0x%04x", ls->lsn); +        etiLog.level(info) << "   active " << (ls->active ? "true" : "false"); +        etiLog.level(info) << "   " << (ls->hard ? "hard" : "soft"); +        etiLog.level(info) << "   international " << (ls->international ? "true" : "false"); +        etiLog.level(info) << "   key service " << ls->keyservice; + +        etiLog.level(info) << "   ID list"; +        for (const auto& link : ls->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 (ls->international) { +                etiLog.log(info,      "    ecc 0x%04x", link.ecc); +            } +        } +    } +} +  void printEnsemble(const shared_ptr<dabEnsemble> ensemble)  {      etiLog.log(info, "Ensemble"); @@ -398,6 +434,8 @@ void printEnsemble(const shared_ptr<dabEnsemble> ensemble)              etiLog.level(info) << cluster->tostring();          }      } + +    printLinking(ensemble);  }  long hexparse(const std::string& input)  | 
