/* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) Copyright (C) 2017 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 . */ #pragma once #include #include #include #include #include #include #include "Eti.h" #include "Log.h" #include "RemoteControl.h" struct frame_timestamp { // Which frame count does this timestamp apply to int32_t fct; uint32_t timestamp_sec; uint32_t timestamp_pps; // In units of 1/16384000 s bool timestamp_valid = false; bool timestamp_refresh; frame_timestamp() = default; frame_timestamp(const frame_timestamp& other) = default; frame_timestamp operator=(const frame_timestamp &rhs) { if (this != &rhs) { this->timestamp_sec = rhs.timestamp_sec; this->timestamp_pps = rhs.timestamp_pps; this->timestamp_valid = rhs.timestamp_valid; this->timestamp_refresh = rhs.timestamp_refresh; this->fct = rhs.fct; } return *this; } frame_timestamp& operator+=(const double& diff) { double offset_pps, offset_secs; offset_pps = modf(diff, &offset_secs); this->timestamp_sec += lrintf(offset_secs); this->timestamp_pps += lrintf(offset_pps * 16384000.0); while (this->timestamp_pps > 16384000) { this->timestamp_pps -= 16384000; this->timestamp_sec += 1; }; return *this; } const frame_timestamp operator+(const double diff) { frame_timestamp ts = *this; ts += diff; return ts; } double pps_offset() const { return timestamp_pps / 16384000.0; } double get_real_secs() const { double t = timestamp_sec; t += timestamp_pps; return t; } void print(const char* t) { fprintf(stderr, "%s \n", t, this->timestamp_valid ? "valid" : "invalid", this->timestamp_sec, pps_offset(), this->fct); } }; /* This module decodes MNSC time information from an ETI source and * EDI time information*/ class TimestampDecoder : public RemoteControllable { public: TimestampDecoder( /* The modulator adds this offset to the TIST to define time of * frame transmission */ double& offset_s, /* Specifies by how many stages the timestamp must be delayed. * (e.g. The FIRFilter is pipelined, therefore we must increase * tist_delay_stages by one if the filter is used */ unsigned tist_delay_stages) : RemoteControllable("tist"), timestamp_offset(offset_s) { m_tist_delay_stages = tist_delay_stages; inhibit_second_update = 0; time_pps = 0.0; time_secs = 0; latestFCT = 0; enableDecode = false; full_timestamp_received = false; // Properly initialise temp_time memset(&temp_time, 0, sizeof(temp_time)); const time_t timep = 0; gmtime_r(&timep, &temp_time); offset_changed = false; RC_ADD_PARAMETER(offset, "TIST offset [s]"); RC_ADD_PARAMETER(timestamp, "FCT and timestamp [s]"); etiLog.level(info) << "Setting up timestamp decoder with " << timestamp_offset << " offset"; }; /* Calculate the timestamp for the current frame. */ void calculateTimestamp(frame_timestamp& ts); /* Update timestamp data from ETI */ void updateTimestampEti( int framephase, uint16_t mnsc, uint32_t pps, // In units of 1/16384000 s int32_t fct); /* Update timestamp data from EDI */ void updateTimestampEdi( uint32_t seconds_utc, uint32_t pps, // In units of 1/16384000 s int32_t fct); /*********** REMOTE CONTROL ***************/ /* Base function to set parameters. */ virtual void set_parameter(const std::string& parameter, const std::string& value); /* Getting a parameter always returns a string. */ virtual const std::string get_parameter( const std::string& parameter) const; const char* name() { return "TS"; } protected: /* Push a new MNSC field into the decoder */ void pushMNSCData(int framephase, uint16_t mnsc); /* Each frame contains the TIST field with the PPS offset. * For each frame, this function must be called to update * the timestamp. * * pps is in units of 1/16384000 s * * This function also takes care of updating the second when * the pps rolls over. */ void updateTimestampPPS(uint32_t pps); /* Update the timestamp when a full set of MNSC data is * known. This function can be called at most every four * frames when the data is transferred using the MNSC. */ void updateTimestampSeconds(uint32_t secs); struct tm temp_time; uint32_t time_secs; int32_t latestFCT; uint32_t time_pps; double& timestamp_offset; unsigned m_tist_delay_stages; int inhibit_second_update; bool offset_changed; /* When the type or identifier don't match, the decoder must * be disabled */ bool enableDecode; /* Disable timstamps until full time has been received */ bool full_timestamp_received; /* when pipelining, we must shift the calculated timestamps * through this queue. Otherwise, it would not be possible to * synchronise two modulators if only one uses (for instance) the * FIRFilter (1 stage pipeline) */ std::queue > queue_timestamps; };