diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ConfigParser.cpp | 96 | ||||
| -rw-r--r-- | src/DabMultiplexer.h | 8 | ||||
| -rw-r--r-- | src/MuxElements.cpp | 47 | ||||
| -rw-r--r-- | src/MuxElements.h | 30 | ||||
| -rw-r--r-- | src/fig/FIG0.cpp | 182 | ||||
| -rw-r--r-- | src/fig/FIG0.h | 23 | ||||
| -rw-r--r-- | src/fig/FIGCarousel.cpp | 4 | ||||
| -rw-r--r-- | src/fig/FIGCarousel.h | 1 | ||||
| -rw-r--r-- | src/utils.cpp | 8 | 
9 files changed, 363 insertions, 36 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 7840a60..1176efe 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -125,6 +125,22 @@ int hexparse(std::string input)      return value;  } +uint16_t get_announcement_flag_from_ptree( +        boost::property_tree::ptree& pt +        ) +{ +    uint16_t flags = 0; +    for (size_t flag = 0; flag < 16; flag++) { +        std::string announcement_name(annoucement_flags_names[flag]); +        bool flag_set = pt.get<bool>(announcement_name, false); + +        if (flag_set) { +            flags |= (1 << flag); +        } +    } + +    return flags; +}  void parse_ptree(boost::property_tree::ptree& pt,          boost::shared_ptr<dabEnsemble> ensemble, @@ -219,6 +235,24 @@ void parse_ptree(boost::property_tree::ptree& pt,              abort();      } +    try { +        ptree pt_announcements = pt_ensemble.get_child("announcements"); +        for (auto announcement : pt_announcements) { +            string name = announcement.first; +            ptree pt_announcement = announcement.second; + +            auto cl = make_shared<AnnouncementCluster>(name); +            cl->cluster_id = pt_announcement.get<uint8_t>("cluster"); +            cl->flags = get_announcement_flag_from_ptree(pt_announcement); +            cl->subchanneluid = pt_announcement.get<string>("subchannel"); + +            cl->enrol_at(*rc); +            ensemble->clusters.push_back(cl); +        } +    } +    catch (ptree_error& e) { +        etiLog.level(info) << "No announcements defined in ensemble"; +    }      /******************** READ SERVICES PARAMETERS *************/ @@ -251,44 +285,44 @@ void parse_ptree(boost::property_tree::ptree& pt,              service = new_srv;          } -        /* Parse ASu */ -        service->ASu = 0; -        for (size_t flag = 0; flag < 16; flag++) { -            std::string announcement_name(annoucement_flags_names[flag]); -            bool flag_set = -                pt_service.get<bool>("announcements." + announcement_name, false); - -            if (flag_set) { -                service->ASu |= (1 << flag); +        try { +            /* Parse announcements */ +            service->ASu = get_announcement_flag_from_ptree( +                    pt_service.get_child("announcements")); + +            auto clusterlist = pt_service.get<std::string>("announcements.clusters", ""); +            vector<string> clusters_s; +            boost::split(clusters_s, +                    clusterlist, +                    boost::is_any_of(",")); + +            for (const auto& cluster_s : clusters_s) { +                if (cluster_s == "") { +                    continue; +                } +                try { +                    service->clusters.push_back(std::stoi(cluster_s)); +                } +                catch (std::logic_error& e) { +                    etiLog.level(warn) << "Cannot parse '" << clusterlist << +                        "' announcement clusters for service " << serviceuid << +                        ": " << e.what(); +                }              } -        } - -        auto clusterlist = pt_service.get<std::string>("announcements.clusters", ""); -        vector<string> clusters_s; -        boost::split(clusters_s, -                clusterlist, -                boost::is_any_of(",")); -        for (const auto& cluster_s : clusters_s) { -            if (cluster_s == "") { -                continue; -            } -            try { -                service->clusters.push_back(std::stoi(cluster_s)); -            } -            catch (std::logic_error& e) { -                etiLog.level(warn) << "Cannot parse '" << clusterlist << -                    "' announcement clusters for service " << serviceuid << -                    ": " << e.what(); +            if (service->ASu != 0 and service->clusters.empty()) { +                etiLog.level(warn) << "Cluster list for service " << serviceuid << +                    "is empty, but announcements are defined";              }          } +        catch (ptree_error& e) { +            service->ASu = 0; +            service->clusters.clear(); -        if (service->ASu != 0 and service->clusters.empty()) { -            etiLog.level(warn) << "Cluster list for service " << serviceuid << -                "is empty, but announcements are defined"; +            etiLog.level(info) << "No announcements defined in service " << +                serviceuid;          } -          int success = -5;          string servicelabel = pt_service.get<string>("label"); diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h index 0458969..f454d20 100644 --- a/src/DabMultiplexer.h +++ b/src/DabMultiplexer.h @@ -395,6 +395,14 @@ struct FIGtype0_18 {      /* Followed by uint8_t Cluster IDs */  } PACKED; +struct FIGtype0_19 { +    uint8_t  ClusterId; +    uint16_t ASw; +    uint8_t  SubChId:6; +    uint8_t  RegionFlag:1; // shall be zero +    uint8_t  NewFlag:1; +    // Region and RFa not supported +} PACKED;  struct FIGtype0_1 {      uint8_t Length:5; diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp index 2f45651..452e179 100644 --- a/src/MuxElements.cpp +++ b/src/MuxElements.cpp @@ -3,7 +3,7 @@     2011, 2012 Her Majesty the Queen in Right of Canada (Communications     Research Center Canada) -   Copyright (C) 2014 +   Copyright (C) 2014, 2015     Matthias P. Braendli, matthias.braendli@mpb.li     */  /* @@ -28,6 +28,7 @@  #include "MuxElements.h"  #include <boost/algorithm/string.hpp> +#include <boost/format.hpp>  const unsigned short Sub_Channel_SizeTable[64] = {      16, 21, 24, 29, 35, 24, 29, 35, @@ -44,6 +45,50 @@ const unsigned short Sub_Channel_SizeTable[64] = {  using namespace std; +std::string AnnouncementCluster::tostring() const +{ +    stringstream ss; +    ss << "    cluster id : " << (int)cluster_id; +    ss << "    flags      : 0x" << boost::format("%04x") % flags; +    ss << "    subchannel : " << subchanneluid; +    if (m_active) { +        ss << "  *"; +    } + +    return ss.str(); +} + +void AnnouncementCluster::set_parameter(const string& parameter, +        const string& value) +{ +    if (parameter == "active") { +        stringstream ss; +        ss << value; +        ss >> m_active; +    } +    else { +        stringstream ss; +        ss << "Parameter '" << parameter << +            "' is not exported by controllable " << get_rc_name(); +        throw ParameterError(ss.str()); +    } +} + +const string AnnouncementCluster::get_parameter(const string& parameter) const +{ +    stringstream ss; +    if (parameter == "active") { +        ss << m_active; +    } +    else { +        ss << "Parameter '" << parameter << +            "' is not exported by controllable " << get_rc_name(); +        throw ParameterError(ss.str()); +    } +    return ss.str(); +} + +  int DabLabel::setLabel(const std::string& label)  {      size_t len = label.length(); diff --git a/src/MuxElements.h b/src/MuxElements.h index e1cc3a9..9fe2aeb 100644 --- a/src/MuxElements.h +++ b/src/MuxElements.h @@ -64,6 +64,34 @@ const char * const annoucement_flags_names[] = {      "tba1", "tba2", "tba3", "tba4", "tba5"  }; +/* Class representing an announcement cluster for FIG 0/19 */ +class AnnouncementCluster : public RemoteControllable { +    public: +        AnnouncementCluster(std::string name) : +            RemoteControllable(name), +            m_active(false) +        { +            RC_ADD_PARAMETER(active, "Signal this announcement"); +        } + +        uint8_t cluster_id; +        uint16_t flags; +        std::string subchanneluid; + +        std::string tostring(void) const; + +        bool is_active(void) const { return m_active; }; + +    private: +        bool m_active; + +        /* 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; +};  struct dabOutput {      dabOutput(const char* proto, const char* name) : @@ -160,6 +188,8 @@ class dabEnsemble : public RemoteControllable {          std::vector<std::shared_ptr<DabService> > services;          std::vector<DabComponent*> components;          std::vector<dabSubchannel*> subchannels; + +        std::vector<std::shared_ptr<AnnouncementCluster> > clusters;  }; diff --git a/src/fig/FIG0.cpp b/src/fig/FIG0.cpp index 9f89109..0e8856f 100644 --- a/src/fig/FIG0.cpp +++ b/src/fig/FIG0.cpp @@ -994,5 +994,187 @@ FillStatus FIG0_18::fill(uint8_t *buf, size_t max_size)      return fs;  } +//=========== FIG 0/19 =========== + +FIG0_19::FIG0_19(FIGRuntimeInformation *rti) : +    m_rti(rti) +{ } + +FillStatus FIG0_19::fill(uint8_t *buf, size_t max_size) +{ +    using namespace std; + +    update_state(); + +    FillStatus fs; +    ssize_t remaining = max_size; + +    auto ensemble = m_rti->ensemble; + +    FIGtype0* fig0 = NULL; +    set<AnnouncementCluster*> allclusters; +    for (const auto& cluster : m_new_announcements) { +        allclusters.insert(cluster.first.get()); +    } +    for (const auto& cluster : m_repeated_announcements) { +        allclusters.insert(cluster.get()); +    } +    for (const auto& cluster : m_disabled_announcements) { +        allclusters.insert(cluster.first.get()); +    } + +    fs.complete_fig_transmitted = true; +    for (const auto& cluster : allclusters) { + +        if (remaining < (int)sizeof(FIGtype0_19)) { +            fs.complete_fig_transmitted = false; +            break; +        } + +        if (fig0 == NULL) { +            if (remaining < 2 + (int)sizeof(FIGtype0_19)) { +                break; +            } + +            fig0 = (FIGtype0*)buf; +            fig0->FIGtypeNumber = 0; +            fig0->Length = 1; +            fig0->CN = 0; +            fig0->OE = 0; +            fig0->PD = 0; +            fig0->Extension = 19; +            buf += 2; +            remaining -= 2; +        } + +        auto fig0_19 = (FIGtype0_19*)buf; +        fig0_19->ClusterId = cluster->cluster_id; +        if (cluster->is_active()) { +            fig0_19->ASw = cluster->flags; +        } +        else { +            fig0_19->ASw = 0; +        } +        fig0_19->NewFlag = 1; +        fig0_19->RegionFlag = 0; +        fig0_19->SubChId = 0; +        bool found = false; + +        for (const auto& subchannel : ensemble->subchannels) { +            cerr << "*****" << subchannel->uid << " vs " << +                cluster->subchanneluid << endl; + +            if (subchannel->uid == cluster->subchanneluid) { +                fig0_19->SubChId = subchannel->id; +                found = true; +                break; +            } +        } +        if (not found) { +            etiLog.level(warn) << "FIG0/19: could not find subchannel " << +                cluster->subchanneluid << " for cluster " << +                (int)cluster->cluster_id; +            continue; +        } + +        buf += sizeof(FIGtype0_19); +        remaining -= sizeof(FIGtype0_19); +    } + +    fs.num_bytes_written = max_size - remaining; +    return fs; +} + +void FIG0_19::update_state() +{ +    auto ensemble = m_rti->ensemble; + +    // We are called every 24ms, and must timeout after 2s +    const int timeout = 2000/24; + +    etiLog.level(info) << " FIG0/19 new"; +    for (const auto& cluster : m_new_announcements) { +        etiLog.level(info) << "  " << cluster.first->tostring() +            << ": " << cluster.second; +    } +    etiLog.level(info) << " FIG0/19 repeated"; +    for (const auto& cluster : m_repeated_announcements) { +        etiLog.level(info) << "  " << cluster->tostring(); +    } +    etiLog.level(info) << " FIG0/19 disabled"; +    for (const auto& cluster : m_disabled_announcements) { +        etiLog.level(info) << "  " << cluster.first->tostring() +            << ": " << cluster.second; +    } + +    etiLog.level(info) << " FIG0/19 in ensemble"; +    for (const auto& cluster : ensemble->clusters) { +        etiLog.level(info) << "  " << cluster->tostring(); +        if (cluster->is_active()) { +            if (m_repeated_announcements.count(cluster) > 0) { +                // We are currently announcing this cluster +                continue; +            } + +            if (m_new_announcements.count(cluster) > 0) { +                // We are currently announcing this cluster at a +                // fast rate. Handle timeout: +                m_new_announcements[cluster] -= 1; +                if (m_new_announcements[cluster] <= 0) { +                    m_repeated_announcements.insert(cluster); +                    m_new_announcements.erase(cluster); +                } +                continue; +            } + +            // unlikely +            if (m_disabled_announcements.count(cluster) > 0) { +                m_new_announcements[cluster] = timeout; +                m_disabled_announcements.erase(cluster); +                continue; +            } + +            // It's a new announcement! +            m_new_announcements[cluster] = timeout; +        } +        else { // Not active +            if (m_disabled_announcements.count(cluster) > 0) { +                m_disabled_announcements[cluster] -= 1; +                if (m_disabled_announcements[cluster] <= 0) { +                    m_disabled_announcements.erase(cluster); +                } +                continue; +            } + +            if (m_repeated_announcements.count(cluster) > 0) { +                // We are currently announcing this cluster +                m_disabled_announcements[cluster] = timeout; +                m_repeated_announcements.erase(cluster); +                continue; +            } + +            // unlikely +            if (m_new_announcements.count(cluster) > 0) { +                // We are currently announcing this cluster at a +                // fast rate. We must stop announcing it +                m_disabled_announcements[cluster] = timeout; +                m_new_announcements.erase(cluster); +                continue; +            } +        } +    } +} + +FIG_rate FIG0_19::repetition_rate(void) +{ +    if (    m_new_announcements.size() > 0 or +            m_disabled_announcements.size() ) { +        return FIG_rate::A_B; +    } +    else { +        return FIG_rate::B; +    } +} +  } // namespace FIC diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h index 9015b00..9811eaf 100644 --- a/src/fig/FIG0.h +++ b/src/fig/FIG0.h @@ -220,15 +220,32 @@ class FIG0_19 : public IFIG      public:          FIG0_19(FIGRuntimeInformation* rti);          virtual FillStatus fill(uint8_t *buf, size_t max_size); -        virtual FIG_rate repetition_rate(void) { return FIG_rate::A; } +        virtual FIG_rate repetition_rate(void);          virtual const int figtype(void) const { return 0; }          virtual const int figextension(void) const { return 19; }      private:          FIGRuntimeInformation *m_rti; -        bool m_initialised; -        std::vector<std::shared_ptr<DabService> >::iterator service; + +        void update_state(void); + +        /* When a new announcement gets active, it is moved into the list +         * of new announcements, and gets transmitted at a faster rate for +         * two seconds. +         * Same for recently disabled announcements. +         */ + +        /* Map of cluster to frame count */ +        std::map< +            std::shared_ptr<AnnouncementCluster>,int> m_new_announcements; + +        std::set< +            std::shared_ptr<AnnouncementCluster> > m_repeated_announcements; + +        /* Map of cluster to frame count */ +        std::map< +            std::shared_ptr<AnnouncementCluster>,int> m_disabled_announcements;  };  } // namespace FIC diff --git a/src/fig/FIGCarousel.cpp b/src/fig/FIGCarousel.cpp index 8eedd6e..d565c23 100644 --- a/src/fig/FIGCarousel.cpp +++ b/src/fig/FIGCarousel.cpp @@ -68,7 +68,8 @@ FIGCarousel::FIGCarousel(boost::shared_ptr<dabEnsemble> ensemble) :      m_fig1_1(&m_rti),      m_fig1_4(&m_rti),      m_fig1_5(&m_rti), -    m_fig0_18(&m_rti) +    m_fig0_18(&m_rti), +    m_fig0_19(&m_rti)  {      load_and_allocate(m_fig0_0, 0);      load_and_allocate(m_fig0_1, 0); @@ -85,6 +86,7 @@ FIGCarousel::FIGCarousel(boost::shared_ptr<dabEnsemble> ensemble) :      load_and_allocate(m_fig1_1, 2);      load_and_allocate(m_fig1_5, 2);      load_and_allocate(m_fig0_18, 2); +    load_and_allocate(m_fig0_19, 2);  }  void FIGCarousel::load_and_allocate(IFIG& fig, int fib) diff --git a/src/fig/FIGCarousel.h b/src/fig/FIGCarousel.h index 170ffdd..f7a839c 100644 --- a/src/fig/FIGCarousel.h +++ b/src/fig/FIGCarousel.h @@ -86,6 +86,7 @@ class FIGCarousel {          FIG1_4 m_fig1_4;          FIG1_5 m_fig1_5;          FIG0_18 m_fig0_18; +        FIG0_19 m_fig0_19;  };  } // namespace FIC diff --git a/src/utils.cpp b/src/utils.cpp index 1f6754d..b45f930 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -554,5 +554,13 @@ void printEnsemble(const boost::shared_ptr<dabEnsemble> ensemble)      }      etiLog.log(info, " intl. table. %d", ensemble->international_table); +    if (ensemble->clusters.empty()) { +        etiLog.level(info) << " No announcement clusters defined"; +    } +    else { +        for (const auto& cluster : ensemble->clusters) { +            etiLog.level(info) << cluster->tostring(); +        } +    }  }  | 
