summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml5
-rw-r--r--doc/advanced.mux4
-rw-r--r--doc/servicelinking.mux5
-rw-r--r--src/ClockTAI.cpp281
-rw-r--r--src/ClockTAI.h24
-rw-r--r--src/ConfigParser.cpp24
-rw-r--r--src/Makefile.am1
-rw-r--r--src/MuxElements.h3
-rw-r--r--src/fig/FIG0.h1
-rw-r--r--src/fig/FIG0_24.cpp184
-rw-r--r--src/fig/FIG0_24.h53
-rw-r--r--src/fig/FIGCarousel.cpp6
-rw-r--r--src/fig/FIGCarousel.h3
13 files changed, 423 insertions, 171 deletions
diff --git a/.travis.yml b/.travis.yml
index b21773f..57a1d13 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,8 +13,7 @@ addons: &addons
- libtool
- libboost1.55-all-dev
- libcurl4-openssl-dev
- - gcc-5
- - g++-5
+ - g++-6
env:
- CONF=--disable-output-edi
@@ -27,6 +26,6 @@ compiler:
script:
- |
./bootstrap.sh
- CC=gcc-5 CXX=g++-5 ./configure $CONF
+ CC=gcc-6 CXX=g++-6 ./configure $CONF
make
diff --git a/doc/advanced.mux b/doc/advanced.mux
index f725f84..93a636f 100644
--- a/doc/advanced.mux
+++ b/doc/advanced.mux
@@ -362,6 +362,10 @@ outputs {
edi {
; EDI uses the UDP protocol. This implementation of EDI does not support
; EDI Packet Resend.
+ ;
+ ; When both EDI and TIST are enabled, ODR-DabMod will download leap-second
+ ; information from the IETF website, and cache it to the file
+ ; /tmp/odr-dabmux-leap-seconds.cache
destinations {
; The names you give to the destinations have no meaning,
; but have to be unique. You can give them meaningful names to help
diff --git a/doc/servicelinking.mux b/doc/servicelinking.mux
index 6c491c8..03c842a 100644
--- a/doc/servicelinking.mux
+++ b/doc/servicelinking.mux
@@ -160,6 +160,11 @@ services {
srv-fu {
id 0x8daa
label "Funk"
+
+ ; If this service is present in other ensembles, it can be announced
+ ; through FIG0/24. other_ensembles is a comma separated list of
+ ; ensemble IDs (decimal or hexadecimal with 0x prefix)
+ other_ensembles "0x4ffe,0x4ffd"
}
srv-ri {
id 0x8dab
diff --git a/src/ClockTAI.cpp b/src/ClockTAI.cpp
index 6d05370..bdef901 100644
--- a/src/ClockTAI.cpp
+++ b/src/ClockTAI.cpp
@@ -25,9 +25,8 @@
along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
*/
-/* This file downloads the TAI-UTC bulletins from the USNO servers and/or
- * from IETF and parses them so that correct time can be communicated in EDI
- * timestamps.
+/* This file downloads the TAI-UTC bulletins from the from IETF and parses them
+ * so that correct time can be communicated in EDI timestamps.
*
* This file contains self-test code that can be executed by running
* g++ -g -Wall -DTEST -DHAVE_CURL -std=c++11 -lcurl -lboost_regex -pthread \
@@ -60,83 +59,67 @@
static bool wait_longer = true;
#endif
+// Offset between NTP time and POSIX time:
+// timestamp_unix = timestamp_ntp - ntp_unix_offset
+const int64_t ntp_unix_offset = 2208988800L;
+
// leap seconds insertion bulletin is available at
static const char* tai_ietf_url =
"http://www.ietf.org/timezones/data/leap-seconds.list";
// and in the tz distribution
static const char* tai_tz_url =
"https://raw.githubusercontent.com/eggert/tz/master/leap-seconds.list";
-// leap-seconds.list has a different format from the USNO one
-
-static const char* tai_usno_url1 =
- "http://maia.usno.navy.mil/ser7/tai-utc.dat";
-static const char* tai_usno_url2 =
- "http://toshi.nofs.navy.mil/ser7/tai-utc.dat";
-/* Last two lines from USNO bulletin, example:
- 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0 S + (MJD - 41317.) X 0.0 S
- 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0 S + (MJD - 41317.) X 0.0
- */
-int ClockTAI::download_offset_task()
+static const char* tai_ietf_cache_file = "/tmp/odr-dabmux-leap-seconds.cache";
+
+int ClockTAI::get_valid_offset()
{
int offset = 0;
bool offset_valid = false;
- // Clear the bulletin
- m_bulletin.str("");
- m_bulletin.clear();
-
- try {
- download_tai_utc_bulletin(tai_ietf_url);
+ if (bulletin_is_valid()) {
+#if TEST
+ etiLog.level(info) << "Bulletin already valid";
+#endif
offset = parse_ietf_bulletin();
offset_valid = true;
}
- catch (std::runtime_error& e)
- {
- etiLog.level(warn) <<
- "TAI-UTC offset from IETF could not be retrieved: " <<
- e.what();
- }
+ else {
+ load_bulletin_from_file(tai_ietf_cache_file);
- if (not offset_valid) {
- try {
- download_tai_utc_bulletin(tai_tz_url);
+ if (bulletin_is_valid()) {
+#if TEST
+ etiLog.level(info) << "Bulletin from file valid";
+#endif
offset = parse_ietf_bulletin();
offset_valid = true;
}
- catch (std::runtime_error& e)
- {
- etiLog.level(warn) <<
- "TAI-UTC offset from TZ distribution could not be retrieved: " <<
- e.what();
- }
- }
-
- if (not offset_valid) {
- try {
- download_tai_utc_bulletin(tai_usno_url1);
- offset = parse_usno_bulletin();
- offset_valid = true;
- }
- catch (std::runtime_error& e)
- {
- etiLog.level(warn) <<
- "TAI-UTC offset from USNO server #1 could not be retrieved: " <<
- e.what();
- }
- }
-
- if (not offset_valid) {
- try {
- download_tai_utc_bulletin(tai_usno_url2);
- offset = parse_usno_bulletin();
- offset_valid = true;
- }
- catch (std::runtime_error& e)
- {
- etiLog.level(warn) <<
- "TAI-UTC offset from USNO server #2 could not be retrieved: " <<
- e.what();
+ else {
+ for (const auto url : {tai_ietf_url, tai_tz_url}) {
+ try {
+ download_tai_utc_bulletin(tai_ietf_url);
+#if TEST
+ etiLog.level(info) << "Load bulletin from " << url;
+#endif
+ if (bulletin_is_valid()) {
+#if TEST
+ etiLog.level(info) << "Bulletin from " << url << " valid";
+#endif
+ offset = parse_ietf_bulletin();
+ offset_valid = true;
+ }
+ }
+ catch (std::runtime_error& e) {
+ etiLog.level(warn) <<
+ "TAI-UTC offset could not be retrieved from " <<
+ url << " : " << e.what();
+ }
+
+ if (offset_valid) {
+ update_cache(tai_ietf_cache_file);
+ break;
+ }
+ }
}
}
@@ -175,7 +158,7 @@ int ClockTAI::get_offset()
#else
// First time we run we must block until we know
// the offset
- m_offset = download_offset_task();
+ m_offset = get_valid_offset();
m_offset_valid = true;
m_bulletin_download_time = std::chrono::system_clock::now();
#endif
@@ -217,7 +200,7 @@ int ClockTAI::get_offset()
etiLog.level(debug) << " Launch async";
#endif
m_offset_future = std::async(std::launch::async,
- &ClockTAI::download_offset_task, this);
+ &ClockTAI::get_valid_offset, this);
}
}
@@ -252,8 +235,6 @@ int ClockTAI::parse_ietf_bulletin()
// The difference between NTP timestamps and unix epoch is 70
// years i.e. 2208988800 seconds
- const int64_t ntp_unix_offset = 2208988800L;
-
boost::regex regex_bulletin(R"(([0-9]+)\s+([0-9]+)\s+#.*)");
time_t now = time(nullptr);
@@ -262,6 +243,9 @@ int ClockTAI::parse_ietf_bulletin()
int tai_utc_offset_valid = false;
+ m_bulletin.clear();
+ m_bulletin.seekg(0);
+
/* We cannot just take the last line, because it might
* be in the future, announcing an upcoming leap second.
*
@@ -285,10 +269,6 @@ int ClockTAI::parse_ietf_bulletin()
std::atol(bulletin_ntp_timestamp.c_str()) - ntp_unix_offset;
const int offset = std::atoi(bulletin_offset.c_str());
-#if TEST
- std::cerr << "Match for line " << line << std::endl;
- std::cerr << " " << timestamp_unix << " < " << now << std::endl;
-#endif
// Ignore entries announcing leap seconds in the future
if (timestamp_unix < now) {
tai_utc_offset = offset;
@@ -311,114 +291,97 @@ int ClockTAI::parse_ietf_bulletin()
return tai_utc_offset;
}
-int ClockTAI::parse_usno_bulletin()
+size_t ClockTAI::fill_bulletin(char *ptr, size_t size, size_t nmemb)
{
- boost::regex regex_bulletin(
- "([0-9]{4}) ([A-Z]{3}) +([0-9]+) =JD +[0-9.]+ +TAI-UTC= *([0-9.]+)");
- /* regex groups:
- * Year Month Day Julian date Offset
- */
+ size_t len = size * nmemb;
+ for (size_t i = 0; i < len; i++) {
+ m_bulletin << ptr[i];
+ }
+ return len;
+}
- const std::array<std::string, 12> months{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
+size_t ClockTAI::fill_bulletin_cb(
+ char *ptr, size_t size, size_t nmemb, void *ctx)
+{
+ return ((ClockTAI*)ctx)->fill_bulletin(ptr, size, nmemb);
+}
- /* I'm not certain about the format they would use if the day is a
- * two-digit number. Will they keep two spaces after the month? The regex
- * should be resilient enough in that case.
- */
+void ClockTAI::load_bulletin_from_file(const char* cache_filename)
+{
+ // Clear the bulletin
+ m_bulletin.str("");
+ m_bulletin.clear();
- time_t now = time(nullptr);
- struct tm *utc_time_now = gmtime(&now);
+ std::ifstream f(cache_filename);
+ if (not f.good()) {
+ return;
+ }
- const int year = utc_time_now->tm_year + 1900;
- const int month = utc_time_now->tm_mon; // January is 0
- if (month < 0 or month > 11) {
- throw std::runtime_error("Invalid value for month");
+ m_bulletin << f.rdbuf();
+ f.close();
+}
+
+void ClockTAI::update_cache(const char* cache_filename)
+{
+ std::ofstream f(cache_filename);
+ if (not f.good()) {
+ throw std::runtime_error("TAI-UTC bulletin open cache for writing");
}
- const int day = utc_time_now->tm_mday;
+ m_bulletin.clear();
+ m_bulletin.seekg(0);
+#if TEST
+ etiLog.level(info) << "Update cache, state:" <<
+ (m_bulletin.eof() ? " eof" : "") <<
+ (m_bulletin.fail() ? " fail" : "") <<
+ (m_bulletin.bad() ? " bad" : "");
+#endif
- int tai_utc_offset = 0;
- bool tai_utc_offset_valid = false;
+ f << m_bulletin.rdbuf();
+ f.close();
+}
- /* We cannot just take the last line, because it might
- * be in the future, announcing an upcoming leap second.
- *
- * So we need to look at the current date, and compare it
- * with the date of the leap second.
- */
- for (std::string line; std::getline(m_bulletin, line); ) {
+bool ClockTAI::bulletin_is_valid()
+{
+ // The bulletin contains one line that specifies an expiration date
+ // in NTP time. If that point in time is in the future, we consider
+ // the bulletin valid.
+ //
+ // The entry looks like this:
+ //#@ 3707596800
+
+ boost::regex regex_expiration(R"(#@\s+([0-9]+))");
+ time_t now = time(nullptr);
+
+ m_bulletin.clear();
+ m_bulletin.seekg(0);
+
+ for (std::string line; std::getline(m_bulletin, line); ) {
boost::smatch bulletin_entry;
- bool is_match = boost::regex_search(line, bulletin_entry, regex_bulletin);
+ bool is_match = boost::regex_search(line, bulletin_entry, regex_expiration);
if (is_match) {
-
- if (bulletin_entry.size() != 5) {
+ if (bulletin_entry.size() != 2) {
throw std::runtime_error(
- "Incorrect number of matched USNO TAI bulletin entries");
- }
- const std::string bulletin_year(bulletin_entry[1]);
- const std::string bulletin_month_name(bulletin_entry[2]);
- const std::string bulletin_day(bulletin_entry[3]);
- const std::string bulletin_offset(bulletin_entry[4]);
-
- const auto match = std::find(months.begin(), months.end(), bulletin_month_name);
- if (match == months.end()) {
- std::stringstream s;
- s << "Month " << bulletin_month_name << " not in array!";
- std::runtime_error(s.str());
+ "Incorrect number of matched TAI IETF bulletin expiration");
}
+ const std::string expiry_data_str(bulletin_entry[1]);
+ const int64_t expiry_unix =
+ std::atol(expiry_data_str.c_str()) - ntp_unix_offset;
- if ( (std::atoi(bulletin_year.c_str()) < year) or
-
- (std::atoi(bulletin_year.c_str()) == year and
- std::distance(months.begin(), match) < month) or
-
- (std::atoi(bulletin_year.c_str()) == year and
- std::distance(months.begin(), match) == month and
- std::atoi(bulletin_day.c_str()) < day) ) {
- tai_utc_offset = std::atoi(bulletin_offset.c_str());
- tai_utc_offset_valid = true;
- }
-#if TEST
- else {
- std::cerr << "Ignoring offset " << bulletin_offset << " at date "
- << bulletin_year << " " << bulletin_month_name << " " <<
- bulletin_day << " in the future" << std::endl;
- }
-#endif
- }
-#if TEST
- else {
- std::cerr << "No match for line " << line << std::endl;
+ return expiry_unix > now;
}
-#endif
- }
-
- if (not tai_utc_offset_valid) {
- throw std::runtime_error("No data in TAI bulletin");
- }
-
- 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);
+ return false;
}
void ClockTAI::download_tai_utc_bulletin(const char* url)
{
+ // Clear the bulletin
+ m_bulletin.str("");
+ m_bulletin.clear();
+
#ifdef HAVE_CURL
CURL *curl;
CURLcode res;
@@ -487,10 +450,12 @@ int main(int argc, char **argv)
while (wait_longer) {
try {
- cerr << "Offset is " << tai.get_offset() << endl;
+ etiLog.level(info) <<
+ "Offset is " << tai.get_offset();
}
catch (std::exception &e) {
- cerr << "Exception " << e.what() << endl;
+ etiLog.level(error) <<
+ "Exception " << e.what();
}
std::this_thread::sleep_for(std::chrono::seconds(2));
diff --git a/src/ClockTAI.h b/src/ClockTAI.h
index 6ab9ab6..99827ba 100644
--- a/src/ClockTAI.h
+++ b/src/ClockTAI.h
@@ -44,10 +44,10 @@
// We can keep this code, maybe for future use
#define SUPPORT_SETTING_CLOCK_TAI 0
-/* Loads, parses and represents TAI-UTC offset information from the USNO bulletin */
+/* Loads, parses and represents TAI-UTC offset information from the IETF bulletin */
class ClockTAI {
public:
- // Fetch the bulletin from the USNO website and return the current
+ // Fetch the bulletin from the IETF website and return the current
// TAI-UTC offset.
// Throws runtime_error on failure.
int get_offset(void);
@@ -59,7 +59,10 @@ class ClockTAI {
#endif
private:
- int download_offset_task(void);
+ // Either retrieve the bulletin from the cache or if necessarly
+ // download it, and calculate the TAI-UTC offset.
+ // Returns the offset.
+ int get_valid_offset(void);
// Download of new bulletin is done asynchronously
std::future<int> m_offset_future;
@@ -71,15 +74,22 @@ class ClockTAI {
std::stringstream m_bulletin;
std::chrono::system_clock::time_point m_bulletin_download_time;
- // Load bulletin into m_bulletin
+ // Load bulletin into m_bulletin from the cache file
+ void load_bulletin_from_file(const char* cache_filename);
+
+ // Update the cache file with the current m_bulletin
+ void update_cache(const char* cache_filename);
+
+ // Verifies the expiration date in the m_bulletin. Returns
+ // true if the bulletin is valid.
+ bool bulletin_is_valid(void);
+
+ // Load bulletin into m_bulletin from the URL
void download_tai_utc_bulletin(const char* url);
// read TAI offset from m_bulletin in IETF format
int parse_ietf_bulletin(void);
- // read TAI offset from m_bulletin in USNO format
- int parse_usno_bulletin(void);
-
// callback that receives data from cURL
size_t fill_bulletin(char *ptr, size_t size, size_t nmemb);
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 8c7e6b4..3f977ad 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -511,6 +511,30 @@ void parse_ptree(
serviceuid;
}
+ try {
+ auto oelist = pt_service.get<std::string>("other_ensembles", "");
+ vector<string> oe_string_list;
+ boost::split(oe_string_list, oelist, boost::is_any_of(","));
+
+ for (const auto& oe_string : oe_string_list) {
+ if (oe_string == "") {
+ continue;
+ }
+ try {
+ service->other_ensembles.push_back(hexparse(oe_string));
+ }
+ catch (std::logic_error& e) {
+ etiLog.level(warn) << "Cannot parse '" << oelist <<
+ "' announcement clusters for service " << serviceuid <<
+ ": " << e.what();
+ }
+ }
+ }
+ catch (ptree_error& e) {
+ etiLog.level(info) << "No other_sensmbles information for Service " <<
+ serviceuid;
+ }
+
int success = -5;
string servicelabel = pt_service.get<string>("label");
diff --git a/src/Makefile.am b/src/Makefile.am
index 4ee8792..12934f0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -96,6 +96,7 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \
fig/FIG0_18.cpp fig/FIG0_18.h \
fig/FIG0_19.cpp fig/FIG0_19.h \
fig/FIG0_21.cpp fig/FIG0_21.h \
+ fig/FIG0_24.cpp fig/FIG0_24.h \
fig/FIG1.cpp fig/FIG1.h \
fig/FIGCarousel.cpp fig/FIGCarousel.h \
fig/TransitionHandler.h \
diff --git a/src/MuxElements.h b/src/MuxElements.h
index 7f1b828..6a1ca74 100644
--- a/src/MuxElements.h
+++ b/src/MuxElements.h
@@ -406,6 +406,9 @@ class DabService : public RemoteControllable
uint16_t ASu = 0;
std::vector<uint8_t> clusters;
+ // Ensembles in which this service is also available, used for FIG0/24
+ std::vector<uint16_t> other_ensembles;
+
subchannel_type_t getType(const std::shared_ptr<dabEnsemble> ensemble) const;
bool isProgramme(const std::shared_ptr<dabEnsemble>& ensemble) const;
unsigned char nbComponent(const std::vector<DabComponent*>& components) const;
diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h
index d8de751..856b5ee 100644
--- a/src/fig/FIG0.h
+++ b/src/fig/FIG0.h
@@ -40,4 +40,5 @@
#include "fig/FIG0_18.h"
#include "fig/FIG0_19.h"
#include "fig/FIG0_21.h"
+#include "fig/FIG0_24.h"
diff --git a/src/fig/FIG0_24.cpp b/src/fig/FIG0_24.cpp
new file mode 100644
index 0000000..a790440
--- /dev/null
+++ b/src/fig/FIG0_24.cpp
@@ -0,0 +1,184 @@
+/*
+ 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) 2017
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ 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/>.
+*/
+
+#include "fig/FIG0structs.h"
+#include "fig/FIG0_24.h"
+#include "utils.h"
+
+/* FIG0/24 allows us to announce if a service is available in another ensemble.
+ * Things we do not support:
+ *
+ * - The CEI using length=0, because this information is not available in the
+ * remote control, and there is no runtime changes.
+ * - Announcing information about other ensembles (OE=1)
+ */
+
+namespace FIC {
+
+struct FIGtype0_24_audioservice {
+ uint16_t SId;
+ uint8_t Length:4;
+ uint8_t CAId:3;
+ uint8_t rfa:1;
+} PACKED;
+
+struct FIGtype0_24_dataservice {
+ uint32_t SId;
+ uint8_t Length:4;
+ uint8_t CAId:3;
+ uint8_t rfa:1;
+} PACKED;
+
+
+FIG0_24::FIG0_24(FIGRuntimeInformation *rti) :
+ m_rti(rti),
+ m_initialised(false),
+ m_inserting_audio_not_data(false)
+{
+}
+
+FillStatus FIG0_24::fill(uint8_t *buf, size_t max_size)
+{
+#define FIG0_24_TRACE discard
+ using namespace std;
+
+ FillStatus fs;
+ FIGtype0* fig0 = nullptr;
+ ssize_t remaining = max_size;
+
+ const auto ensemble = m_rti->ensemble;
+
+ etiLog.level(FIG0_24_TRACE) << "FIG0_24::fill init " << (m_initialised ? 1 : 0) <<
+ " ********************************";
+
+ if (not m_initialised) {
+ serviceFIG0_24 = ensemble->services.begin();
+ m_initialised = true;
+ }
+
+ const auto last_service = ensemble->services.end();
+
+ // Rotate through the subchannels until there is no more
+ // space
+ for (; serviceFIG0_24 != last_service; ++serviceFIG0_24) {
+ const auto type = (*serviceFIG0_24)->getType(ensemble);
+ const auto isProgramme = (*serviceFIG0_24)->isProgramme(ensemble);
+
+ etiLog.log(FIG0_24_TRACE, "FIG0_24::fill loop SId=%04x %s/%s",
+ (*serviceFIG0_24)->id,
+ m_inserting_audio_not_data ? "AUDIO" : "DATA",
+ type == subchannel_type_t::Audio ? "Audio" :
+ type == subchannel_type_t::Packet ? "Packet" :
+ type == subchannel_type_t::DataDmb ? "DataDmb" :
+ type == subchannel_type_t::Fidc ? "Fidc" : "?");
+
+ // Skip services that are not in other ensembles
+ if ((*serviceFIG0_24)->other_ensembles.empty()) {
+ continue;
+ }
+
+ const ssize_t required_size =
+ (isProgramme ? 2 : 4) + 1 +
+ (*serviceFIG0_24)->other_ensembles.size() * 2;
+
+
+ if (fig0 == nullptr) {
+ etiLog.level(FIG0_24_TRACE) << "FIG0_24::fill header";
+ if (remaining < 2 + required_size) {
+ etiLog.level(FIG0_24_TRACE) << "FIG0_24::fill header no place" <<
+ " rem=" << remaining << " req=" << 2 + required_size;
+ break;
+ }
+ fig0 = (FIGtype0*)buf;
+
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ // CN according to ETSI TS 103 176, Clause 5.3.4.1
+ fig0->CN = (serviceFIG0_24 == ensemble->services.begin() ? 0 : 1);
+ fig0->OE = 0;
+ fig0->PD = isProgramme ? 0 : 1;
+ fig0->Extension = 24;
+ buf += 2;
+ remaining -= 2;
+ }
+ else if (remaining < required_size) {
+ etiLog.level(FIG0_24_TRACE) << "FIG0_24::fill no place" <<
+ " rem=" << remaining << " req=" << required_size;
+ break;
+ }
+
+ if (type == subchannel_type_t::Audio) {
+ auto fig0_2serviceAudio = (FIGtype0_24_audioservice*)buf;
+
+ fig0_2serviceAudio->SId = htons((*serviceFIG0_24)->id);
+ fig0_2serviceAudio->rfa = 0;
+ fig0_2serviceAudio->CAId = 0;
+ fig0_2serviceAudio->Length = (*serviceFIG0_24)->other_ensembles.size();
+ buf += 3;
+ fig0->Length += 3;
+ remaining -= 3;
+
+ etiLog.log(FIG0_24_TRACE, "FIG0_24::fill audio SId=%04x",
+ (*serviceFIG0_24)->id);
+ }
+ else {
+ auto fig0_2serviceData = (FIGtype0_24_dataservice*)buf;
+
+ fig0_2serviceData->SId = htonl((*serviceFIG0_24)->id);
+ fig0_2serviceData->rfa = 0;
+ fig0_2serviceData->CAId = 0;
+ fig0_2serviceData->Length = (*serviceFIG0_24)->other_ensembles.size();
+ buf += 4;
+ fig0->Length += 4;
+ remaining -= 4;
+
+ etiLog.log(FIG0_24_TRACE, "FIG0_24::fill data SId=%04x",
+ (*serviceFIG0_24)->id);
+ }
+
+ for (const uint16_t oe : (*serviceFIG0_24)->other_ensembles) {
+ buf[0] = oe >> 8;
+ buf[1] = oe & 0xFF;
+
+ buf += 2;
+ fig0->Length += 2;
+ remaining -= 2;
+ }
+ }
+
+ if (serviceFIG0_24 == last_service) {
+ etiLog.log(FIG0_24_TRACE, "FIG0_24::loop reached last");
+ fs.complete_fig_transmitted = true;
+ m_initialised = false;
+ }
+
+ etiLog.log(FIG0_24_TRACE, "FIG0_24::loop end complete=%d",
+ fs.complete_fig_transmitted ? 1 : 0);
+
+ fs.num_bytes_written = max_size - remaining;
+ return fs;
+}
+
+}
diff --git a/src/fig/FIG0_24.h b/src/fig/FIG0_24.h
new file mode 100644
index 0000000..4ccbd0c
--- /dev/null
+++ b/src/fig/FIG0_24.h
@@ -0,0 +1,53 @@
+/*
+ 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) 2017
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ 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/>.
+*/
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include "FIG.h"
+
+namespace FIC {
+/* The OE Services feature is used to identify the services currently carried
+ * in other DAB ensembles.
+ */
+class FIG0_24 : public IFIG
+{
+ public:
+ FIG0_24(FIGRuntimeInformation* rti);
+ virtual FillStatus fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::E; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 24; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ bool m_inserting_audio_not_data;
+ std::vector<std::shared_ptr<DabService> >::iterator serviceFIG0_24;
+};
+
+}
diff --git a/src/fig/FIGCarousel.cpp b/src/fig/FIGCarousel.cpp
index 6d89ad8..e32ea39 100644
--- a/src/fig/FIGCarousel.cpp
+++ b/src/fig/FIGCarousel.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2016
+ Copyright (C) 2017
Matthias P. Braendli, matthias.braendli@mpb.li
Implementation of the FIG carousel to schedule the FIGs into the
@@ -76,7 +76,8 @@ FIGCarousel::FIGCarousel(std::shared_ptr<dabEnsemble> ensemble) :
m_fig1_5(&m_rti),
m_fig0_18(&m_rti),
m_fig0_19(&m_rti),
- m_fig0_21(&m_rti)
+ m_fig0_21(&m_rti),
+ m_fig0_24(&m_rti)
{
/* Complete MCI except FIG0/8 should be in FIB0.
* EN 300 401 V1.4.1 Clause 6.1
@@ -111,6 +112,7 @@ FIGCarousel::FIGCarousel(std::shared_ptr<dabEnsemble> ensemble) :
load_and_allocate(m_fig0_18, FIBAllocation::FIB_ANY);
load_and_allocate(m_fig0_19, FIBAllocation::FIB_ANY);
load_and_allocate(m_fig0_21, FIBAllocation::FIB_ANY);
+ load_and_allocate(m_fig0_24, FIBAllocation::FIB_ANY);
}
void FIGCarousel::load_and_allocate(IFIG& fig, FIBAllocation fib)
diff --git a/src/fig/FIGCarousel.h b/src/fig/FIGCarousel.h
index ec1611d..5e102ad 100644
--- a/src/fig/FIGCarousel.h
+++ b/src/fig/FIGCarousel.h
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2016
+ Copyright (C) 2017
Matthias P. Braendli, matthias.braendli@mpb.li
Implementation of the FIG carousel to schedule the FIGs into the
@@ -102,6 +102,7 @@ class FIGCarousel {
FIG0_18 m_fig0_18;
FIG0_19 m_fig0_19;
FIG0_21 m_fig0_21;
+ FIG0_24 m_fig0_24;
};
} // namespace FIC