/* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) Copyright (C) 2023 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org */ /* This file is part of ODR-DabMod. ODR-DabMod is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ODR-DabMod is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with ODR-DabMod. If not, see . */ #include #include #include #include #include "PcDebug.h" #include "TimestampDecoder.h" #include "Log.h" #include "Eti.h" //#define MDEBUG(fmt, args...) fprintf (LOG, "*****" fmt , ## args) #define MDEBUG(fmt, args...) PDEBUG(fmt, ## args) double frame_timestamp::offset_to_system_time() const { if (not timestamp_valid) { throw new std::runtime_error("Cannot calculate offset for invalid timestamp"); } struct timespec t; if (clock_gettime(CLOCK_REALTIME, &t) != 0) { throw std::runtime_error(std::string("Failed to retrieve CLOCK_REALTIME") + strerror(errno)); } return get_real_secs() - (double)t.tv_sec - (t.tv_nsec / 1000000000.0); } std::string frame_timestamp::to_string() const { time_t s = timestamp_sec; std::stringstream ss; char timestr[100]; if (std::strftime(timestr, sizeof(timestr), "%Y-%m-%dZ%H:%M:%S", std::gmtime(&s))) { ss << timestr << " + " << ((double)timestamp_pps / 16384000.0); } return ss.str(); } frame_timestamp& frame_timestamp::operator+=(const double& diff) { double offset_pps, offset_secs; offset_pps = modf(diff, &offset_secs); this->timestamp_sec += lrint(offset_secs); int64_t new_pps = (int64_t)this->timestamp_pps + llrint(offset_pps * 16384000.0); while (new_pps < 0) { this->timestamp_sec -= 1; new_pps += 16384000; } while (new_pps >= 16384000) { this->timestamp_sec += 1; new_pps -= 16384000; } this->timestamp_pps = new_pps; return *this; } TimestampDecoder::TimestampDecoder(double& offset_s) : RemoteControllable("tist"), timestamp_offset(offset_s) { // Properly initialise temp_time memset(&temp_time, 0, sizeof(temp_time)); const time_t timep = 0; gmtime_r(&timep, &temp_time); RC_ADD_PARAMETER(offset, "TIST offset [s]"); RC_ADD_PARAMETER(timestamp, "FCT and timestamp [s]"); RC_ADD_PARAMETER(timestamp0, "Timestamp of frame with FCT=0 [s]"); etiLog.level(info) << "Setting up timestamp decoder with " << timestamp_offset << " offset"; } frame_timestamp TimestampDecoder::getTimestamp() { frame_timestamp ts; ts.timestamp_valid = full_timestamp_received; ts.timestamp_sec = time_secs; ts.timestamp_pps = time_pps; ts.fct = latestFCT; ts.fp = latestFP; ts.timestamp_offset = timestamp_offset; ts.offset_changed = offset_changed; offset_changed = false; ts += timestamp_offset; return ts; } void TimestampDecoder::pushMNSCData(uint8_t framephase, uint16_t mnsc) { struct eti_MNSC_TIME_0 *mnsc0; struct eti_MNSC_TIME_1 *mnsc1; struct eti_MNSC_TIME_2 *mnsc2; struct eti_MNSC_TIME_3 *mnsc3; switch (framephase) { case 0: mnsc0 = (struct eti_MNSC_TIME_0*)&mnsc; enableDecode = (mnsc0->type == 0) && (mnsc0->identifier == 0); { const time_t timep = 0; gmtime_r(&timep, &temp_time); } break; case 1: mnsc1 = (struct eti_MNSC_TIME_1*)&mnsc; temp_time.tm_sec = mnsc1->second_tens * 10 + mnsc1->second_unit; temp_time.tm_min = mnsc1->minute_tens * 10 + mnsc1->minute_unit; if (!mnsc1->sync_to_frame) { enableDecode = false; PDEBUG("TimestampDecoder: " "MNSC time info is not synchronised to frame\n"); } break; case 2: mnsc2 = (struct eti_MNSC_TIME_2*)&mnsc; temp_time.tm_hour = mnsc2->hour_tens * 10 + mnsc2->hour_unit; temp_time.tm_mday = mnsc2->day_tens * 10 + mnsc2->day_unit; break; case 3: mnsc3 = (struct eti_MNSC_TIME_3*)&mnsc; temp_time.tm_mon = (mnsc3->month_tens * 10 + mnsc3->month_unit) - 1; temp_time.tm_year = (mnsc3->year_tens * 10 + mnsc3->year_unit) + 100; if (enableDecode) { updateTimestampSeconds(mktime(&temp_time)); } break; } MDEBUG("TimestampDecoder::pushMNSCData(%d, 0x%x)\n", framephase, mnsc); MDEBUG(" -> %s\n", asctime(&temp_time)); MDEBUG(" -> %zu\n", mktime(&temp_time)); } void TimestampDecoder::updateTimestampSeconds(uint32_t secs) { if (inhibit_second_update > 0) { MDEBUG("TimestampDecoder::updateTimestampSeconds(%d) inhibit\n", secs); inhibit_second_update--; } else { MDEBUG("TimestampDecoder::updateTimestampSeconds(%d) apply\n", secs); time_secs = secs; full_timestamp_received = true; } } void TimestampDecoder::updateTimestampPPS(uint32_t pps) { MDEBUG("TimestampDecoder::updateTimestampPPS(%f)\n", (double)pps / 16384000.0); if (time_pps > pps) { // Second boundary crossed MDEBUG("TimestampDecoder::updateTimestampPPS crossed second\n"); // The second for the next eight frames will not // be defined by the MNSC inhibit_second_update = 2; time_secs += 1; } time_pps = pps; } void TimestampDecoder::updateTimestampEti( uint8_t framephase, uint16_t mnsc, uint32_t pps, // In units of 1/16384000 s int32_t fct) { updateTimestampPPS(pps); pushMNSCData(framephase, mnsc); latestFCT = fct; latestFP = framephase; if (full_timestamp_received and fct == 0) { time_secs_of_frame0 = time_secs; time_pps_of_frame0 = time_pps; } } void TimestampDecoder::updateTimestampEdi( uint32_t seconds_utc, uint32_t pps, // In units of 1/16384000 s int32_t fct, uint8_t framephase) { time_secs = seconds_utc; time_pps = pps; latestFCT = fct; latestFP = framephase; full_timestamp_received = true; if (fct == 0) { time_secs_of_frame0 = time_secs; time_pps_of_frame0 = time_pps; } } void TimestampDecoder::set_parameter( const std::string& parameter, const std::string& value) { using namespace std; stringstream ss(value); ss.exceptions ( stringstream::failbit | stringstream::badbit ); if (parameter == "offset") { ss >> timestamp_offset; offset_changed = true; } else if (parameter == "timestamp") { throw ParameterError("timestamp is read-only"); } else if (parameter == "timestamp0") { throw ParameterError("timestamp0 is read-only"); } else { stringstream ss_err; ss_err << "Parameter '" << parameter << "' is not exported by controllable " << get_rc_name(); throw ParameterError(ss_err.str()); } } const std::string TimestampDecoder::get_parameter( const std::string& parameter) const { using namespace std; stringstream ss; if (parameter == "offset") { ss << timestamp_offset; } else if (parameter == "timestamp") { if (full_timestamp_received) { ss.setf(std::ios_base::fixed, std::ios_base::floatfield); ss << time_secs + ((double)time_pps / 16384000.0) << " for frame FCT " << latestFCT; } else { throw ParameterError("Not available yet"); } } else if (parameter == "timestamp0") { if (full_timestamp_received) { ss.setf(std::ios_base::fixed, std::ios_base::floatfield); ss << time_secs_of_frame0 + ((double)time_pps_of_frame0 / 16384000.0) << " for frame FCT 0"; } else { throw ParameterError("Not available yet"); } } else { ss << "Parameter '" << parameter << "' is not exported by controllable " << get_rc_name(); throw ParameterError(ss.str()); } return ss.str(); } const json::map_t TimestampDecoder::get_all_values() const { json::map_t map; map["offset"].v = timestamp_offset; if (full_timestamp_received) { map["timestamp"].v = time_secs + ((double)time_pps / 16384000.0); } else { map["timestamp"].v = std::nullopt; } if (full_timestamp_received) { map["timestamp0"].v = time_secs_of_frame0 + ((double)time_pps_of_frame0 / 16384000.0); } else { map["timestamp0"].v = std::nullopt; } return map; }