aboutsummaryrefslogtreecommitdiffstats
path: root/src/DabMultiplexer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/DabMultiplexer.cpp')
-rw-r--r--src/DabMultiplexer.cpp196
1 files changed, 121 insertions, 75 deletions
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
index e6e6782..7a8ac97 100644
--- a/src/DabMultiplexer.cpp
+++ b/src/DabMultiplexer.cpp
@@ -26,11 +26,15 @@
#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"
#include "crc.h"
#include "utils.h"
+#include "Eti.h"
using namespace std;
@@ -47,22 +51,21 @@ static vector<string> split_pipe_separated_string(const std::string& s)
return components;
}
-uint64_t MuxTime::init(uint32_t tist_at_fct0_us)
+uint64_t MuxTime::init(uint32_t tist_at_fct0_ms, double tist_offset)
{
- m_tist_at_fct0_us = tist_at_fct0_us;
-
- /* At startup, derive edi_time, TIST and CIF count such that there is
- * a consistency across mux restarts. Ensure edi_time and TIST represent
- * current time.
- *
- * FCT and DLFC are directly derived from m_currentFrame.
- * Every 6s, FCT overflows. DLFC overflows at 5000 every 120s.
- *
- * Keep a granularity of 24ms, which corresponds to the duration of an ETI
- * frame, to get nicer timestamps.
- */
+ // Things we must guarantee, up to granularity of 24ms:
+ // Difference between current time and EDI time = tist_offset
+ // TIST of frame 0 = tist_at_fct0_ms
+ // In order to achieve the second, we calculate the initial
+ // counter value so that FCT0 corresponds to the desired TIST.
+ //
+ // Changing the tist_offset at runtime will throw off the TIST@FCT0 value
+ m_tist_offset_ms = std::lround(tist_offset * 1000);
+
using Sec = chrono::seconds;
- const auto now = chrono::system_clock::now();
+ const auto now = chrono::system_clock::now() +
+ chrono::milliseconds(std::lround(tist_offset * 1000.0));
+
const auto offset = now - chrono::time_point_cast<Sec>(now);
if (offset >= chrono::seconds(1)) {
throw std::logic_error("Invalid startup offset calculation for TIST! " +
@@ -70,43 +73,24 @@ uint64_t MuxTime::init(uint32_t tist_at_fct0_us)
" ms");
}
const time_t t_now = chrono::system_clock::to_time_t(chrono::time_point_cast<Sec>(now));
+ const auto offset_ms = chrono::duration_cast<chrono::milliseconds>(offset).count();
- m_edi_time = t_now - (t_now % 6);
- uint64_t currentFrame = 0;
- time_t edi_time_at_cif0 = t_now - (t_now % 120);
- while (edi_time_at_cif0 < m_edi_time) {
- edi_time_at_cif0 += 6;
- currentFrame += 250;
- }
+ m_edi_time = t_now;
+ m_pps_offset_ms = std::lround(offset_ms / 24.0) * 24;
- if (edi_time_at_cif0 != m_edi_time) {
- throw std::logic_error("Invalid startup offset calculation for CIF!");
- }
+ const auto counter_offset = tist_at_fct0_ms / 24;
+ const auto offset_as_count = m_pps_offset_ms / 24;
- int64_t offset_ms = chrono::duration_cast<chrono::milliseconds>(offset).count();
- offset_ms += 1000 * (t_now - m_edi_time);
-
- if (tist_at_fct0_us >= 1000000) {
- etiLog.level(error) << "tist_at_fct0 may not be larger than 1s";
- throw MuxInitException();
- }
-
- m_timestamp = (uint64_t)tist_at_fct0_us * 16384 / 1000;
- while (offset_ms >= 24) {
- increment_timestamp();
- currentFrame++;
- offset_ms -= 24;
- }
- return currentFrame;
+ return (250 - counter_offset + offset_as_count) % 250;
}
constexpr int TIMESTAMP_LEVEL_2_SHIFT = 14;
void MuxTime::increment_timestamp()
{
- m_timestamp += 24 << TIMESTAMP_LEVEL_2_SHIFT; // Shift 24ms by 14 to Timestamp level 2
- if (m_timestamp > 0xf9FFff) {
- m_timestamp -= 0xfa0000; // Subtract 16384000, corresponding to one second
+ m_pps_offset_ms += 24;
+ if (m_pps_offset_ms >= 1000) {
+ m_pps_offset_ms -= 1000;
m_edi_time += 1;
// Also update MNSC time for next time FP==0
@@ -114,27 +98,34 @@ void MuxTime::increment_timestamp()
}
}
-std::pair<uint32_t, std::time_t> MuxTime::get_tist_seconds()
+void MuxTime::set_tist_offset(double new_tist_offset)
{
- // The user-visible configuration tist_offset is the effective
- // offset, but since we implicitly add the tist_at_fct0 to it,
- // we must compensate.
- double corrected_tist_offset = tist_offset - (m_tist_at_fct0_us / 1e6);
-
- // negative tist_offset not supported, because the calculation is annoying
- if (corrected_tist_offset < 0) return {m_timestamp, m_edi_time};
-
- double fractional_part = corrected_tist_offset - std::floor(corrected_tist_offset);
- const size_t steps = std::lround(std::floor(fractional_part / 24e-3));
- uint32_t timestamp = m_timestamp + (24 << TIMESTAMP_LEVEL_2_SHIFT) * steps;
-
- std::time_t edi_time = m_edi_time + std::lround(std::floor(corrected_tist_offset));
-
- if (timestamp > 0xf9FFff) {
- edi_time += 1;
+ const int32_t new_tist_offset_ms = std::lround(new_tist_offset * 1000.0);
+ int32_t delta = m_tist_offset_ms - new_tist_offset_ms;
+ if (delta > 0) {
+ while (delta > 0) {
+ increment_timestamp();
+ delta -= 24;
+ }
}
+ else if (delta < 0) {
+ while (delta < 0) {
+ m_edi_time -= 1;
+ delta += 1000;
+ }
+ // compensate the we subtracted too much
+ while (delta > 0) {
+ increment_timestamp();
+ delta -= 24;
+ }
+ }
+ m_tist_offset_ms = new_tist_offset_ms;
+}
- return {timestamp % 0xfa0000, edi_time};
+std::pair<uint32_t, std::time_t> MuxTime::get_tist_seconds()
+{
+ auto timestamp = m_pps_offset_ms * 16384;
+ return {timestamp % 0xfa0000, m_edi_time};
}
std::pair<uint32_t, std::time_t> MuxTime::get_milliseconds_seconds()
@@ -144,16 +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, "Timestamp offset in fractional number of seconds");
+ 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);
}
@@ -173,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());
@@ -199,12 +209,11 @@ void DabMultiplexer::prepare(bool require_tai_clock)
throw MuxInitException();
}
- const uint32_t tist_at_fct0_us = m_pt.get<double>("general.tist_at_fct0", 0);
- currentFrame = m_time.init(tist_at_fct0_us);
+ 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);
- m_time.tist_offset = m_pt.get<double>("general.tist_offset", 0.0);
+ 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;
@@ -453,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)
@@ -472,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) {
@@ -487,6 +522,10 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
auto tist_edi_time = m_time.get_tist_seconds();
const auto timestamp = tist_edi_time.first;
const auto edi_time = tist_edi_time.second;
+ /*
+ etiLog.level(debug) << "Frame " << currentFrame << " " << edi_time <<
+ " + " << (timestamp >> TIMESTAMP_LEVEL_2_SHIFT);
+ */
// Initialise the ETI frame
memset(etiFrame, 0, 6144);
@@ -520,7 +559,6 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
eti_FC *fc = (eti_FC *) &etiFrame[4];
//****** FCT ******//
- // Incremente for each frame, overflows at 249
fc->FCT = currentFrame % 250;
edi_tagDETI.dlfc = currentFrame % 5000;
@@ -729,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;
@@ -857,7 +895,10 @@ void DabMultiplexer::set_parameter(const std::string& parameter,
throw ParameterError(ss.str());
}
else if (parameter == "tist_offset") {
- m_time.tist_offset = std::stod(value);
+ m_time.set_tist_offset(std::stod(value));
+ }
+ else if (parameter == "reload_linkagesets") {
+ reload_linkagesets();
}
else {
stringstream ss;
@@ -875,7 +916,12 @@ const std::string DabMultiplexer::get_parameter(const std::string& parameter) co
ss << currentFrame;
}
else if (parameter == "tist_offset") {
- ss << m_time.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 <<
@@ -890,7 +936,7 @@ const json::map_t DabMultiplexer::get_all_values() const
{
json::map_t map;
map["frames"].v = currentFrame;
- map["tist_offset"].v = m_time.tist_offset;
+ map["tist_offset"].v = m_time.tist_offset();
return map;
}