diff options
| -rw-r--r-- | doc/servicelinking.mux | 166 | ||||
| -rw-r--r-- | src/ConfigParser.cpp | 90 | ||||
| -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 | 53 | ||||
| -rw-r--r-- | src/RemoteControl.cpp | 4 | ||||
| -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 | 98 | ||||
| -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 | 39 | 
15 files changed, 807 insertions, 5 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 bb1e0e0..1876697 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -3,7 +3,7 @@     2011, 2012 Her Majesty the Queen in Right of Canada (Communications     Research Center Canada) -   Copyright (C) 2014, 2015 +   Copyright (C) 2016     Matthias P. Braendli, matthias.braendli@mpb.li      http://www.opendigitalradio.org @@ -142,6 +142,92 @@ 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) @@ -554,6 +640,8 @@ void parse_ptree(          ensemble->components.push_back(component);      } + +    parse_linkage(pt, ensemble);  }  void setup_subchannel_from_ptree(DabSubchannel* subchan, diff --git a/src/DabMux.cpp b/src/DabMux.cpp index aefa701..6dd30bf 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 3476bbd..5edd0a4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -103,6 +103,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 773ec17..510775e 100644 --- a/src/MuxElements.cpp +++ b/src/MuxElements.cpp @@ -662,4 +662,60 @@ unsigned short DabSubchannel::getSizeDWord(void) 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..8efc2cb 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::list<std::shared_ptr<LinkageSet> > linkagesets;  }; @@ -421,6 +423,55 @@ 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; +    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.cpp b/src/RemoteControl.cpp index 9ee1f24..31e63f2 100644 --- a/src/RemoteControl.cpp +++ b/src/RemoteControl.cpp @@ -3,8 +3,10 @@     Her Majesty the Queen in Right of Canada (Communications Research     Center Canada) -   Copyright (C) 2014 +   Copyright (C) 2016     Matthias P. Braendli, matthias.braendli@mpb.li + +    http://www.opendigitalradio.org   */  /*     This file is part of ODR-DabMux. diff --git a/src/RemoteControl.h b/src/RemoteControl.h index 1a81b42..fe8ac42 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..401606e --- /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) + +            (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 = 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 +} + +} + diff --git a/src/fig/FIG0_6.h b/src/fig/FIG0_6.h new file mode 100644 index 0000000..1af396f --- /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::list<LinkageSetData> linkageSubsets; +        std::list<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; +        bool m_initialised; +        std::vector<std::shared_ptr<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 cbea12b..bd2bf51 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 67772a6..be0b23f 100644 --- a/src/fig/FIGCarousel.h +++ b/src/fig/FIGCarousel.h @@ -92,6 +92,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 02fbc3f..94268da 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -552,6 +552,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"); @@ -584,5 +621,7 @@ void printEnsemble(const shared_ptr<dabEnsemble> ensemble)              etiLog.level(info) << cluster->tostring();          }      } + +    printLinking(ensemble);  }  | 
