aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ConfigParser.cpp15
-rw-r--r--src/ConfigParser.h8
-rw-r--r--src/DabMultiplexer.cpp75
-rw-r--r--src/DabMultiplexer.h78
-rw-r--r--src/DabMux.cpp44
-rw-r--r--src/ManagementServer.cpp4
-rw-r--r--src/ManagementServer.h16
-rw-r--r--src/MuxElements.h18
8 files changed, 161 insertions, 97 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 7d166b6..2d500b3 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) 2022
+ Copyright (C) 2025
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
@@ -110,10 +110,10 @@ static void parse_fig2_label(ptree& pt, DabLabel& label) {
}
// Parse the linkage section
-static void parse_linkage(ptree& pt,
- std::shared_ptr<dabEnsemble> ensemble)
+void parse_linkage(
+ const boost::optional<boost::property_tree::ptree&> pt_linking,
+ std::vector<std::shared_ptr<LinkageSet> >& linkageSets)
{
- auto pt_linking = pt.get_child_optional("linking");
if (pt_linking) {
for (const auto& it : *pt_linking) {
const string setuid = it.first;
@@ -132,7 +132,7 @@ static void parse_linkage(ptree& pt,
string service_uid = pt_set.get("keyservice", "");
auto linkageset = make_shared<LinkageSet>(setuid, lsn, active, hard, international);
- linkageset->keyservice = service_uid; // TODO check if it exists
+ linkageset->keyservice = service_uid; // existence check is done in validate_linkage_sets()
auto pt_list = pt_set.get_child_optional("list");
@@ -189,7 +189,7 @@ static void parse_linkage(ptree& pt,
linkageset->id_list.push_back(link);
}
}
- ensemble->linkagesets.push_back(linkageset);
+ linkageSets.push_back(linkageset);
}
}
}
@@ -910,7 +910,8 @@ void parse_ptree(
}
- parse_linkage(pt, ensemble);
+ const auto pt_linking = pt.get_child_optional("linking");
+ parse_linkage(pt_linking, ensemble->linkagesets);
parse_freq_info(pt, ensemble);
parse_other_service_linking(pt, ensemble);
}
diff --git a/src/ConfigParser.h b/src/ConfigParser.h
index 9ca6c81..038247b 100644
--- a/src/ConfigParser.h
+++ b/src/ConfigParser.h
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2016
+ Copyright (C) 2025
Matthias P. Braendli, matthias.braendli@mpb.li
The Configuration parser sets up the ensemble according
@@ -34,6 +34,10 @@
#include <boost/property_tree/ptree.hpp>
#include <memory>
-void parse_ptree(boost::property_tree::ptree& pt,
+void parse_ptree(
+ boost::property_tree::ptree& pt,
std::shared_ptr<dabEnsemble> ensemble);
+void parse_linkage(
+ const boost::optional<boost::property_tree::ptree&> pt_linking,
+ std::vector<std::shared_ptr<LinkageSet> >& linkageSets);
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
index c665f2c..7a8ac97 100644
--- a/src/DabMultiplexer.cpp
+++ b/src/DabMultiplexer.cpp
@@ -26,6 +26,9 @@
#include <cmath>
#include <set>
#include <memory>
+#include <boost/property_tree/info_parser.hpp>
+#include <boost/property_tree/json_parser.hpp>
+
#include "DabMultiplexer.h"
#include "ConfigParser.h"
#include "ManagementServer.h"
@@ -132,15 +135,35 @@ std::pair<uint32_t, std::time_t> MuxTime::get_milliseconds_seconds()
}
-DabMultiplexer::DabMultiplexer(boost::property_tree::ptree pt) :
+void DabMultiplexerConfig::read(const std::string& filename)
+{
+ m_config_file = "";
+ try {
+ if (stringEndsWith(filename, ".json")) {
+ read_json(filename, pt);
+ }
+ else {
+ read_info(filename, pt);
+ }
+ m_config_file = filename;
+ }
+ catch (const boost::property_tree::file_parser_error& e)
+ {
+ etiLog.level(warn) << "Failed to read " << filename;
+ }
+}
+
+DabMultiplexer::DabMultiplexer(DabMultiplexerConfig& config) :
RemoteControllable("mux"),
- m_pt(pt),
+ m_config(config),
m_time(),
ensemble(std::make_shared<dabEnsemble>()),
- m_clock_tai(split_pipe_separated_string(pt.get("general.tai_clock_bulletins", ""))),
+ m_clock_tai(split_pipe_separated_string(m_config.pt.get("general.tai_clock_bulletins", ""))),
fig_carousel(ensemble, [&]() { return m_time.get_milliseconds_seconds(); })
{
RC_ADD_PARAMETER(frames, "Show number of frames generated [read-only]");
+ RC_ADD_PARAMETER(tist_offset, "Configured tist-offset");
+ RC_ADD_PARAMETER(reload_linkagesets, "Write 1 to this parameter to trigger a reload of the linkage sets from the config [write-only]");
rcs.enrol(&m_clock_tai);
}
@@ -160,7 +183,7 @@ void DabMultiplexer::set_edi_config(const edi::configuration_t& new_edi_conf)
// Run a set of checks on the configuration
void DabMultiplexer::prepare(bool require_tai_clock)
{
- parse_ptree(m_pt, ensemble);
+ parse_ptree(m_config.pt, ensemble);
rcs.enrol(this);
rcs.enrol(ensemble.get());
@@ -186,11 +209,11 @@ void DabMultiplexer::prepare(bool require_tai_clock)
throw MuxInitException();
}
- const uint32_t tist_at_fct0_ms = m_pt.get<double>("general.tist_at_fct0", 0);
- currentFrame = m_time.init(tist_at_fct0_ms, m_pt.get<double>("general.tist_offset", 0.0));
+ const uint32_t tist_at_fct0_ms = m_config.pt.get<double>("general.tist_at_fct0", 0);
+ currentFrame = m_time.init(tist_at_fct0_ms, m_config.pt.get<double>("general.tist_offset", 0.0));
m_time.mnsc_increment_time = false;
- bool tist_enabled = m_pt.get("general.tist", false);
+ bool tist_enabled = m_config.pt.get("general.tist", false);
auto tist_edi_time = m_time.get_tist_seconds();
const auto timestamp = tist_edi_time.first;
@@ -439,6 +462,32 @@ void DabMultiplexer::prepare_data_inputs()
}
}
+void DabMultiplexer::reload_linkagesets()
+{
+ try {
+ DabMultiplexerConfig new_conf;
+ new_conf.read(m_config.config_file());
+ if (new_conf.valid()) {
+ const auto pt_linking = new_conf.pt.get_child_optional("linking");
+ std::vector<std::shared_ptr<LinkageSet> > linkagesets;
+ parse_linkage(pt_linking, linkagesets);
+
+ etiLog.level(info) << "Validating " << linkagesets.size() << " new linkage sets.";
+
+ if (ensemble->validate_linkage_sets(ensemble->services, linkagesets)) {
+ ensemble->linkagesets = linkagesets;
+ etiLog.level(info) << "Loaded new linkage sets.";
+ }
+ else {
+ etiLog.level(warn) << "New linkage set validation failed";
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ etiLog.level(warn) << "Failed to update linkage sets: " << e.what();
+ }
+}
/* Each call creates one ETI frame */
void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs)
@@ -458,7 +507,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
// The above Tag Items will be assembled into a TAG Packet
edi::TagPacket edi_tagpacket(edi_conf.tagpacket_alignment);
- const bool tist_enabled = m_pt.get("general.tist", false);
+ const bool tist_enabled = m_config.pt.get("general.tist", false);
int tai_utc_offset = 0;
if (tist_enabled and m_tai_clock_required) {
@@ -718,7 +767,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
index = (FLtmp + 2 + 1) * 4;
eti_TIST *tist = (eti_TIST *) & etiFrame[index];
- bool enableTist = m_pt.get("general.tist", false);
+ bool enableTist = m_config.pt.get("general.tist", false);
if (enableTist) {
tist->TIST = htonl(timestamp) | 0xff;
edi_tagDETI.tsta = timestamp & 0xffffff;
@@ -848,6 +897,9 @@ void DabMultiplexer::set_parameter(const std::string& parameter,
else if (parameter == "tist_offset") {
m_time.set_tist_offset(std::stod(value));
}
+ else if (parameter == "reload_linkagesets") {
+ reload_linkagesets();
+ }
else {
stringstream ss;
ss << "Parameter '" << parameter <<
@@ -866,6 +918,11 @@ const std::string DabMultiplexer::get_parameter(const std::string& parameter) co
else if (parameter == "tist_offset") {
ss << m_time.tist_offset();
}
+ else if (parameter == "reload_linkagesets") {
+ ss << "Parameter '" << parameter <<
+ "' is not write-only in controllable " << get_rc_name();
+ throw ParameterError(ss.str());
+ }
else {
ss << "Parameter '" << parameter <<
"' is not exported by controllable " << get_rc_name();
diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h
index 9306eed..620e65d 100644
--- a/src/DabMultiplexer.h
+++ b/src/DabMultiplexer.h
@@ -45,47 +45,59 @@ constexpr uint32_t ETI_FSYNC1 = 0x49C5F8;
class MuxTime {
private:
- std::time_t m_edi_time = 0;
- uint32_t m_pps_offset_ms = 0;
- int64_t m_tist_offset_ms = 0;
+ std::time_t m_edi_time = 0;
+ uint32_t m_pps_offset_ms = 0;
+ int64_t m_tist_offset_ms = 0;
public:
- std::pair<uint32_t, std::time_t> get_tist_seconds();
- std::pair<uint32_t, std::time_t> get_milliseconds_seconds();
-
-
- /* Pre v3 odr-dabmux did the MNSC calculation differently,
- * which works with the easydabv2. The rework in odr-dabmux,
- * deriving MNSC time from EDI time broke this.
- *
- * That's why we're now tracking MNSC time in separate variables,
- * to get the same behaviour back.
- *
- * I'm not aware of any devices using MNSC time besides the
- * easydab. ODR-DabMod now considers EDI seconds or ZMQ metadata.
- */
- bool mnsc_increment_time = false;
- std::time_t mnsc_time = 0;
-
- /* Setup the time and return the initial currentFrame counter value */
- uint64_t init(uint32_t tist_at_fct0_ms, double tist_offset);
- void increment_timestamp();
- double tist_offset() const { return m_tist_offset_ms / 1000.0; }
- void set_tist_offset(double new_tist_offset);
+ std::pair<uint32_t, std::time_t> get_tist_seconds();
+ std::pair<uint32_t, std::time_t> get_milliseconds_seconds();
+
+
+ /* Pre v3 odr-dabmux did the MNSC calculation differently,
+ * which works with the easydabv2. The rework in odr-dabmux,
+ * deriving MNSC time from EDI time broke this.
+ *
+ * That's why we're now tracking MNSC time in separate variables,
+ * to get the same behaviour back.
+ *
+ * I'm not aware of any devices using MNSC time besides the
+ * easydab. ODR-DabMod now considers EDI seconds or ZMQ metadata.
+ */
+ bool mnsc_increment_time = false;
+ std::time_t mnsc_time = 0;
+
+ /* Setup the time and return the initial currentFrame counter value */
+ uint64_t init(uint32_t tist_at_fct0_ms, double tist_offset);
+ void increment_timestamp();
+ double tist_offset() const { return m_tist_offset_ms / 1000.0; }
+ void set_tist_offset(double new_tist_offset);
+};
+
+class DabMultiplexerConfig {
+ public:
+ boost::property_tree::ptree pt;
+
+ void read(const std::string& filename);
+ bool valid() const { return m_config_file != ""; }
+ std::string config_file() const { return m_config_file; }
+
+ private:
+ std::string m_config_file;
};
class DabMultiplexer : public RemoteControllable {
public:
- DabMultiplexer(boost::property_tree::ptree pt);
+ DabMultiplexer(DabMultiplexerConfig& config);
DabMultiplexer(const DabMultiplexer& other) = delete;
DabMultiplexer& operator=(const DabMultiplexer& other) = delete;
- ~DabMultiplexer();
+ virtual ~DabMultiplexer();
void prepare(bool require_tai_clock);
void mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs);
- void print_info(void);
+ void print_info();
void set_edi_config(const edi::configuration_t& new_edi_conf);
@@ -99,11 +111,13 @@ class DabMultiplexer : public RemoteControllable {
virtual const json::map_t get_all_values() const;
private:
- void prepare_subchannels(void);
- void prepare_services_components(void);
- void prepare_data_inputs(void);
+ void prepare_subchannels();
+ void prepare_services_components();
+ void prepare_data_inputs();
+
+ void reload_linkagesets();
- boost::property_tree::ptree m_pt;
+ DabMultiplexerConfig& m_config;
MuxTime m_time;
uint64_t currentFrame = 0;
diff --git a/src/DabMux.cpp b/src/DabMux.cpp
index 0066629..7b5f5d6 100644
--- a/src/DabMux.cpp
+++ b/src/DabMux.cpp
@@ -31,8 +31,6 @@
#include <memory>
#include <boost/property_tree/ptree.hpp>
-#include <boost/property_tree/info_parser.hpp>
-#include <boost/property_tree/json_parser.hpp>
#include <ctime>
#include <cstdlib>
#include <cstdio>
@@ -132,12 +130,13 @@ int main(int argc, char *argv[])
}
int returnCode = 0;
- ptree pt;
std::vector<std::shared_ptr<DabOutput> > outputs;
try {
string conf_file = "";
+ DabMultiplexerConfig mux_conf;
+
if (argc == 2) { // Assume the only argument is a config file
conf_file = argv[1];
@@ -154,8 +153,7 @@ int main(int argc, char *argv[])
}
conf_file = argv[2];
-
- read_info(conf_file, pt);
+ mux_conf.read(conf_file);
}
catch (runtime_error &e) {
throw MuxInitException(e.what());
@@ -168,23 +166,18 @@ int main(int argc, char *argv[])
}
try {
- if (stringEndsWith(conf_file, ".json")) {
- read_json(conf_file, pt);
- }
- else {
- read_info(conf_file, pt);
- }
+ mux_conf.read(conf_file);
}
catch (runtime_error &e) {
throw MuxInitException(e.what());
}
/* Enable Logging to syslog conditionally */
- if (pt.get<bool>("general.syslog", false)) {
+ if (mux_conf.pt.get<bool>("general.syslog", false)) {
etiLog.register_backend(std::make_shared<LogToSyslog>());
}
- const auto startupcheck = pt.get<string>("general.startupcheck", "");
+ const auto startupcheck = mux_conf.pt.get<string>("general.startupcheck", "");
if (not startupcheck.empty()) {
etiLog.level(info) << "Running startup check '" << startupcheck << "'";
int wstatus = system(startupcheck.c_str());
@@ -204,26 +197,26 @@ int main(int argc, char *argv[])
}
}
- int mgmtserverport = pt.get<int>("general.managementport",
- pt.get<int>("general.statsserverport", 0) );
+ int mgmtserverport = mux_conf.pt.get<int>("general.managementport",
+ mux_conf.pt.get<int>("general.statsserverport", 0) );
/* Management: stats and config server */
get_mgmt_server().open(mgmtserverport);
/************** READ REMOTE CONTROL PARAMETERS *************/
- int telnetport = pt.get<int>("remotecontrol.telnetport", 0);
+ int telnetport = mux_conf.pt.get<int>("remotecontrol.telnetport", 0);
if (telnetport != 0) {
auto rc = std::make_shared<RemoteControllerTelnet>(telnetport);
rcs.add_controller(rc);
}
- auto zmqendpoint = pt.get<string>("remotecontrol.zmqendpoint", "");
+ auto zmqendpoint = mux_conf.pt.get<string>("remotecontrol.zmqendpoint", "");
if (not zmqendpoint.empty()) {
auto rc = std::make_shared<RemoteControllerZmq>(zmqendpoint);
rcs.add_controller(rc);
}
- DabMultiplexer mux(pt);
+ DabMultiplexer mux(mux_conf);
etiLog.level(info) <<
PACKAGE_NAME << " " <<
@@ -240,7 +233,7 @@ int main(int argc, char *argv[])
/******************** READ OUTPUT PARAMETERS ***************/
set<string> all_output_names;
bool output_require_tai_clock = false;
- ptree pt_outputs = pt.get_child("outputs");
+ ptree pt_outputs = mux_conf.pt.get_child("outputs");
for (auto ptree_pair : pt_outputs) {
string outputuid = ptree_pair.first;
@@ -444,7 +437,6 @@ int main(int argc, char *argv[])
}
outputs.push_back(output);
-
}
}
@@ -464,7 +456,7 @@ int main(int argc, char *argv[])
edi_conf.print();
}
- size_t limit = pt.get("general.nbframes", 0);
+ const size_t limit = mux_conf.pt.get("general.nbframes", 0);
etiLog.level(info) << "Start loop";
/* Each iteration of the main loop creates one ETI frame */
@@ -473,6 +465,7 @@ int main(int argc, char *argv[])
mux.mux_frame(outputs);
if (limit && currentFrame >= limit) {
+ etiLog.level(info) << "Max number of ETI frames reached: " << currentFrame;
break;
}
@@ -491,17 +484,12 @@ int main(int argc, char *argv[])
mgmt_server.restart();
}
- mgmt_server.update_ptree(pt);
+ mgmt_server.update_ptree(mux_conf.pt);
}
}
-
- if (limit) {
- etiLog.level(info) << "Max number of ETI frames reached: " << currentFrame;
- }
}
catch (const MuxInitException& except) {
- etiLog.level(error) << "Multiplex initialisation aborted: " <<
- except.what();
+ etiLog.level(error) << "Multiplex initialisation aborted: " << except.what();
returnCode = 1;
}
catch (const std::invalid_argument& except) {
diff --git a/src/ManagementServer.cpp b/src/ManagementServer.cpp
index 2c25a7a..7344b8b 100644
--- a/src/ManagementServer.cpp
+++ b/src/ManagementServer.cpp
@@ -473,7 +473,7 @@ void InputStat::notifyPeakLevels(int peak_left, int peak_right)
}
}
-void InputStat::notifyUnderrun(void)
+void InputStat::notifyUnderrun()
{
unique_lock<mutex> lock(m_mutex);
@@ -492,7 +492,7 @@ void InputStat::notifyUnderrun(void)
}
}
-void InputStat::notifyOverrun(void)
+void InputStat::notifyOverrun()
{
unique_lock<mutex> lock(m_mutex);
diff --git a/src/ManagementServer.h b/src/ManagementServer.h
index d328f88..93ad28c 100644
--- a/src/ManagementServer.h
+++ b/src/ManagementServer.h
@@ -93,20 +93,20 @@ class InputStat
InputStat(const InputStat& other) = delete;
InputStat& operator=(const InputStat& other) = delete;
~InputStat();
- void registerAtServer(void);
+ void registerAtServer();
- std::string get_name(void) const { return m_name; }
+ std::string get_name() const { return m_name; }
/* This function is called for every frame read by
* the multiplexer */
void notifyBuffer(long bufsize);
void notifyTimestampOffset(double offset);
void notifyPeakLevels(int peak_left, int peak_right);
- void notifyUnderrun(void);
- void notifyOverrun(void);
+ void notifyUnderrun();
+ void notifyOverrun();
void notifyVersion(const std::string& version, uint32_t uptime_s);
- std::string encodeValuesJSON(void);
- input_state_t determineState(void);
+ std::string encodeValuesJSON();
+ input_state_t determineState();
private:
std::string m_name;
@@ -183,7 +183,7 @@ class ManagementServer
void update_ptree(const boost::property_tree::ptree& pt);
bool fault_detected() const { return m_fault; }
- void restart(void);
+ void restart();
private:
void restart_thread(long);
@@ -192,7 +192,7 @@ class ManagementServer
zmq::context_t m_zmq_context;
zmq::socket_t m_zmq_sock;
- void serverThread(void);
+ void serverThread();
void handle_message(zmq::message_t& zmq_message);
bool isInputRegistered(std::string& id);
diff --git a/src/MuxElements.h b/src/MuxElements.h
index 0266671..dfc4380 100644
--- a/src/MuxElements.h
+++ b/src/MuxElements.h
@@ -84,7 +84,7 @@ class MuxInitException : public std::exception
MuxInitException(const std::string m = "ODR-DabMux initialisation error")
throw()
: msg(m) {}
- ~MuxInitException(void) throw() {}
+ ~MuxInitException() throw() {}
const char* what() const throw() { return msg.c_str(); }
private:
std::string msg;
@@ -137,12 +137,12 @@ class AnnouncementCluster : public RemoteControllable {
uint16_t flags = 0;
std::string subchanneluid;
- std::string tostring(void) const;
+ std::string tostring() const;
/* Check if the activation/deactivation timeout occurred,
* and return of if the Announcement is active
*/
- bool is_active(void);
+ bool is_active();
private:
mutable std::mutex m_active_mutex;
@@ -372,7 +372,7 @@ struct dabProtectionEEP {
// select EEP profile A and B.
// Other values are for future use, see
// EN 300 401 Clause 6.2.1 "Basic sub-channel organisation"
- uint8_t GetOption(void) const {
+ uint8_t GetOption() const {
return (this->profile == EEP_A) ? 0 : 1;
}
};
@@ -402,16 +402,16 @@ public:
protection() { }
// Calculate subchannel size in number of CU
- unsigned short getSizeCu(void) const;
+ unsigned short getSizeCu() const;
// Calculate subchannel size in number of bytes
- unsigned short getSizeByte(void) const;
+ unsigned short getSizeByte() const;
// Calculate subchannel size in number of uint32_t
- unsigned short getSizeWord(void) const;
+ unsigned short getSizeWord() const;
// Calculate subchannel size in number of uint64_t
- unsigned short getSizeDWord(void) const;
+ unsigned short getSizeDWord() const;
// Read from the input, using the correct buffer management
size_t readFrame(uint8_t *buffer, size_t size, std::time_t seconds, int utco, uint32_t tsta);
@@ -574,7 +574,7 @@ class LinkageSet {
bool hard,
bool international);
- std::string get_name(void) const { return m_name; }
+ std::string get_name() const { return m_name; }
std::list<ServiceLink> id_list;