From d20b9a0c8f90fbc373dbd5757b5c6a03bbfd8c83 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sat, 15 Aug 2015 13:42:23 +0200 Subject: Add FIG0/19 --- doc/example.mux | 14 +++- src/ConfigParser.cpp | 96 ++++++++++++++++--------- src/DabMultiplexer.h | 8 +++ src/MuxElements.cpp | 47 ++++++++++++- src/MuxElements.h | 30 ++++++++ src/fig/FIG0.cpp | 182 ++++++++++++++++++++++++++++++++++++++++++++++++ src/fig/FIG0.h | 23 +++++- src/fig/FIGCarousel.cpp | 4 +- src/fig/FIGCarousel.h | 1 + src/utils.cpp | 8 +++ 10 files changed, 376 insertions(+), 37 deletions(-) diff --git a/doc/example.mux b/doc/example.mux index c818dbb..1141006 100644 --- a/doc/example.mux +++ b/doc/example.mux @@ -74,6 +74,18 @@ ensemble { ; 2 corresponds to program types used in north america label "TuxMux" shortlabel "Tux" + + ; Announcement settings for FIG0/19 + announcements { + test_announcement { + cluster 1 + flags { + Traffic true + } + + subchannel sub-fu + } + } } ; Definition of DAB services @@ -91,7 +103,7 @@ services { ; This lists all possible announcements. If one is left out, it is disabled. announcements { Alarm false - Traffic false + Traffic true Travel false Warning false News false 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(announcement_name, false); + + if (flag_set) { + flags |= (1 << flag); + } + } + + return flags; +} void parse_ptree(boost::property_tree::ptree& pt, boost::shared_ptr 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(name); + cl->cluster_id = pt_announcement.get("cluster"); + cl->flags = get_announcement_flag_from_ptree(pt_announcement); + cl->subchanneluid = pt_announcement.get("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("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("announcements.clusters", ""); + vector 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("announcements.clusters", ""); - vector 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("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 +#include 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 > services; std::vector components; std::vector subchannels; + + std::vector > 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 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 >::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,int> m_new_announcements; + + std::set< + std::shared_ptr > m_repeated_announcements; + + /* Map of cluster to frame count */ + std::map< + std::shared_ptr,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 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 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 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(); + } + } } -- cgit v1.2.3