summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2016-01-08 11:49:26 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2016-01-10 20:32:49 +0100
commit70c5ba0867945bb097f466262ce03470b690518f (patch)
treec7b14e7e5debdad25778d18559d42f5306289e67
parent3bcda54518c4914c1b934d7d12b7219d1ee7d941 (diff)
downloaddabmux-70c5ba0867945bb097f466262ce03470b690518f.tar.gz
dabmux-70c5ba0867945bb097f466262ce03470b690518f.tar.bz2
dabmux-70c5ba0867945bb097f466262ce03470b690518f.zip
Add TAI handling for EDI with TIST
-rw-r--r--configure.ac6
-rw-r--r--src/ClockTAI.cpp220
-rw-r--r--src/ClockTAI.h85
-rw-r--r--src/DabMultiplexer.cpp36
-rw-r--r--src/DabMultiplexer.h3
-rw-r--r--src/Makefile.am42
-rw-r--r--src/dabOutput/edi/TagItems.h35
7 files changed, 410 insertions, 17 deletions
diff --git a/configure.ac b/configure.ac
index f23df79..e7e169c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -201,12 +201,18 @@ AC_ARG_ENABLE([output_edi],
[AS_HELP_STRING([--disable-output-edi], [Disable EDI output])],
[], [enable_output_edi=yes])
AS_IF([test "x$enable_output_edi" = "xyes"],
+ AC_CHECK_LIB(curl, curl_easy_init, [true], [AC_MSG_ERROR([cURL is required for EDI output])]))
+AS_IF([test "x$enable_output_edi" = "xyes"],
[AC_DEFINE(HAVE_OUTPUT_EDI, [1], [Define if EDI output is enabled])])
# Link against lzmq
AM_CONDITIONAL([HAVE_ZEROMQ_TEST],
[test "x$enable_output_zeromq" = "xyes" -o "x$enable_input_zeromq" = "xyes"])
+# Link against cURL
+AM_CONDITIONAL([HAVE_CURL_TEST],
+ [test "x$enable_output_edi" = "xyes"])
+
# Formats
# RAW
diff --git a/src/ClockTAI.cpp b/src/ClockTAI.cpp
new file mode 100644
index 0000000..1b2d4ef
--- /dev/null
+++ b/src/ClockTAI.cpp
@@ -0,0 +1,220 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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-DabMux 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-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "ClockTAI.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/timex.h>
+#include <curl/curl.h>
+#include <string>
+#include <iostream>
+#include <algorithm>
+#include <regex>
+
+/* Last line from bulletin, example:
+ 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S
+ */
+
+ClockTAI::ClockTAI()
+{
+ m_offset = 0;
+ m_bulletin_download_time.tv_nsec = 0;
+ m_bulletin_download_time.tv_sec = 0;
+}
+
+int ClockTAI::get_offset()
+{
+ struct timespec time_now;
+
+ int err = clock_gettime(CLOCK_REALTIME, &time_now);
+ if (err) {
+ throw std::runtime_error("ClockTAI::get_offset() clock_gettime failed");
+ }
+
+ if (time_now.tv_sec - m_bulletin_download_time.tv_sec >
+ 3600 * 24 * 31) {
+ // Refresh if it's older than one month. Leap seconds are
+ // announced several months in advance
+
+ if (download_tai_utc_bulletin(tai_data_url1) == 0) {
+ m_offset = parse_tai_offset();
+ }
+ else if (download_tai_utc_bulletin(tai_data_url2) == 0) {
+ m_offset = parse_tai_offset();
+ }
+ }
+
+ return m_offset;
+
+ throw std::runtime_error("Could not fetch TAI-UTC offset");
+}
+
+#if SUPPORT_SETTING_CLOCK_TAI
+int ClockTAI::update_local_tai_clock(int offset)
+{
+ struct timex timex_request;
+ timex_request.modes = ADJ_TAI;
+ timex_request.constant = offset;
+
+ int err = adjtimex(&timex_request);
+ if (err == -1) {
+ perror("adjtimex");
+ }
+
+ printf("adjtimex: %d, tai %d\n", err, timex_request.tai);
+
+ return err;
+}
+#endif
+
+int ClockTAI::parse_tai_offset()
+{
+ std::regex regex_offset("TAI-UTC= *([0-9.]+)");
+
+ int tai_utc_offset = 0;
+
+ int linecount = 0;
+
+ for (std::string line; std::getline(m_bulletin, line); ) {
+ linecount++;
+ auto words_begin =
+ std::sregex_token_iterator(
+ line.begin(), line.end(), regex_offset, 1);
+ auto words_end = std::sregex_token_iterator();
+
+ for (auto w = words_begin; w != words_end; ++w) {
+ std::string s(*w);
+ tai_utc_offset = std::atoi(s.c_str());
+ }
+
+ }
+
+ if (linecount == 0) {
+ throw std::runtime_error("No data in TAI bulletin");
+ }
+
+ // With the current evolution of the offset, we're probably going
+ // to reach 500 long after DAB gets replaced by another standard.
+ if (tai_utc_offset < 0 or tai_utc_offset > 500) {
+ std::stringstream ss;
+ ss << "TAI offset " << tai_utc_offset << " out of range";
+ throw std::range_error(ss.str());
+ }
+
+ return tai_utc_offset;
+}
+
+size_t ClockTAI::fill_bulletin(char *ptr, size_t size, size_t nmemb)
+{
+ size_t len = size * nmemb;
+ for (size_t i = 0; i < len; i++) {
+ m_bulletin << ptr[i];
+ }
+ return len;
+}
+
+size_t ClockTAI::fill_bulletin_cb(
+ char *ptr, size_t size, size_t nmemb, void *ctx)
+{
+ return ((ClockTAI*)ctx)->fill_bulletin(ptr, size, nmemb);
+}
+
+int ClockTAI::download_tai_utc_bulletin(const char* url)
+{
+ int r = 0;
+
+ CURL *curl;
+ CURLcode res;
+
+ curl = curl_easy_init();
+ if (curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ /* example.com is redirected, so we tell libcurl to follow redirection */
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ClockTAI::fill_bulletin_cb);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ fprintf(stderr, "curl_easy_perform() failed: %s\n",
+ curl_easy_strerror(res));
+
+ r = 1;
+ }
+ else {
+ // Save the download time of the bulletin
+ int err = clock_gettime(CLOCK_REALTIME, &m_bulletin_download_time);
+ if (err) {
+ perror("REALTIME clock_gettime failed");
+ r = 1;
+ }
+ }
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ return r;
+}
+
+#if 0
+// Example testing code
+void debug_tai_clk()
+{
+ struct timespec rt_clk;
+
+ int err = clock_gettime(CLOCK_REALTIME, &rt_clk);
+ if (err) {
+ perror("REALTIME clock_gettime failed");
+ }
+
+ struct timespec tai_clk;
+
+ err = clock_gettime(CLOCK_TAI, &tai_clk);
+ if (err) {
+ perror("TAI clock_gettime failed");
+ }
+
+ printf("RT - TAI = %ld\n", rt_clk.tv_sec - tai_clk.tv_sec);
+
+
+ struct timex timex_request;
+ timex_request.modes = 0; // Do not set anything
+
+ err = adjtimex(&timex_request);
+ if (err == -1) {
+ perror("adjtimex");
+ }
+
+ printf("adjtimex: %d, tai %d\n", err, timex_request.tai);
+}
+#endif
+
diff --git a/src/ClockTAI.h b/src/ClockTAI.h
new file mode 100644
index 0000000..027e6db
--- /dev/null
+++ b/src/ClockTAI.h
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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-DabMux 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-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* The EDI output needs TAI clock, according to ETSI TS 102 693 Annex F
+ * "EDI Timestamps". This module sets the local CLOCK_TAI clock by
+ * setting the TAI-UTC offset using adjtimex.
+ *
+ * This functionality requires Linux 3.10 (30 Jun 2013) or newer */
+
+#ifndef __CLOCK_TAI_H_
+#define __CLOCK_TAI_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sstream>
+#include <time.h>
+
+// EDI needs to know UTC-TAI, but doesn't need the CLOCK_TAI to be set
+#define SUPPORT_SETTING_CLOCK_TAI 0
+
+/* Loads, parses and represents TAI-UTC offset information from the USNO bulletin */
+class ClockTAI {
+ public:
+ ClockTAI();
+
+ // Fetch the bulletin from the USNO website and return the current
+ // TAI-UTC offset.
+ // Throws runtime_error on failure.
+ int get_offset(void);
+
+#if SUPPORT_SETTING_CLOCK_TAI
+ // Update the local TAI clock according to the TAI-UTC offset
+ // return 0 on success
+ int update_local_tai_clock(int offset);
+#endif
+
+ private:
+ // leap seconds insertion bulletin is available at
+ const char* tai_data_url1 = "http://maia.usno.navy.mil/ser7/tai-utc.dat";
+ const char* tai_data_url2 = "http://toshi.nofs.navy.mil/ser7/tai-utc.dat";
+
+ int m_offset;
+ std::stringstream m_bulletin;
+ struct timespec m_bulletin_download_time;
+
+ // Load bulletin into m_bulletin, return 0 on success
+ int download_tai_utc_bulletin(const char* url);
+
+ // read TAI offset from m_bulletin
+ int parse_tai_offset(void);
+
+ // callback that receives data from cURL
+ size_t fill_bulletin(char *ptr, size_t size, size_t nmemb);
+
+ // static callback wrapper for cURL
+ static size_t fill_bulletin_cb(
+ char *ptr, size_t size, size_t nmemb, void *ctx);
+};
+
+#endif // __CLOCK_TAI_H_
+
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
index fd59b8f..d9e1a10 100644
--- a/src/DabMultiplexer.cpp
+++ b/src/DabMultiplexer.cpp
@@ -239,6 +239,30 @@ void DabMultiplexer::prepare()
*/
gettimeofday(&mnsc_time, NULL);
+#if HAVE_OUTPUT_EDI
+ // Try to load offset once
+
+ bool tist_enabled = m_pt.get("general.tist", false);
+
+ try {
+ m_clock_tai.get_offset();
+ }
+ catch (std::runtime_error& e) {
+ const char* err_msg =
+ "Could not initialise TAI clock properly required by "
+ "EDI with timestamp. Do you have a working internet "
+ "connection?";
+
+ if (tist_enabled and edi_conf.enabled) {
+ etiLog.level(error) << err_msg;
+ throw e;
+ }
+ else {
+ etiLog.level(warn) << err_msg;
+ }
+ }
+#endif
+
// Shift ms by 13 to Timestamp level 2, see below in Section TIST
timestamp = (mnsc_time.tv_usec / 1000) << 13;
}
@@ -450,6 +474,18 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
edi_tagDETI.atstf = 1;
edi_tagDETI.utco = 0;
edi_tagDETI.seconds = 0;
+ try {
+ bool tist_enabled = m_pt.get("general.tist", false);
+
+ if (tist_enabled and edi_conf.enabled) {
+ edi_tagDETI.set_utco(m_clock_tai.get_offset());
+ }
+
+ edi_tagDETI.set_seconds(mnsc_time);
+ }
+ catch (std::runtime_error& e) {
+ etiLog.level(error) << "Could not get UTC-TAI offset for EDI timestamp";
+ }
date = getDabTime();
diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h
index aa468ea..7b3834e 100644
--- a/src/DabMultiplexer.h
+++ b/src/DabMultiplexer.h
@@ -45,6 +45,7 @@
#include "MuxElements.h"
#include "RemoteControl.h"
#include "Eti.h"
+#include "ClockTAI.h"
#include <exception>
#include <vector>
#include <memory>
@@ -136,6 +137,8 @@ class DabMultiplexer : public RemoteControllable {
std::shared_ptr<dabEnsemble> ensemble_next;
#if HAVE_OUTPUT_EDI
+ ClockTAI m_clock_tai;
+
std::ofstream edi_debug_file;
UdpSocket edi_output;
diff --git a/src/Makefile.am b/src/Makefile.am
index 1cf8dbb..4c5cd60 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,9 +39,16 @@ bin_PROGRAMS=odr-dabmux odr-bridgetest
ZMQ_LIBS =
endif
+if HAVE_CURL_TEST
+CURL_LIBS =-lcurl
+else
+CURL_LIBS =
+endif
+
odr_dabmux_CFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
odr_dabmux_CXXFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
-odr_dabmux_LDADD =$(FEC_LIBS) $(ZMQ_LIBS) -lpthread -lboost_thread -lboost_system
+odr_dabmux_LDADD =$(FEC_LIBS) $(ZMQ_LIBS) $(CURL_LIBS) \
+ -lpthread -lboost_thread -lboost_system
odr_dabmux_SOURCES =DabMux.cpp DabMux.h \
DabMultiplexer.cpp DabMultiplexer.h \
dabInput.h dabInput.cpp \
@@ -76,32 +83,33 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \
dabOutput/edi/TagItems.cpp dabOutput/edi/TagItems.h \
dabOutput/edi/TagPacket.cpp dabOutput/edi/TagPacket.h \
dabOutput/edi/PFT.cpp dabOutput/edi/PFT.h \
- bridge.h bridge.c \
- utils.cpp utils.h \
- MuxElements.cpp MuxElements.h \
- RemoteControl.cpp RemoteControl.h \
- ParserCmdline.cpp ParserCmdline.h \
+ ClockTAI.h ClockTAI.cpp \
ConfigParser.cpp ConfigParser.h \
+ Dmb.h Dmb.cpp \
Eti.h Eti.cpp \
- Log.h Log.cpp \
- UdpSocket.h UdpSocket.cpp \
InetAddress.h InetAddress.cpp \
- prbs.h prbs.c \
- crc.h crc.c \
- dabUtils.h dabUtils.cpp \
- PcDebug.h \
- Dmb.h Dmb.cpp \
Interleaver.h Interleaver.cpp \
- ReedSolomon.h ReedSolomon.cpp \
- mpeg.h mpeg.c \
+ Log.h Log.cpp \
ManagementServer.h ManagementServer.cpp \
+ MuxElements.cpp MuxElements.h \
+ ParserCmdline.cpp ParserCmdline.h \
+ PcDebug.h \
+ ReedSolomon.h ReedSolomon.cpp \
+ RemoteControl.cpp RemoteControl.h \
TcpServer.h TcpServer.cpp \
TcpSocket.h TcpSocket.cpp \
- zmq.hpp \
+ UdpSocket.h UdpSocket.cpp \
+ bridge.h bridge.c \
+ crc.h crc.c \
+ dabUtils.h dabUtils.cpp \
+ fig/FIG.h \
fig/FIG0.cpp fig/FIG0.h \
fig/FIG1.cpp fig/FIG1.h \
fig/FIGCarousel.cpp fig/FIGCarousel.h \
- fig/FIG.h
+ mpeg.h mpeg.c \
+ prbs.h prbs.c \
+ utils.cpp utils.h \
+ zmq.hpp
odr_bridgetest_CFLAGS =-DBRIDGE_TEST
odr_bridgetest_SOURCES =bridge.c \
diff --git a/src/dabOutput/edi/TagItems.h b/src/dabOutput/edi/TagItems.h
index 355b6e6..dee857a 100644
--- a/src/dabOutput/edi/TagItems.h
+++ b/src/dabOutput/edi/TagItems.h
@@ -74,8 +74,43 @@ class TagDETI : public TagItem
// ATST (optional)
bool atstf; // presence of atst data
+
+ /* UTCO: Offset (in seconds) between UTC and the Seconds value. The
+ * value is expressed as an unsigned 8-bit quantity. As of February
+ * 2009, the value shall be 2 and shall change as a result of each
+ * modification of the number of leap seconds, as proscribed by
+ * International Earth Rotation and Reference Systems Service (IERS).
+ *
+ * According to Annex F
+ * EDI = TAI - 32s (constant)
+ * EDI = UTC + UTCO
+ * we derive
+ * UTCO = TAI-UTC - 32
+ * where the TAI-UTC offset is given by the USNO bulletin using
+ * the ClockTAI module.
+ */
uint8_t utco;
+
+ void set_utco(int tai_utc_offset) { utco = tai_utc_offset - 32; }
+
+ /* The number of SI seconds since 2000-01-01 T 00:00:00 UTC as an
+ * unsigned 32-bit quantity
+ */
uint32_t seconds;
+
+ void set_seconds(struct timeval tv)
+ {
+ time_t posix_timestamp_1_jan_2000 = 946684800;
+ seconds = tv.tv_sec - posix_timestamp_1_jan_2000;
+ }
+
+
+ /* TSTA: Shall be the 24 least significant bits of the Time Stamp
+ * (TIST) field from the STI-D(LI) Frame. The full definition for the
+ * STI TIST can be found in annex B of EN 300 797 [4]. The most
+ * significant 8 bits of the TIST field of the incoming STI-D(LI)
+ * frame, if required, may be carried in the RFAD field.
+ */
uint32_t tsta;
// the FIC (optional)