aboutsummaryrefslogtreecommitdiffstats
path: root/src/DabMultiplexer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/DabMultiplexer.cpp')
-rw-r--r--src/DabMultiplexer.cpp232
1 files changed, 144 insertions, 88 deletions
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
index b1f2c75..52f053a 100644
--- a/src/DabMultiplexer.cpp
+++ b/src/DabMultiplexer.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2024
+ Copyright (C) 2025
Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
@@ -23,11 +23,14 @@
along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <cmath>
#include <set>
#include <memory>
#include "DabMultiplexer.h"
#include "ConfigParser.h"
-#include "fig/FIG.h"
+#include "ManagementServer.h"
+#include "crc.h"
+#include "utils.h"
using namespace std;
@@ -44,16 +47,101 @@ static vector<string> split_pipe_separated_string(const std::string& s)
return components;
}
-DabMultiplexer::DabMultiplexer(
- boost::property_tree::ptree pt) :
+uint64_t MuxTime::init(uint32_t tist_at_fct0_ms, double tist_offset)
+{
+ // 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() +
+ 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! " +
+ to_string(chrono::duration_cast<chrono::milliseconds>(offset).count()) +
+ " 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;
+ m_pps_offset_ms = std::lround(offset_ms / 24.0) * 24;
+
+ const auto counter_offset = tist_at_fct0_ms / 24;
+ const auto offset_as_count = m_pps_offset_ms / 24;
+
+ etiLog.level(debug) << "Init " << counter_offset << " " << offset_as_count;
+
+ return (250 - counter_offset + offset_as_count) % 250;
+}
+
+constexpr int TIMESTAMP_LEVEL_2_SHIFT = 14;
+
+void MuxTime::increment_timestamp()
+{
+ 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
+ mnsc_increment_time = true;
+ }
+}
+
+void MuxTime::set_tist_offset(double new_tist_offset)
+{
+ 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;
+}
+
+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()
+{
+ auto tist_seconds = get_tist_seconds();
+ return {tist_seconds.first >> TIMESTAMP_LEVEL_2_SHIFT, tist_seconds.second};
+}
+
+
+DabMultiplexer::DabMultiplexer(boost::property_tree::ptree pt) :
RemoteControllable("mux"),
m_pt(pt),
+ m_time(),
ensemble(std::make_shared<dabEnsemble>()),
m_clock_tai(split_pipe_separated_string(pt.get("general.tai_clock_bulletins", ""))),
- fig_carousel(ensemble)
+ 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 integral number of seconds");
rcs.enrol(&m_clock_tai);
}
@@ -99,58 +187,23 @@ void DabMultiplexer::prepare(bool require_tai_clock)
throw MuxInitException();
}
- /* 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.
- */
- using Sec = chrono::seconds;
- const auto now = chrono::system_clock::now();
- const time_t t_now = chrono::system_clock::to_time_t(chrono::time_point_cast<Sec>(now));
-
- m_edi_time = t_now - (t_now % 6);
- m_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;
- m_currentFrame += 250;
- }
-
- if (edi_time_at_cif0 != m_edi_time) {
- throw std::logic_error("Invalid startup offset calculation for CIF!");
- }
-
- 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! " +
- to_string(chrono::duration_cast<chrono::milliseconds>(offset).count()) +
- " ms");
- }
+ 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));
+ m_time.mnsc_increment_time = false;
- int64_t offset_ms = chrono::duration_cast<chrono::milliseconds>(offset).count();
- offset_ms += 1000 * (t_now - m_edi_time);
+ bool tist_enabled = m_pt.get("general.tist", false);
- m_timestamp = 0;
- while (offset_ms >= 24) {
- increment_timestamp();
- m_currentFrame++;
- offset_ms -= 24;
- }
+ auto tist_edi_time = m_time.get_tist_seconds();
+ const auto timestamp = tist_edi_time.first;
+ const auto edi_time = tist_edi_time.second;
+ m_time.mnsc_time = edi_time;
etiLog.log(info, "Startup CIF Count %i with timestamp: %d + %f",
- m_currentFrame, m_edi_time,
- (m_timestamp & 0xFFFFFF) / 16384000.0);
+ currentFrame, edi_time,
+ (timestamp & 0xFFFFFF) / 16384000.0);
// Try to load offset once
- bool tist_enabled = m_pt.get("general.tist", false);
- m_tist_offset = m_pt.get<int>("general.tist_offset", 0);
-
m_tai_clock_required = (tist_enabled and edi_conf.enabled()) or require_tai_clock;
if (m_tai_clock_required) {
@@ -387,14 +440,6 @@ void DabMultiplexer::prepare_data_inputs()
}
}
-void DabMultiplexer::increment_timestamp()
-{
- m_timestamp += 24 << 14; // Shift 24ms by 14 to Timestamp level 2
- if (m_timestamp > 0xf9FFff) {
- m_timestamp -= 0xfa0000; // Substract 16384000, corresponding to one second
- m_edi_time += 1;
- }
-}
/* Each call creates one ETI frame */
void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs)
@@ -425,9 +470,14 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
etiLog.level(error) << "Could not get UTC-TAI offset for EDI timestamp";
}
}
- update_dab_time();
- const auto edi_time = m_edi_time + m_tist_offset;
+ 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);
@@ -443,7 +493,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
//****** Field FSYNC *****//
// See ETS 300 799, 6.2.1.2
- if ((m_currentFrame & 1) == 0) {
+ if ((currentFrame & 1) == 0) {
etiSync->FSYNC = ETI_FSYNC1;
}
else {
@@ -461,9 +511,8 @@ 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 = m_currentFrame % 250;
- edi_tagDETI.dlfc = m_currentFrame % 5000;
+ fc->FCT = currentFrame % 250;
+ edi_tagDETI.dlfc = currentFrame % 5000;
//****** FICF ******//
// Fast Information Channel Flag, 1 bit, =1 if FIC present
@@ -480,7 +529,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
/* Frame Phase, 3 bit counter, tells the COFDM generator
* when to insert the TII. Is also used by the MNSC.
*/
- fc->FP = edi_tagDETI.fp = m_currentFrame & 0x7;
+ fc->FP = edi_tagDETI.fp = currentFrame & 0x7;
//****** MID ******//
//Mode Identity, 2 bits, 01 ModeI, 10 modeII, 11 ModeIII, 00 ModeIV
@@ -554,14 +603,9 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
eoh->MNSC = 0;
- if (fc->FP == 0) {
- // update the latched time only when FP==0 to ensure MNSC encodes
- // a consistent time
- m_edi_time_latched_for_mnsc = edi_time;
- }
-
struct tm time_tm;
- gmtime_r(&m_edi_time_latched_for_mnsc, &time_tm);
+ gmtime_r(&m_time.mnsc_time, &time_tm);
+
switch (fc->FP & 0x3) {
case 0:
{
@@ -571,6 +615,12 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
mnsc->identifier = 0;
mnsc->rfa = 0;
}
+
+ if (m_time.mnsc_increment_time)
+ {
+ m_time.mnsc_increment_time = false;
+ m_time.mnsc_time += 1;
+ }
break;
case 1:
{
@@ -613,7 +663,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
// Insert all FIBs
const bool fib3_present = (ensemble->transmission_mode == TransmissionMode_e::TM_III);
- index += fig_carousel.write_fibs(&etiFrame[index], m_currentFrame, fib3_present);
+ index += fig_carousel.write_fibs(&etiFrame[index], currentFrame, fib3_present);
/**********************************************************************
****** Input Data Reading *******************************************
@@ -625,12 +675,13 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
int sizeSubchannel = subchannel->getSizeByte();
// no need to check enableTist because we always increment the timestamp
int result = subchannel->readFrame(&etiFrame[index],
- sizeSubchannel, edi_time, tai_utc_offset, m_timestamp);
+ sizeSubchannel,
+ edi_time, tai_utc_offset, timestamp);
if (result < 0) {
etiLog.log(info,
"Subchannel %d read failed at ETI frame number: %d",
- subchannel->id, m_currentFrame);
+ subchannel->id, currentFrame);
}
// save pointer to Audio or Data Stream into correct TagESTn for EDI
@@ -670,8 +721,8 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
bool enableTist = m_pt.get("general.tist", false);
if (enableTist) {
- tist->TIST = htonl(m_timestamp) | 0xff;
- edi_tagDETI.tsta = m_timestamp & 0xffffff;
+ tist->TIST = htonl(timestamp) | 0xff;
+ edi_tagDETI.tsta = timestamp & 0xffffff;
}
else {
tist->TIST = htonl(0xffffff) | 0xff;
@@ -692,7 +743,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
output->setMetadata(md_edi_time);
shared_ptr<OutputMetadata> md_dlfc =
- make_shared<OutputMetadataDLFC>(m_currentFrame % 5000);
+ make_shared<OutputMetadataDLFC>(currentFrame % 5000);
output->setMetadata(md_dlfc);
}
}
@@ -708,8 +759,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
Approximate 8 ms 1 ms 3,91 us 488 ns 61 ns
time resolution
*/
-
- increment_timestamp();
+ m_time.increment_timestamp();
/**********************************************************************
*********** Section FRPD *****************************************
@@ -751,6 +801,12 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
}
edi_sender->write(edi_tagpacket);
+
+ for (const auto& stat : edi_sender->get_tcp_server_stats()) {
+ get_mgmt_server().update_edi_tcp_output_stat(
+ stat.listen_port,
+ stat.stats.size());
+ }
}
#if _DEBUG
@@ -761,7 +817,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
if (enableTist) {
etiLog.log(info, "ETI frame number %i Timestamp: %d + %f",
m_currentFrame, edi_time,
- (m_timestamp & 0xFFFFFF) / 16384000.0);
+ (timestamp & 0xFFFFFF) / 16384000.0);
}
else {
etiLog.log(info, "ETI frame number %i Time: %d, no TIST",
@@ -770,7 +826,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
}
#endif
- m_currentFrame++;
+ currentFrame++;
}
void DabMultiplexer::print_info()
@@ -791,7 +847,7 @@ void DabMultiplexer::set_parameter(const std::string& parameter,
throw ParameterError(ss.str());
}
else if (parameter == "tist_offset") {
- m_tist_offset = std::stoi(value);
+ m_time.set_tist_offset(std::stod(value));
}
else {
stringstream ss;
@@ -806,10 +862,10 @@ const std::string DabMultiplexer::get_parameter(const std::string& parameter) co
{
stringstream ss;
if (parameter == "frames") {
- ss << m_currentFrame;
+ ss << currentFrame;
}
else if (parameter == "tist_offset") {
- ss << m_tist_offset;
+ ss << m_time.tist_offset();
}
else {
ss << "Parameter '" << parameter <<
@@ -823,8 +879,8 @@ const std::string DabMultiplexer::get_parameter(const std::string& parameter) co
const json::map_t DabMultiplexer::get_all_values() const
{
json::map_t map;
- map["frames"].v = m_currentFrame;
- map["tist_offset"].v = m_tist_offset;
+ map["frames"].v = currentFrame;
+ map["tist_offset"].v = m_time.tist_offset();
return map;
}