aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/example.mux14
-rw-r--r--src/ConfigParser.cpp96
-rw-r--r--src/DabMultiplexer.h8
-rw-r--r--src/MuxElements.cpp47
-rw-r--r--src/MuxElements.h30
-rw-r--r--src/fig/FIG0.cpp182
-rw-r--r--src/fig/FIG0.h23
-rw-r--r--src/fig/FIGCarousel.cpp4
-rw-r--r--src/fig/FIGCarousel.h1
-rw-r--r--src/utils.cpp8
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<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();
+ }
+ }
}