aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2019-01-28 12:04:21 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2019-01-28 12:04:21 +0100
commitcdb12647f4f3759022ad6886680d7b47d72c370e (patch)
tree8a8963d1329b39b71df26088bac3e1019dc19027
parent390f7601ec23466e0cb1af54137f7839c9f0a9a7 (diff)
downloadodr-edilib-cdb12647f4f3759022ad6886680d7b47d72c370e.tar.gz
odr-edilib-cdb12647f4f3759022ad6886680d7b47d72c370e.tar.bz2
odr-edilib-cdb12647f4f3759022ad6886680d7b47d72c370e.zip
Also decode and compare timestamp in MNSC
-rw-r--r--CMakeLists.txt1
-rw-r--r--decoder/ETIWriter.cpp7
-rw-r--r--decoder/ETIWriter.hpp6
-rw-r--r--test/TimestampDecoder.cpp201
-rw-r--r--test/TimestampDecoder.h189
-rw-r--r--test/main.cpp33
6 files changed, 427 insertions, 10 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3515d17..20fb1e4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,6 +51,7 @@ list(APPEND edilib_cpp_sources
decoder/PFT.cpp
decoder/eti.cpp
test/main.cpp
+ test/TimestampDecoder.cpp
test/InetAddress.cpp
test/UdpSocket.cpp
)
diff --git a/decoder/ETIWriter.cpp b/decoder/ETIWriter.cpp
index 2a54a75..eaff872 100644
--- a/decoder/ETIWriter.cpp
+++ b/decoder/ETIWriter.cpp
@@ -48,7 +48,7 @@ time_t eti_frame_t::timestamp_in_unix_epoch() const
// EDI epoch: 2000-01-01T00:00:00Z
// Convert using
// TZ=UTC python -c 'import datetime; print(datetime.datetime(2000,1,1,0,0,0,0).strftime("%s"))'
- return 946684800 + seconds;
+ return 946684800 + seconds - utco;
}
void ETIWriter::update_protocol(
@@ -320,5 +320,10 @@ eti_frame_t ETIWriter::getEtiFrame()
return eti;
}
+uint16_t ETIWriter::get_mnsc() const
+{
+ return m_mnsc;
+}
+
}
diff --git a/decoder/ETIWriter.hpp b/decoder/ETIWriter.hpp
index 580d3d2..58a56bc 100644
--- a/decoder/ETIWriter.hpp
+++ b/decoder/ETIWriter.hpp
@@ -72,6 +72,12 @@ class ETIWriter : public DataCollector {
// Return the assembled ETI frame or an empty frame if not ready
eti_frame_t getEtiFrame(void);
+ uint16_t get_mnsc() const;
+
+ uint32_t get_timestamp() const { return m_fc.tsta; }
+
+ uint8_t get_framephase() const { return m_fc.fp; }
+
private:
void reinit(void);
diff --git a/test/TimestampDecoder.cpp b/test/TimestampDecoder.cpp
new file mode 100644
index 0000000..0f1d897
--- /dev/null
+++ b/test/TimestampDecoder.cpp
@@ -0,0 +1,201 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2018
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <cstring>
+#include "TimestampDecoder.h"
+#include "Log.h"
+
+#define PDEBUG(fmt, args...) // fprintf (stderr, "*****" fmt , ## args)
+
+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) :
+ 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);
+}
+
+std::shared_ptr<frame_timestamp> TimestampDecoder::getTimestamp()
+{
+ auto ts = std::make_shared<frame_timestamp>();
+
+ ts->timestamp_valid = full_timestamp_received;
+ ts->timestamp_sec = time_secs;
+ ts->timestamp_pps = time_pps;
+ ts->fct = latestFCT;
+ ts->fp = latestFP;
+
+ ts->timestamp_refresh = 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;
+ }
+
+ PDEBUG("TimestampDecoder::pushMNSCData(%d, 0x%x)\n", framephase, mnsc);
+ PDEBUG(" -> %s\n", asctime(&temp_time));
+ PDEBUG(" -> %zu\n", mktime(&temp_time));
+}
+
+void TimestampDecoder::updateTimestampSeconds(uint32_t secs)
+{
+ if (inhibit_second_update > 0) {
+ PDEBUG("TimestampDecoder::updateTimestampSeconds(%d) inhibit\n", secs);
+ inhibit_second_update--;
+ }
+ else {
+ PDEBUG("TimestampDecoder::updateTimestampSeconds(%d) apply\n", secs);
+ time_secs = secs;
+ full_timestamp_received = true;
+ }
+}
+
+void TimestampDecoder::updateTimestampPPS(uint32_t pps)
+{
+ PDEBUG("TimestampDecoder::updateTimestampPPS(%f)\n", (double)pps / 16384000.0);
+
+ if (time_pps > pps) { // Second boundary crossed
+ PDEBUG("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;
+ }
+}
+
diff --git a/test/TimestampDecoder.h b/test/TimestampDecoder.h
new file mode 100644
index 0000000..cb99cd1
--- /dev/null
+++ b/test/TimestampDecoder.h
@@ -0,0 +1,189 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2018
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <cmath>
+#include <cstdio>
+
+#define PACKED __attribute__ ((packed))
+
+struct eti_MNSC_TIME_0 {
+ uint32_t type:4;
+ uint32_t identifier:4;
+ uint32_t rfa:8;
+} PACKED;
+
+struct eti_MNSC_TIME_1 {
+ uint32_t second_unit:4;
+ uint32_t second_tens:3;
+ uint32_t accuracy:1;
+
+ uint32_t minute_unit:4;
+ uint32_t minute_tens:3;
+ uint32_t sync_to_frame:1;
+} PACKED;
+
+struct eti_MNSC_TIME_2 {
+ uint32_t hour_unit:4;
+ uint32_t hour_tens:4;
+
+ uint32_t day_unit:4;
+ uint32_t day_tens:4;
+} PACKED;
+
+struct eti_MNSC_TIME_3 {
+ uint32_t month_unit:4;
+ uint32_t month_tens:4;
+
+ uint32_t year_unit:4;
+ uint32_t year_tens:4;
+} PACKED;
+
+struct eti_extension_TIME {
+ uint32_t TIME_SECONDS;
+} PACKED;
+
+struct frame_timestamp
+{
+ // Which frame count does this timestamp apply to
+ int32_t fct;
+ uint8_t fp; // Frame Phase
+
+ uint32_t timestamp_sec;
+ uint32_t timestamp_pps; // In units of 1/16384000 s
+ bool timestamp_valid = false;
+ bool timestamp_refresh;
+
+ frame_timestamp& operator+=(const double& diff);
+
+ const frame_timestamp operator+(const double diff) const {
+ 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 += pps_offset();
+ return t;
+ }
+
+ long long int get_ns() const {
+ long long int ns = timestamp_sec * 1000000000ull;
+ ns += llrint((double)timestamp_pps / 0.016384);
+ return ns;
+ }
+
+ void set_ns(long long int time_ns) {
+ timestamp_sec = time_ns / 1000000000ull;
+ const long long int subsecond = time_ns % 1000000000ull;
+ timestamp_pps = lrint(subsecond * 16384000.0);
+ }
+
+ void print(const char* t) const {
+ fprintf(stderr,
+ "%s <frame_timestamp(%s, %d, %.9f, %d)>\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:
+ /* offset_s: The modulator adds this offset to the TIST to define time of
+ * frame transmission
+ */
+ TimestampDecoder(double& offset_s);
+
+ std::shared_ptr<frame_timestamp> getTimestamp(void);
+
+ /* Update timestamp data from ETI */
+ void updateTimestampEti(
+ uint8_t 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,
+ uint8_t framephase);
+ protected:
+ /* Push a new MNSC field into the decoder */
+ void pushMNSCData(uint8_t 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 = 0;
+ int32_t latestFCT = 0;
+ uint32_t latestFP = 0;
+ uint32_t time_pps = 0;
+ double& timestamp_offset;
+ int inhibit_second_update = 0;
+ bool offset_changed = false;
+
+ uint32_t time_secs_of_frame0 = 0;
+ uint32_t time_pps_of_frame0 = 0;
+
+ /* When the type or identifier don't match, the decoder must
+ * be disabled
+ */
+ bool enableDecode = false;
+
+ /* Disable timstamps until full time has been received */
+ bool full_timestamp_received = false;
+};
+
diff --git a/test/main.cpp b/test/main.cpp
index 2158644..28b586b 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -19,19 +19,20 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include <stdint.h>
-#include <stdio.h>
-#include <vector>
-#include <cstdlib>
-#include <string>
+#include <atomic>
#include <regex>
+#include <string>
#include <thread>
-#include <atomic>
+#include <vector>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
#include "ETIDecoder.hpp"
#include "ETIWriter.hpp"
#include "UdpSocket.h"
#include "ThreadsafeQueue.h"
+#include "TimestampDecoder.h"
struct options_t {
std::string edi_source;
@@ -156,6 +157,10 @@ int main(int argc, char **argv)
return 1;
}
+ // Set timezone to UTC
+ setenv("TZ", "", 1);
+ tzset();
+
const auto options = parseargs(argc, argv);
if (options.edi_source.empty()) {
@@ -167,6 +172,8 @@ int main(int argc, char **argv)
fd_eti = fopen(options.eti_file.c_str(), "w");
}
+ double offset = 0;
+ TimestampDecoder ts_dec(offset);
EdiDecoder::ETIWriter writer;
EdiDecoder::ETIDecoder decoder(writer, options.verbose > 1);
@@ -202,17 +209,25 @@ int main(int argc, char **argv)
if (not eti.frame.empty() and options.verbose > 0) {
const uint8_t fct = eti.frame[4];
const std::time_t now = std::time(nullptr);
- fprintf(stderr, "Got ETI Frame with FCT %d at %s: seconds=%d, utco=%d, now-ts=%ld\n",
+
+ ts_dec.updateTimestampEti(writer.get_framephase(), writer.get_mnsc(), writer.get_timestamp(), fct);
+
+ const auto ts = ts_dec.getTimestamp();
+
+ fprintf(stderr, "%ld: Got ETI Frame with FCT %d at %s: seconds=%d, utco=%d, now-ts=%ld, MNSC: %d, now-MNSC=%ld\n",
+ now,
fct,
eti.calculate_timestamp().c_str(),
eti.seconds, eti.utco,
- now - eti.timestamp_in_unix_epoch());
+ now - eti.timestamp_in_unix_epoch(),
+ ts->timestamp_sec,
+ now - ts->timestamp_sec
+ );
}
if (fd_eti and not eti.frame.empty()) {
fwrite(eti.frame.data(), eti.frame.size(), 1, fd_eti);
}
-
}
}
else {