diff options
-rw-r--r-- | .travis.yml | 5 | ||||
-rw-r--r-- | doc/advanced.mux | 4 | ||||
-rw-r--r-- | doc/servicelinking.mux | 5 | ||||
-rw-r--r-- | src/ClockTAI.cpp | 281 | ||||
-rw-r--r-- | src/ClockTAI.h | 24 | ||||
-rw-r--r-- | src/ConfigParser.cpp | 24 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/MuxElements.h | 3 | ||||
-rw-r--r-- | src/fig/FIG0.h | 1 | ||||
-rw-r--r-- | src/fig/FIG0_24.cpp | 184 | ||||
-rw-r--r-- | src/fig/FIG0_24.h | 53 | ||||
-rw-r--r-- | src/fig/FIGCarousel.cpp | 6 | ||||
-rw-r--r-- | src/fig/FIGCarousel.h | 3 |
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 |