diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ConfigParser.cpp | 11 | ||||
-rw-r--r-- | src/DabMultiplexer.cpp | 206 | ||||
-rw-r--r-- | src/DabMultiplexer.h | 85 | ||||
-rw-r--r-- | src/fig/FIG0structs.h | 18 | ||||
-rw-r--r-- | src/utils.cpp | 1 |
5 files changed, 165 insertions, 156 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 74e627b..7d166b6 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -36,16 +36,13 @@ # include "config.h" #endif -#include "dabOutput/dabOutput.h" #include "utils.h" -#include "DabMux.h" -#include "ManagementServer.h" #include "input/Edi.h" #include "input/Prbs.h" #include "input/Zmq.h" #include "input/File.h" #include "input/Udp.h" -#include "Eti.h" +#include "fig/FIG0structs.h" #include <boost/property_tree/ptree.hpp> #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> @@ -63,6 +60,12 @@ using namespace std; using boost::property_tree::ptree; using boost::property_tree::ptree_error; +constexpr uint16_t DEFAULT_DATA_BITRATE = 384; +constexpr uint16_t DEFAULT_PACKET_BITRATE = 32; + +constexpr uint32_t DEFAULT_SERVICE_ID = 50; + + static void setup_subchannel_from_ptree(shared_ptr<DabSubchannel>& subchan, const ptree &pt, std::shared_ptr<dabEnsemble> ensemble, diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp index bd1c909..e84bfbf 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,13 @@ 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 "crc.h" +#include "utils.h" using namespace std; @@ -44,8 +46,84 @@ static vector<string> split_pipe_separated_string(const std::string& s) return components; } -DabMultiplexer::DabMultiplexer( - boost::property_tree::ptree pt) : +uint64_t MuxTime::init() +{ + /* 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 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)); + + 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; + } + + if (edi_time_at_cif0 != m_edi_time) { + throw std::logic_error("Invalid startup offset calculation for CIF!"); + } + + int64_t offset_ms = chrono::duration_cast<chrono::milliseconds>(offset).count(); + offset_ms += 1000 * (t_now - m_edi_time); + + m_timestamp = 0; + while (offset_ms >= 24) { + increment_timestamp(); + currentFrame++; + offset_ms -= 24; + } + return currentFrame; +} + +void MuxTime::increment_timestamp() +{ + m_timestamp += 24 << 14; // Shift 24ms by 14 to Timestamp level 2 + if (m_timestamp > 0xf9FFff) { + m_timestamp -= 0xfa0000; // Subtract 16384000, corresponding to one second + m_edi_time += 1; + + // Also update MNSC time for next time FP==0 + mnsc_increment_time = true; + } +} + +std::pair<uint32_t, std::time_t> MuxTime::get_time() +{ + // negative tist_offset not supported, because the calculation is annoying + if (tist_offset < 0) return {m_timestamp, m_edi_time}; + + double fractional_part = tist_offset - std::floor(tist_offset); + const size_t steps = std::lround(std::floor(fractional_part / 24e-3)); + uint32_t timestamp = m_timestamp + (24 << 14) * steps; + + std::time_t edi_time = m_edi_time + std::lround(std::floor(tist_offset)); + + if (timestamp > 0xf9FFff) { + edi_time += 1; + } + + return {timestamp % 0xfa0000, edi_time}; +} + + +DabMultiplexer::DabMultiplexer(boost::property_tree::ptree pt) : RemoteControllable("mux"), m_pt(pt), ensemble(std::make_shared<dabEnsemble>()), @@ -53,7 +131,7 @@ DabMultiplexer::DabMultiplexer( fig_carousel(ensemble) { RC_ADD_PARAMETER(frames, "Show number of frames generated [read-only]"); - RC_ADD_PARAMETER(tist_offset, "Timestamp offset in integral number of seconds"); + RC_ADD_PARAMETER(tist_offset, "Timestamp offset in fractional number of seconds"); rcs.enrol(&m_clock_tai); } @@ -99,59 +177,20 @@ 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"); - } - - int64_t offset_ms = chrono::duration_cast<chrono::milliseconds>(offset).count(); - offset_ms += 1000 * (t_now - m_edi_time); - - m_timestamp = 0; - while (offset_ms >= 24) { - increment_timestamp(); - m_currentFrame++; - offset_ms -= 24; - } - - mnsc_increment_time = false; + m_time.init(); + m_time.mnsc_increment_time = false; bool tist_enabled = m_pt.get("general.tist", false); - m_tist_offset = m_pt.get<int>("general.tist_offset", 0); + m_time.tist_offset = m_pt.get<double>("general.tist_offset", 0.0); - mnsc_time = m_edi_time + m_tist_offset; + auto tist_edi_time = m_time.get_time(); + 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 @@ -391,17 +430,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; - - // Also update MNSC time for next time FP==0 - mnsc_increment_time = true; - } -} /* Each call creates one ETI frame */ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs) @@ -434,7 +462,9 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs } update_dab_time(); - const auto edi_time = m_edi_time + m_tist_offset; + auto tist_edi_time = m_time.get_time(); + const auto timestamp = tist_edi_time.first; + const auto edi_time = tist_edi_time.second; // Initialise the ETI frame memset(etiFrame, 0, 6144); @@ -450,7 +480,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 { @@ -469,8 +499,8 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs //****** 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 @@ -487,7 +517,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 @@ -562,7 +592,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs eoh->MNSC = 0; struct tm time_tm; - gmtime_r(&mnsc_time, &time_tm); + gmtime_r(&m_time.mnsc_time, &time_tm); switch (fc->FP & 0x3) { case 0: @@ -574,10 +604,10 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs mnsc->rfa = 0; } - if (mnsc_increment_time) + if (m_time.mnsc_increment_time) { - mnsc_increment_time = false; - mnsc_time += 1; + m_time.mnsc_increment_time = false; + m_time.mnsc_time += 1; } break; case 1: @@ -621,7 +651,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 ******************************************* @@ -633,12 +663,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 @@ -678,8 +709,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; @@ -700,7 +731,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); } } @@ -716,8 +747,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 ***************************************** @@ -769,7 +799,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", @@ -778,7 +808,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs } #endif - m_currentFrame++; + currentFrame++; } void DabMultiplexer::print_info() @@ -799,7 +829,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.tist_offset = std::stod(value); } else { stringstream ss; @@ -814,10 +844,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 << @@ -831,8 +861,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; } diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h index 89d547e..92fa2c0 100644 --- a/src/DabMultiplexer.h +++ b/src/DabMultiplexer.h @@ -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 */ /* @@ -30,21 +30,12 @@ #endif #include "dabOutput/dabOutput.h" -#include "edioutput/TagItems.h" -#include "edioutput/TagPacket.h" -#include "edioutput/AFPacket.h" #include "edioutput/Transport.h" #include "fig/FIGCarousel.h" -#include "crc.h" -#include "utils.h" -#include "Socket.h" -#include "PcDebug.h" #include "MuxElements.h" #include "RemoteControl.h" -#include "Eti.h" #include "ClockTAI.h" #include <vector> -#include <chrono> #include <memory> #include <string> #include <memory> @@ -52,6 +43,34 @@ constexpr uint32_t ETI_FSYNC1 = 0x49C5F8; +class MuxTime { + private: + uint32_t m_timestamp = 0; + std::time_t m_edi_time = 0; + + public: + std::pair<uint32_t, std::time_t> get_time(); + + double tist_offset = 0; + + /* 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(); + void increment_timestamp(); +}; + class DabMultiplexer : public RemoteControllable { public: DabMultiplexer(boost::property_tree::ptree pt); @@ -61,7 +80,7 @@ class DabMultiplexer : public RemoteControllable { void prepare(bool require_tai_clock); - uint64_t getCurrentFrame() const { return m_currentFrame; } + uint64_t getCurrentFrame() const { return currentFrame; } void mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs); @@ -82,60 +101,20 @@ class DabMultiplexer : public RemoteControllable { void prepare_subchannels(void); void prepare_services_components(void); void prepare_data_inputs(void); - void increment_timestamp(void); boost::property_tree::ptree m_pt; - uint32_t m_timestamp = 0; - std::time_t m_edi_time = 0; - - /* 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; + MuxTime m_time; + uint64_t currentFrame = 0; edi::configuration_t edi_conf; std::shared_ptr<edi::Sender> edi_sender; - uint64_t m_currentFrame = 0; - std::shared_ptr<dabEnsemble> ensemble; - int m_tist_offset = 0; bool m_tai_clock_required = false; ClockTAI m_clock_tai; /* New FIG Carousel */ FIC::FIGCarousel fig_carousel; }; - -// DAB Mode -#define DEFAULT_DAB_MODE 1 - -// Taille de la trame de donnee, sous-canal 3, nb de paquets de 64bits, -// STL3 * 8 = x kbytes par trame ETI - -// Data bitrate in kbits/s. Must be 64 kb/s multiple. -#define DEFAULT_DATA_BITRATE 384 -#define DEFAULT_PACKET_BITRATE 32 - -/* default ensemble parameters. Label must be max 16 chars, short label - * a subset of the label, max 8 chars - */ -#define DEFAULT_ENSEMBLE_LABEL "ODR Dab Mux" -#define DEFAULT_ENSEMBLE_SHORT_LABEL "ODRMux" -#define DEFAULT_ENSEMBLE_ID 0xc000 -#define DEFAULT_ENSEMBLE_ECC 0xa1 - -// start value for default service IDs (if not overridden by configuration) -#define DEFAULT_SERVICE_ID 50 -#define DEFAULT_PACKET_ADDRESS 0 - diff --git a/src/fig/FIG0structs.h b/src/fig/FIG0structs.h index 5f514b3..2e107e8 100644 --- a/src/fig/FIG0structs.h +++ b/src/fig/FIG0structs.h @@ -24,19 +24,17 @@ */ #pragma once - #include <cstdint> - #include "fig/FIG.h" -#define FIG0_13_APPTYPE_SLIDESHOW 0x2 -#define FIG0_13_APPTYPE_WEBSITE 0x3 -#define FIG0_13_APPTYPE_TPEG 0x4 -#define FIG0_13_APPTYPE_DGPS 0x5 -#define FIG0_13_APPTYPE_TMC 0x6 -#define FIG0_13_APPTYPE_SPI 0x7 -#define FIG0_13_APPTYPE_DABJAVA 0x8 -#define FIG0_13_APPTYPE_JOURNALINE 0x44a +constexpr uint16_t FIG0_13_APPTYPE_SLIDESHOW = 0x2; +constexpr uint16_t FIG0_13_APPTYPE_WEBSITE = 0x3; +constexpr uint16_t FIG0_13_APPTYPE_TPEG = 0x4; +constexpr uint16_t FIG0_13_APPTYPE_DGPS = 0x5; +constexpr uint16_t FIG0_13_APPTYPE_TMC = 0x6; +constexpr uint16_t FIG0_13_APPTYPE_SPI = 0x7; +constexpr uint16_t FIG0_13_APPTYPE_DABJAVA = 0x8; +constexpr uint16_t FIG0_13_APPTYPE_JOURNALINE = 0x44a; struct FIGtype0 { diff --git a/src/utils.cpp b/src/utils.cpp index e7ef224..1a13caf 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -29,7 +29,6 @@ #include <iostream> #include <memory> #include <boost/algorithm/string/join.hpp> -#include "DabMux.h" #include "utils.h" #include "fig/FIG0structs.h" |