summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--Makefile.am2
-rw-r--r--doc/advanced.mux5
-rw-r--r--doc/example.mux9
-rw-r--r--lib/charset/charset.cpp4
-rw-r--r--lib/charset/charset.h4
-rw-r--r--src/ConfigParser.cpp10
-rw-r--r--src/DabMultiplexer.cpp54
-rw-r--r--src/DabMultiplexer.h1
-rw-r--r--src/MuxElements.cpp61
-rw-r--r--src/MuxElements.h80
-rw-r--r--src/dabOutput/edi/TagItems.cpp10
-rw-r--r--src/dabOutput/edi/TagItems.h8
-rw-r--r--src/fig/FIG0_10.cpp16
-rw-r--r--src/fig/FIG0_9.cpp5
-rw-r--r--src/fig/FIG1.cpp27
-rw-r--r--src/fig/FIG1.h2
-rw-r--r--src/fig/FIG2.cpp406
-rw-r--r--src/fig/FIG2.h163
-rw-r--r--src/fig/FIGCarousel.cpp11
-rw-r--r--src/fig/FIGCarousel.h5
-rw-r--r--src/utils.cpp34
22 files changed, 782 insertions, 143 deletions
diff --git a/.gitignore b/.gitignore
index 0492be0..ba13c42 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,10 +4,10 @@
.*.swp
.directory
-src/odr-zmq2farsync
-src/zmqinput-keygen
-src/odr-bridgetest
-src/odr-dabmux
+odr-dabmux
+odr-zmq2farsync
+odr-zmq2edi
+zmqinput-keygen
src/dabOutput/.deps/
src/dabOutput/.dirstamp
diff --git a/Makefile.am b/Makefile.am
index 64aa02b..fb6bae3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -160,6 +160,8 @@ odr_dabmux_SOURCES =src/DabMux.cpp \
src/fig/FIG0_24.h \
src/fig/FIG1.cpp \
src/fig/FIG1.h \
+ src/fig/FIG2.cpp \
+ src/fig/FIG2.h \
src/fig/FIGCarousel.cpp \
src/fig/FIGCarousel.h \
src/fig/TransitionHandler.h \
diff --git a/doc/advanced.mux b/doc/advanced.mux
index 974df78..ab145fa 100644
--- a/doc/advanced.mux
+++ b/doc/advanced.mux
@@ -31,6 +31,11 @@ general {
; This also enables time encoding using the MNSC.
tist false
+ ; On startup, EDI time is initialised to system time. If you want
+ ; to add an offset, uncomment the following line and give a number
+ ; in seconds.
+ ; tist_edioffset 0
+
; The management server is a simple TCP server that can present
; statistics data (buffers, overruns, underruns, etc)
; which can then be graphed a tool like Munin
diff --git a/doc/example.mux b/doc/example.mux
index 48a89dc..caa4d2d 100644
--- a/doc/example.mux
+++ b/doc/example.mux
@@ -107,6 +107,8 @@ ensemble {
; automatic calculation of the local time offset, set the environment variable TZ
; to your timezone (e.g. TZ=Europe/Rome) before you launch ODR-DabMux
+ ; FIG1 labels are given with the 'label' and 'shortlabel' keys.
+ ;
; All labels are maximum 16 characters in length.
; Labels that are valid utf-8 will be converted to EBU Latin Character set
; as defined in ETSI TS 101 756, in Annex C. If it's not valid utf-8, the
@@ -118,6 +120,11 @@ ensemble {
; be longer than 8 characters. If omitted, it will be truncated from the
; label
shortlabel "ODR"
+
+ ; The FIG2 label can be up to 16 characters long, and is in UTF-8.
+ ;fig2_label "ÓpêñÐigıtålRadiō"
+
+ ; There is no FIG2 short label for the moment.
}
; Definition of DAB services
@@ -127,7 +134,7 @@ services {
srv-fu {
id 0x4daa
label "Fünk"
- ; You can define a shortlabel too.
+ ; You can define a shortlabel and a fig2_label too.
}
srv-ri {
; If your ensemble contains a service from another country,
diff --git a/lib/charset/charset.cpp b/lib/charset/charset.cpp
index 1abc097..f5eb216 100644
--- a/lib/charset/charset.cpp
+++ b/lib/charset/charset.cpp
@@ -73,7 +73,7 @@ CharsetConverter::CharsetConverter()
}
}
-std::string CharsetConverter::convert(std::string line_utf8, bool up_to_first_error)
+std::string CharsetConverter::utf8_to_ebu(std::string line_utf8, bool up_to_first_error)
{
string::iterator end_it;
@@ -107,7 +107,7 @@ std::string CharsetConverter::convert(std::string line_utf8, bool up_to_first_er
return encoded_line;
}
-std::string CharsetConverter::convert_ebu_to_utf8(const std::string& str)
+std::string CharsetConverter::ebu_to_utf8(const std::string& str)
{
string utf8_str;
for (const uint8_t c : str) {
diff --git a/lib/charset/charset.h b/lib/charset/charset.h
index 8476ee7..5f5899e 100644
--- a/lib/charset/charset.h
+++ b/lib/charset/charset.h
@@ -39,12 +39,12 @@ class CharsetConverter
* stream. If up_to_first_error is set, convert as much text as possible.
* If false, raise an utf8::exception in case of conversion errors.
*/
- std::string convert(std::string line_utf8, bool up_to_first_error = true);
+ std::string utf8_to_ebu(std::string line_utf8, bool up_to_first_error = true);
/*! Convert a EBU Latin byte stream to a UTF-8 encoded string.
* Invalid input characters are converted to ⁇ (unicode U+2047).
*/
- std::string convert_ebu_to_utf8(const std::string& str);
+ std::string ebu_to_utf8(const std::string& str);
private:
// Representation of the table in 32-bit unicode
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 0bf3070..62d81c7 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -412,7 +412,7 @@ static void parse_general(ptree& pt,
}
int success = -5;
- string ensemble_label = pt_ensemble.get<string>("label");
+ string ensemble_label = pt_ensemble.get<string>("label", "");
string ensemble_short_label(ensemble_label);
try {
ensemble_short_label = pt_ensemble.get<string>("shortlabel");
@@ -447,6 +447,8 @@ static void parse_general(ptree& pt,
abort();
}
+ ensemble->label.setFIG2Label(pt_ensemble.get<string>("fig2_label", ""));
+
try {
ptree pt_announcements = pt_ensemble.get_child("announcements");
for (auto announcement : pt_announcements) {
@@ -549,7 +551,7 @@ void parse_ptree(
int success = -5;
- string servicelabel = pt_service.get<string>("label");
+ string servicelabel = pt_service.get<string>("label", "");
string serviceshortlabel(servicelabel);
try {
serviceshortlabel = pt_service.get<string>("shortlabel");
@@ -584,6 +586,8 @@ void parse_ptree(
abort();
}
+ service->label.setFIG2Label(pt_service.get<string>("fig2_label", ""));
+
service->id = new_service_id;
service->ecc = hexparse(pt_service.get("ecc", "0"));
service->pty_settings.pty = hexparse(pt_service.get("pty", "0"));
@@ -744,6 +748,8 @@ void parse_ptree(
abort();
}
+ component->label.setFIG2Label(pt_comp.get<string>("fig2_label", ""));
+
if (component->SCIdS == 0 and not component->label.long_label().empty()) {
etiLog.level(warn) << "Primary component " << component->uid <<
" has label set. Since V2.1.1 of the specification, only secondary"
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
index e84b58c..f56a4ff 100644
--- a/src/DabMultiplexer.cpp
+++ b/src/DabMultiplexer.cpp
@@ -92,8 +92,10 @@ DabMultiplexer::DabMultiplexer(
m_clock_tai(split_pipe_separated_string(pt.get("general.tai_clock_bulletins", ""))),
fig_carousel(ensemble)
{
- RC_ADD_PARAMETER(frames,
- "Show number of frames generated [read-only]");
+ gettimeofday(&mnsc_time, nullptr);
+
+ RC_ADD_PARAMETER(frames, "Show number of frames generated [read-only]");
+ RC_ADD_PARAMETER(tist_edioffset, "EDI Time offset in seconds");
rcs.enrol(&m_clock_tai);
}
@@ -169,7 +171,7 @@ void DabMultiplexer::prepare(bool require_tai_clock)
throw MuxInitException();
}
- auto last_subchannel = *(ensemble->subchannels.end() - 1);
+ const auto last_subchannel = *(ensemble->subchannels.end() - 1);
if (last_subchannel->startAddress + last_subchannel->getSizeCu() > 864) {
etiLog.log(error, "Total size in CU exceeds 864");
@@ -191,6 +193,7 @@ void DabMultiplexer::prepare(bool require_tai_clock)
// Try to load offset once
bool tist_enabled = m_pt.get("general.tist", false);
+ m_tist_edioffset = m_pt.get<int>("general.tist_edioffset", 0);
m_tai_clock_required = (tist_enabled and edi_conf.enabled()) or require_tai_clock;
@@ -198,7 +201,7 @@ void DabMultiplexer::prepare(bool require_tai_clock)
try {
m_clock_tai.get_offset();
}
- catch (std::runtime_error& e) {
+ catch (const std::runtime_error& e) {
etiLog.level(error) <<
"Could not initialise TAI clock properly. "
"Do you have a working internet connection?";
@@ -400,9 +403,6 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
unsigned char etiFrame[6144];
unsigned short index = 0;
- vec_sp_service::iterator service;
- vec_sp_component::iterator component;
-
// FIC Length, DAB Mode I, II, IV -> FICL = 24, DAB Mode III -> FICL = 32
unsigned FICL =
(ensemble->transmission_mode == TransmissionMode_e::TM_III ? 32 : 24);
@@ -546,7 +546,8 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
eoh->MNSC = 0;
- struct tm *time_tm = gmtime(&mnsc_time.tv_sec);
+ struct tm time_tm;
+ gmtime_r(&mnsc_time.tv_sec, &time_tm);
switch (fc->FP & 0x3)
{
case 0:
@@ -567,7 +568,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
case 1:
{
eti_MNSC_TIME_1 *mnsc = (eti_MNSC_TIME_1 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
+ mnsc->setFromTime(&time_tm);
mnsc->accuracy = 1;
mnsc->sync_to_frame = 1;
}
@@ -575,13 +576,13 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
case 2:
{
eti_MNSC_TIME_2 *mnsc = (eti_MNSC_TIME_2 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
+ mnsc->setFromTime(&time_tm);
}
break;
case 3:
{
eti_MNSC_TIME_3 *mnsc = (eti_MNSC_TIME_3 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
+ mnsc->setFromTime(&time_tm);
}
break;
}
@@ -670,19 +671,18 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
edi_tagDETI.tsta = 0xffffff;
}
- edi_tagDETI.atstf = 1;
- edi_tagDETI.utco = 0;
- edi_tagDETI.seconds = 0;
#if HAVE_OUTPUT_EDI
- try {
- bool tist_enabled = m_pt.get("general.tist", false);
-
- if (tist_enabled and m_tai_clock_required) {
- edi_tagDETI.set_seconds(edi_time);
+ const bool tist_enabled = m_pt.get("general.tist", false);
- // In case get_offset fails, we still want to update the EDI seconds
+ if (tist_enabled and m_tai_clock_required) {
+ try {
const auto tai_utc_offset = m_clock_tai.get_offset();
- edi_tagDETI.set_tai_utc_offset(tai_utc_offset);
+
+ edi_tagDETI.set_edi_time(edi_time +
+ std::chrono::seconds(m_tist_edioffset),
+ tai_utc_offset);
+
+ edi_tagDETI.atstf = true;
for (auto output : outputs) {
shared_ptr<OutputMetadata> md_utco =
@@ -698,9 +698,9 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
output->setMetadata(md_dlfc);
}
}
- }
- catch (std::runtime_error& e) {
- etiLog.level(error) << "Could not get UTC-TAI offset for EDI timestamp";
+ catch (const std::runtime_error& e) {
+ etiLog.level(error) << "Could not get UTC-TAI offset for EDI timestamp";
+ }
}
#endif
@@ -872,6 +872,9 @@ void DabMultiplexer::set_parameter(const std::string& parameter,
" is read-only";
throw ParameterError(ss.str());
}
+ else if (parameter == "tist_edioffset") {
+ m_tist_edioffset = std::stoi(value);
+ }
else {
stringstream ss;
ss << "Parameter '" << parameter <<
@@ -887,6 +890,9 @@ const std::string DabMultiplexer::get_parameter(const std::string& parameter) co
if (parameter == "frames") {
ss << currentFrame;
}
+ else if (parameter == "tist_edioffset") {
+ ss << m_tist_edioffset;
+ }
else {
ss << "Parameter '" << parameter <<
"' is not exported by controllable " << get_rc_name();
diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h
index 499e023..127ecfb 100644
--- a/src/DabMultiplexer.h
+++ b/src/DabMultiplexer.h
@@ -95,6 +95,7 @@ class DabMultiplexer : public RemoteControllable {
std::shared_ptr<dabEnsemble> ensemble;
+ int m_tist_edioffset = 0;
bool m_tai_clock_required;
ClockTAI m_clock_tai;
diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp
index dcd408c..44e097c 100644
--- a/src/MuxElements.cpp
+++ b/src/MuxElements.cpp
@@ -184,13 +184,13 @@ const string AnnouncementCluster::get_parameter(const string& parameter) const
int DabLabel::setLabel(const std::string& label)
{
try {
- auto ebu_label = charset_converter.convert(label, false);
+ auto ebu_label = charset_converter.utf8_to_ebu(label, false);
size_t len = ebu_label.length();
if (len > DABLABEL_LENGTH) {
return -3;
}
- m_label = ebu_label;
+ m_fig1_label = ebu_label;
}
catch (const utf8::exception& e) {
etiLog.level(warn) << "Failed to convert label '" << label <<
@@ -201,10 +201,10 @@ int DabLabel::setLabel(const std::string& label)
return -3;
}
- m_label = label;
+ m_fig1_label = label;
}
- m_flag = 0xFF00; // truncate the label to the eight first characters
+ m_fig1_flag = 0xFF00; // truncate the label to the eight first characters
return 0;
}
@@ -212,23 +212,23 @@ int DabLabel::setLabel(const std::string& label)
int DabLabel::setLabel(const std::string& label, const std::string& short_label)
{
DabLabel newlabel;
- newlabel.m_flag = 0xFF00;
+ newlabel.m_fig1_flag = 0xFF00;
try {
- newlabel.m_label = charset_converter.convert(label, false);
+ newlabel.m_fig1_label = charset_converter.utf8_to_ebu(label, false);
- size_t len = newlabel.m_label.length();
+ size_t len = newlabel.m_fig1_label.length();
if (len > DABLABEL_LENGTH) {
return -3;
}
- int flag = newlabel.setShortLabel(
- charset_converter.convert(short_label, false));
+ int flag = newlabel.setFIG1ShortLabel(
+ charset_converter.utf8_to_ebu(short_label, false));
if (flag < 0) {
return flag;
}
- m_flag = flag & 0xFFFF;
+ m_fig1_flag = flag & 0xFFFF;
}
catch (const utf8::exception& e) {
etiLog.level(warn) << "Failed to convert label '" << label <<
@@ -241,8 +241,8 @@ int DabLabel::setLabel(const std::string& label, const std::string& short_label)
return -3;
}
- newlabel.m_label = label;
- newlabel.m_flag = 0xFF00;
+ newlabel.m_fig1_label = label;
+ newlabel.m_fig1_flag = 0xFF00;
int result = newlabel.setLabel(label);
if (result < 0) {
@@ -250,16 +250,16 @@ int DabLabel::setLabel(const std::string& label, const std::string& short_label)
}
/* First check if we can actually create the short label */
- int flag = newlabel.setShortLabel(short_label);
+ int flag = newlabel.setFIG1ShortLabel(short_label);
if (flag < 0) {
return flag;
}
- m_flag = flag & 0xFFFF;
+ m_fig1_flag = flag & 0xFFFF;
}
// short label is valid.
- m_label = newlabel.m_label;
+ m_fig1_label = newlabel.m_fig1_label;
return 0;
}
@@ -280,7 +280,7 @@ int DabLabel::setLabel(const std::string& label, const std::string& short_label)
* -1 if the short_label is not a representable
* -2 if the short_label is too long
*/
-int DabLabel::setShortLabel(const std::string& slabel)
+int DabLabel::setFIG1ShortLabel(const std::string& slabel)
{
const char* slab = slabel.c_str();
uint16_t flag = 0x0;
@@ -288,8 +288,8 @@ int DabLabel::setShortLabel(const std::string& slabel)
/* Iterate over the label and set the bits in the flag
* according to the characters in the slabel
*/
- for (size_t i = 0; i < m_label.size(); ++i) {
- if (*slab == m_label[i]) {
+ for (size_t i = 0; i < m_fig1_label.size(); ++i) {
+ if (*slab == m_fig1_label[i]) {
flag |= 0x8000 >> i;
if (*(++slab) == '\0') {
break;
@@ -321,26 +321,37 @@ int DabLabel::setShortLabel(const std::string& slabel)
const string DabLabel::long_label() const
{
- return charset_converter.convert_ebu_to_utf8(m_label);
+ return charset_converter.ebu_to_utf8(m_fig1_label);
}
const string DabLabel::short_label() const
{
stringstream shortlabel;
- for (size_t i = 0; i < m_label.size(); ++i) {
- if (m_flag & 0x8000 >> i) {
- shortlabel << m_label[i];
+ for (size_t i = 0; i < m_fig1_label.size(); ++i) {
+ if (m_fig1_flag & 0x8000 >> i) {
+ shortlabel << m_fig1_label[i];
}
}
- return charset_converter.convert_ebu_to_utf8(shortlabel.str());
+ return charset_converter.ebu_to_utf8(shortlabel.str());
+}
+
+const string DabLabel::fig2_label() const
+{
+ return m_fig2_label;
+}
+
+int DabLabel::setFIG2Label(const std::string& label)
+{
+ m_fig2_label = label;
+ return 0;
}
void DabLabel::writeLabel(uint8_t* buf) const
{
memset(buf, ' ', DABLABEL_LENGTH);
- if (m_label.size() <= DABLABEL_LENGTH) {
- std::copy(m_label.begin(), m_label.end(), (char*)buf);
+ if (m_fig1_label.size() <= DABLABEL_LENGTH) {
+ std::copy(m_fig1_label.begin(), m_fig1_label.end(), (char*)buf);
}
}
diff --git a/src/MuxElements.h b/src/MuxElements.h
index be10bf9..b2be73f 100644
--- a/src/MuxElements.h
+++ b/src/MuxElements.h
@@ -167,27 +167,50 @@ class DabLabel
*/
int setLabel(const std::string& label);
+ /* Set the FIG2 label. label must be UTF-8.
+ *
+ * returns: 0 on success
+ */
+ int setFIG2Label(const std::string& label);
+
/* Write the label to the 16-byte buffer given in buf
* In the DAB standard, the label is 16 bytes long, and is
* padded using spaces.
*/
void writeLabel(uint8_t* buf) const;
- uint16_t flag() const { return m_flag; }
+ // For FIG 1
+ bool has_fig1_label() const { return not m_fig1_label.empty(); };
+ uint16_t flag() const { return m_fig1_flag; }
const std::string long_label() const;
const std::string short_label() const;
+ // For FIG 2
+ bool has_fig2_label() const { return not m_fig2_label.empty(); };
+ const std::string fig2_label() const;
+
+ /* FIG 2 labels are either in UCS-2 or in UTF-8. Because there are upcoming
+ * changes in the spec regarding the encoding of FIG2 (currently in draft
+ * ETSI TS 103 176 v2.2.1), the character flag is not implemented yet.
+ *
+ * Both FIG 1 and FIG 2 labels can be sent, and receiver will show the one
+ * they support.
+ */
+
private:
+ /* The m_fig1_label is not padded in any way. Stored in EBU Latin Charset */
+ std::string m_fig1_label;
+
/* The flag field selects which label characters make
* up the short label
*/
- uint16_t m_flag = 0xFFFF;
+ uint16_t m_fig1_flag = 0xFFFF;
- /* The m_label is not padded in any way */
- std::string m_label;
+ /* FIG2 label, stored in UTF-8. TODO: support UCS-2 */
+ std::string m_fig2_label;
/* Checks and calculates the flag. slabel must be EBU Latin Charset */
- int setShortLabel(const std::string& slabel);
+ int setFIG1ShortLabel(const std::string& slabel);
};
@@ -295,15 +318,7 @@ class DabSubchannel
{
public:
DabSubchannel(std::string& uid) :
- uid(uid),
- input(),
- id(0),
- type(subchannel_type_t::DABAudio),
- startAddress(0),
- bitrate(0),
- protection()
- {
- }
+ uid(uid) { }
// Calculate subchannel size in number of CU
unsigned short getSizeCu(void) const;
@@ -321,20 +336,17 @@ public:
std::string inputUri;
std::shared_ptr<Inputs::InputBase> input;
- unsigned char id;
- subchannel_type_t type;
- uint16_t startAddress;
- uint16_t bitrate;
- dabProtection protection;
+ unsigned char id = 0;
+ subchannel_type_t type = subchannel_type_t::DABAudio;
+ uint16_t startAddress = 0;
+ uint16_t bitrate = 0;
+ struct dabProtection protection;
};
struct dabAudioComponent {
- dabAudioComponent() :
- uaType(0xFFFF) {}
-
- uint16_t uaType; // User Application Type
+ uint16_t uaType = 0xFFFF; // User Application Type
};
@@ -347,16 +359,10 @@ struct dabFidcComponent {
struct dabPacketComponent {
- dabPacketComponent() :
- id(0),
- address(0),
- appType(0xFFFF),
- datagroup(false) { }
-
- uint16_t id;
- uint16_t address;
- uint16_t appType;
- bool datagroup;
+ uint16_t id = 0;
+ uint16_t address = 0;
+ uint16_t appType = 0xFFFF;
+ bool datagroup = false;
};
class DabComponent : public RemoteControllable
@@ -372,10 +378,10 @@ class DabComponent : public RemoteControllable
std::string uid;
DabLabel label;
- uint32_t serviceId;
- uint8_t subchId;
- uint8_t type;
- uint8_t SCIdS;
+ uint32_t serviceId = 0;
+ uint8_t subchId = 0;
+ uint8_t type = 0;
+ uint8_t SCIdS = 0;
dabAudioComponent audio;
dabDataComponent data;
diff --git a/src/dabOutput/edi/TagItems.cpp b/src/dabOutput/edi/TagItems.cpp
index ed8517d..631b88d 100644
--- a/src/dabOutput/edi/TagItems.cpp
+++ b/src/dabOutput/edi/TagItems.cpp
@@ -132,12 +132,14 @@ std::vector<uint8_t> TagDETI::Assemble()
return packet;
}
-void TagDETI::set_seconds(std::chrono::system_clock::time_point t)
+void TagDETI::set_edi_time(const std::chrono::system_clock::time_point& t, int tai_utc_offset)
{
- std::time_t posix_timestamp_1_jan_2000 = 946684800;
- seconds = std::chrono::system_clock::to_time_t(t) - posix_timestamp_1_jan_2000;
-}
+ utco = tai_utc_offset - 32;
+
+ const std::time_t posix_timestamp_1_jan_2000 = 946684800;
+ seconds = std::chrono::system_clock::to_time_t(t) - posix_timestamp_1_jan_2000 + utco;
+}
std::vector<uint8_t> TagESTn::Assemble()
{
diff --git a/src/dabOutput/edi/TagItems.h b/src/dabOutput/edi/TagItems.h
index 19284d2..8666053 100644
--- a/src/dabOutput/edi/TagItems.h
+++ b/src/dabOutput/edi/TagItems.h
@@ -85,15 +85,15 @@ class TagDETI : public TagItem
*/
uint8_t utco = 0;
- void set_tai_utc_offset(int tai_utc_offset) { utco = tai_utc_offset - 32; }
+ /* Update the EDI time. t is in UTC */
+ void set_edi_time(const std::chrono::system_clock::time_point &t, int tai_utc_offset);
/* The number of SI seconds since 2000-01-01 T 00:00:00 UTC as an
- * unsigned 32-bit quantity
+ * unsigned 32-bit quantity. Contrary to POSIX, this value also
+ * counts leap seconds.
*/
uint32_t seconds = 0;
- void set_seconds(std::chrono::system_clock::time_point t);
-
/* 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
diff --git a/src/fig/FIG0_10.cpp b/src/fig/FIG0_10.cpp
index 3ce2847..56ce9fb 100644
--- a/src/fig/FIG0_10.cpp
+++ b/src/fig/FIG0_10.cpp
@@ -101,22 +101,22 @@ FillStatus FIG0_10::fill(uint8_t *buf, size_t max_size)
buf += 2;
remaining -= 2;
- tm* timeData;
+ struct tm timeData;
time_t dab_time_seconds = 0;
uint32_t dab_time_millis = 0;
get_dab_time(&dab_time_seconds, &dab_time_millis);
- timeData = gmtime(&dab_time_seconds);
+ gmtime_r(&dab_time_seconds, &timeData);
fig0_10->RFU = 0;
- fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900,
- timeData->tm_mon + 1,
- timeData->tm_mday));
+ fig0_10->setMJD(gregorian2mjd(timeData.tm_year + 1900,
+ timeData.tm_mon + 1,
+ timeData.tm_mday));
fig0_10->LSI = 0;
fig0_10->ConfInd = 1;
fig0_10->UTC = 1;
- fig0_10->setHours(timeData->tm_hour);
- fig0_10->Minutes = timeData->tm_min;
- fig0_10->Seconds = timeData->tm_sec;
+ fig0_10->setHours(timeData.tm_hour);
+ fig0_10->Minutes = timeData.tm_min;
+ fig0_10->Seconds = timeData.tm_sec;
fig0_10->setMilliseconds(dab_time_millis);
buf += 6;
remaining -= 6;
diff --git a/src/fig/FIG0_9.cpp b/src/fig/FIG0_9.cpp
index cf73625..dcee17c 100644
--- a/src/fig/FIG0_9.cpp
+++ b/src/fig/FIG0_9.cpp
@@ -139,8 +139,9 @@ FillStatus FIG0_9::fill(uint8_t *buf, size_t max_size)
if (ensemble->lto_auto) {
time_t now = time(NULL);
- struct tm* ltime = localtime(&now);
- time_t now2 = timegm(ltime);
+ struct tm ltime;
+ localtime_r(&now, &ltime);
+ time_t now2 = timegm(&ltime);
ensemble->lto = (now2 - now) / 1800;
}
diff --git a/src/fig/FIG1.cpp b/src/fig/FIG1.cpp
index dd1e70b..d0b6a17 100644
--- a/src/fig/FIG1.cpp
+++ b/src/fig/FIG1.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2015
+ Copyright (C) 2019
Matthias P. Braendli, matthias.braendli@mpb.li
Implementation of FIG1
@@ -37,6 +37,12 @@ FillStatus FIG1_0::fill(uint8_t *buf, size_t max_size)
auto ensemble = m_rti->ensemble;
size_t remaining = max_size;
+ if (not ensemble->label.has_fig1_label()) {
+ fs.complete_fig_transmitted = true;
+ fs.num_bytes_written = 0;
+ return fs;
+ }
+
if (remaining < 22) {
fs.num_bytes_written = 0;
return fs;
@@ -83,14 +89,15 @@ FillStatus FIG1_1::fill(uint8_t *buf, size_t max_size)
// Rotate through the subchannels until there is no more
// space
- for (; service != ensemble->services.end();
- ++service) {
-
+ for (; service != ensemble->services.end(); ++service) {
if (remaining < 4 + 16 + 2) {
break;
}
- if ((*service)->getType(ensemble) == subchannel_type_t::DABPlusAudio || (*service)->getType(ensemble) == subchannel_type_t::DABAudio) {
+ const auto type = (*service)->getType(ensemble);
+
+ if ( (type == subchannel_type_t::DABPlusAudio or type == subchannel_type_t::DABAudio) and
+ (*service)->label.has_fig1_label()) {
auto fig1_1 = (FIGtype1_1 *)buf;
fig1_1->FIGtypeNumber = 1;
@@ -148,8 +155,10 @@ FillStatus FIG1_4::fill(uint8_t *buf, size_t max_size)
/* We check in the config parser if the primary component has
* a label, which is forbidden since V2.1.1 */
+ const auto type = (*service)->getType(ensemble);
+
if (not (*component)->label.long_label().empty() ) {
- if ((*service)->getType(ensemble) == subchannel_type_t::DABPlusAudio || (*service)->getType(ensemble) == subchannel_type_t::DABAudio) {
+ if (type == subchannel_type_t::DABAudio or type == subchannel_type_t::DABPlusAudio) {
if (remaining < 5 + 16 + 2) {
break;
@@ -173,7 +182,6 @@ FillStatus FIG1_4::fill(uint8_t *buf, size_t max_size)
remaining -= 5;
}
else { // Data
-
if (remaining < 7 + 16 + 2) {
break;
}
@@ -237,7 +245,10 @@ FillStatus FIG1_5::fill(uint8_t *buf, size_t max_size)
break;
}
- if ((*service)->getType(ensemble) == subchannel_type_t::DABPlusAudio || (*service)->getType(ensemble) == subchannel_type_t::DABAudio) {
+ const auto type = (*service)->getType(ensemble);
+ const bool is_audio = (type == subchannel_type_t::DABAudio or type == subchannel_type_t::DABPlusAudio);
+
+ if (not is_audio) {
auto fig1_5 = (FIGtype1_5 *)buf;
fig1_5->FIGtypeNumber = 1;
fig1_5->Length = 23;
diff --git a/src/fig/FIG1.h b/src/fig/FIG1.h
index 2cca8d5..0fedffe 100644
--- a/src/fig/FIG1.h
+++ b/src/fig/FIG1.h
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2016
+ Copyright (C) 2019
Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
diff --git a/src/fig/FIG2.cpp b/src/fig/FIG2.cpp
new file mode 100644
index 0000000..afa64eb
--- /dev/null
+++ b/src/fig/FIG2.cpp
@@ -0,0 +1,406 @@
+/*
+ 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) 2019
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ Implementation of FIG2
+ */
+/*
+ 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 <algorithm>
+#include <iomanip>
+#include "fig/FIG2.h"
+#include "lib/charset/charset.h"
+
+namespace FIC {
+
+using namespace std;
+
+void FIG2_Segments::clear()
+{
+ segments.clear();
+ current_segment_it = segments.end();
+}
+
+void FIG2_Segments::load(const string& label)
+{
+ /* ETSI EN 300 401 Clause 5.2.2.3.3:
+ * "The label may be carried in one or two FIGs"
+ */
+
+ if (label.size() > 32) {
+ throw runtime_error("FIG2 label too long: " + to_string(label.size()));
+ }
+
+ if (label != label_on_last_load) {
+ toggle = not toggle;
+ label_on_last_load = label;
+ }
+
+ segments.clear();
+ vector<uint8_t> label_bytes(label.begin(), label.end());
+
+ for (size_t i = 0; i < label_bytes.size(); i+=16) {
+ size_t len = distance(label_bytes.begin() + i, label_bytes.end());
+
+ len = min(len, (size_t)16);
+
+ segments.emplace_back(label_bytes.begin() + i, label_bytes.begin() + i + len);
+ }
+
+ current_segment_it = segments.begin();
+}
+
+size_t FIG2_Segments::segment_count() const {
+ if (segments.empty()) {
+ throw runtime_error("Empty FIG2 has not segments");
+ }
+ return segments.size() - 1;
+}
+
+std::vector<uint8_t> FIG2_Segments::advance_segment() {
+ if (current_segment_it == segments.end()) {
+ return {};
+ }
+ else {
+ return *(current_segment_it++);
+ }
+}
+
+size_t FIG2_Segments::current_segment_length() const {
+ if (current_segment_it == segments.end()) {
+ return 0;
+ }
+ else {
+ return current_segment_it->size();
+ }
+}
+
+size_t FIG2_Segments::current_segment_index() const {
+ vv::const_iterator cur = current_segment_it;
+ return distance(segments.begin(), cur);
+}
+
+bool FIG2_Segments::ready() const {
+ return not segments.empty();
+}
+
+bool FIG2_Segments::complete() const {
+ return not segments.empty() and current_segment_it == segments.end();
+}
+
+int FIG2_Segments::toggle_flag() const {
+ return toggle ? 1 : 0;
+}
+
+// Ensemble label
+FillStatus FIG2_0::fill(uint8_t *buf, size_t max_size)
+{
+ FillStatus fs;
+ auto ensemble = m_rti->ensemble;
+ ssize_t remaining = max_size;
+
+ if (not ensemble->label.has_fig2_label()) {
+ fs.complete_fig_transmitted = true;
+ fs.num_bytes_written = 0;
+ return fs;
+ }
+
+ if (not m_segments.ready()) {
+ m_segments.load(ensemble->label.fig2_label());
+
+ if (not m_segments.ready()) {
+ throw logic_error("Non-empty label but segments not ready()");
+ }
+ }
+
+ const ssize_t required_bytes = (m_segments.current_segment_index() == 0) ?
+ sizeof(FIGtype2) + 2 + sizeof(FIG2_Extended_Label) + m_segments.current_segment_length() :
+ sizeof(FIGtype2) + 2 + m_segments.current_segment_length();
+
+ if (remaining < required_bytes) {
+ fs.num_bytes_written = 0;
+ return fs;
+ }
+
+ auto fig2 = (FIGtype2*)buf;
+
+ fig2->Length = required_bytes - 1;
+ fig2->FIGtypeNumber = 2;
+
+ fig2->Extension = 0;
+ fig2->Rfu = 0;
+ fig2->SegmentIndex = m_segments.current_segment_index();
+ fig2->ToggleFlag = m_segments.toggle_flag();
+
+ buf += sizeof(FIGtype2);
+ remaining -= sizeof(FIGtype2);
+
+ // Identifier field
+ buf[0] = ensemble->id >> 8;
+ buf[1] = ensemble->id & 0xFF;
+ buf += 2;
+ remaining -= 2;
+
+ if (m_segments.current_segment_index() == 0) {
+ // EN 300 401 5.2.2.3.3 "The second segment shall be carried in a following
+ // FIG type 2 data field ...", i.e. do not insert the header anymore.
+ auto ext = (FIG2_Extended_Label*)buf;
+ ext->Rfa = 0;
+ ext->SegmentCount = m_segments.segment_count();
+ ext->EncodingFlag = 0; // UTF-8
+ ext->CharacterFlag = htons(0xFF00); // Short label always truncation
+
+ buf += sizeof(FIG2_Extended_Label);
+ remaining -= sizeof(FIG2_Extended_Label);
+ }
+
+ const auto character_field = m_segments.advance_segment();
+ copy(character_field.begin(), character_field.end(), buf);
+ buf += character_field.size();
+ remaining -= character_field.size();
+
+ if (m_segments.complete()) {
+ fs.complete_fig_transmitted = true;
+ m_segments.clear();
+ }
+ fs.num_bytes_written = max_size - remaining;
+ return fs;
+}
+
+// Programme service label and data service label
+FillStatus FIG2_1_and_5::fill(uint8_t *buf, size_t max_size)
+{
+ FillStatus fs;
+
+ ssize_t remaining = max_size;
+
+ if (not m_initialised) {
+ service = m_rti->ensemble->services.end();
+ m_initialised = true;
+ }
+
+ auto ensemble = m_rti->ensemble;
+
+ // Rotate through the subchannels until there is no more space
+ while (service != ensemble->services.end()) {
+ const bool is_programme = (*service)->getType(ensemble) == subchannel_type_t::DABAudio or
+ ((*service)->getType(ensemble) == subchannel_type_t::DABPlusAudio);
+
+ if (not (m_programme xor is_programme) and (*service)->label.has_fig2_label()) {
+
+ auto& segments = segment_per_service[(*service)->id];
+
+ if (not segments.ready()) {
+ segments.load((*service)->label.fig2_label());
+
+ if (not segments.ready()) {
+ throw logic_error("Non-empty label but segments not ready()");
+ }
+ }
+
+ const size_t id_length = (is_programme ? 2 : 4);
+
+ const ssize_t required_bytes = sizeof(FIGtype2) + id_length + segments.current_segment_length() +
+ ((segments.current_segment_index() == 0) ? sizeof(FIG2_Extended_Label) : 0);
+
+ if (remaining < required_bytes) {
+ break;
+ }
+
+ auto fig2 = (FIGtype2*)buf;
+
+ fig2->Length = required_bytes - 1;
+ fig2->FIGtypeNumber = 2;
+
+ fig2->Extension = figextension();
+ fig2->Rfu = 0;
+ fig2->SegmentIndex = segments.current_segment_index();
+ fig2->ToggleFlag = segments.toggle_flag();
+
+ buf += sizeof(FIGtype2);
+ remaining -= sizeof(FIGtype2);
+
+ // Identifier field
+ if (is_programme) {
+ buf[0] = (*service)->id >> 8;
+ buf[1] = (*service)->id & 0xFF;
+ }
+ else {
+ buf[0] = ((*service)->id >> 24) & 0xFF;
+ buf[1] = ((*service)->id >> 16) & 0xFF;
+ buf[2] = ((*service)->id >> 8) & 0xFF;
+ buf[3] = (*service)->id & 0xFF;
+ }
+ buf += id_length;
+ remaining -= id_length;
+
+ if (segments.current_segment_index() == 0) {
+ auto ext = (FIG2_Extended_Label*)buf;
+ ext->Rfa = 0;
+ ext->SegmentCount = segments.segment_count();
+ ext->EncodingFlag = 0; // UTF-8
+ ext->CharacterFlag = htons(0xFF00); // Short label always truncation
+
+ buf += sizeof(FIG2_Extended_Label);
+ remaining -= sizeof(FIG2_Extended_Label);
+ }
+
+ const auto character_field = segments.advance_segment();
+ copy(character_field.begin(), character_field.end(), buf);
+ buf += character_field.size();
+ remaining -= character_field.size();
+
+ if (segments.complete()) {
+ segments.clear();
+ ++service;
+ }
+ }
+ else {
+ ++service;
+ }
+ }
+
+ if (service == ensemble->services.end()) {
+ service = ensemble->services.begin();
+ fs.complete_fig_transmitted = true;
+ }
+
+ fs.num_bytes_written = max_size - remaining;
+ return fs;
+}
+
+// Component label
+FillStatus FIG2_4::fill(uint8_t *buf, size_t max_size)
+{
+ FillStatus fs;
+
+ ssize_t remaining = max_size;
+
+ if (not m_initialised) {
+ component = m_rti->ensemble->components.end();
+ m_initialised = true;
+ }
+
+ auto ensemble = m_rti->ensemble;
+
+ while (component != ensemble->components.end()) {
+ if ((*component)->label.has_fig2_label()) {
+ auto service = getService(*component, ensemble->services);
+
+ auto& segments = segment_per_component[{(*component)->serviceId, (*component)->SCIdS}];
+
+ if (not segments.ready()) {
+ segments.load((*component)->label.fig2_label());
+
+ if (not segments.ready()) {
+ throw logic_error("Non-empty label but segments not ready()");
+ }
+ }
+
+ const bool is_programme = (*service)->getType(ensemble) == subchannel_type_t::DABAudio or
+ ((*service)->getType(ensemble) == subchannel_type_t::DABPlusAudio);
+
+ const size_t id_length = is_programme ?
+ sizeof(FIGtype2_4_Programme_Identifier) :
+ sizeof(FIGtype2_4_Data_Identifier);
+
+ const ssize_t required_bytes = sizeof(FIGtype2) + id_length + segments.current_segment_length() +
+ ((segments.current_segment_index() == 0) ? sizeof(FIG2_Extended_Label) : 0);
+
+ if (remaining < required_bytes) {
+ break;
+ }
+
+ auto fig2 = (FIGtype2*)buf;
+
+ fig2->Length = required_bytes - 1;
+ fig2->FIGtypeNumber = 2;
+
+ fig2->Extension = 4;
+ fig2->Rfu = 0;
+ fig2->SegmentIndex = segments.current_segment_index();
+ fig2->ToggleFlag = segments.toggle_flag();
+
+ buf += sizeof(FIGtype2);
+ remaining -= sizeof(FIGtype2);
+
+ // Identifier field
+ if (is_programme) {
+ auto fig2_4 = (FIGtype2_4_Programme_Identifier*)buf;
+
+ fig2_4->SCIdS = (*component)->SCIdS;
+ fig2_4->rfa = 0;
+ fig2_4->PD = 0;
+ fig2_4->SId = htons((*service)->id);
+
+ buf += sizeof(FIGtype2_4_Programme_Identifier);
+ remaining -= sizeof(FIGtype2_4_Programme_Identifier);
+ }
+ else {
+ auto fig2_4 = (FIGtype2_4_Data_Identifier*)buf;
+
+ fig2_4->SCIdS = (*component)->SCIdS;
+ fig2_4->rfa = 0;
+ fig2_4->PD = 1;
+ fig2_4->SId = htonl((*service)->id);
+
+ buf += sizeof(FIGtype2_4_Data_Identifier);
+ remaining -= sizeof(FIGtype2_4_Data_Identifier);
+ }
+
+ if (segments.current_segment_index() == 0) {
+ auto ext = (FIG2_Extended_Label*)buf;
+ ext->Rfa = 0;
+ ext->SegmentCount = segments.segment_count();
+ ext->EncodingFlag = 0; // UTF-8
+ ext->CharacterFlag = htons(0xFF00); // Short label always truncation
+
+ buf += sizeof(FIG2_Extended_Label);
+ remaining -= sizeof(FIG2_Extended_Label);
+ }
+
+ const auto character_field = segments.advance_segment();
+ copy(character_field.begin(), character_field.end(), buf);
+ buf += character_field.size();
+ remaining -= character_field.size();
+
+ if (segments.complete()) {
+ segments.clear();
+ ++component;
+ }
+ }
+ else {
+ ++component;
+ }
+ }
+
+ if (component == ensemble->components.end()) {
+ component = ensemble->components.begin();
+ fs.complete_fig_transmitted = true;
+ }
+
+ fs.num_bytes_written = max_size - remaining;
+ return fs;
+}
+
+} // namespace FIC
+
diff --git a/src/fig/FIG2.h b/src/fig/FIG2.h
new file mode 100644
index 0000000..6fad658
--- /dev/null
+++ b/src/fig/FIG2.h
@@ -0,0 +1,163 @@
+/*
+ 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) 2019
+ 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/>.
+*/
+
+#ifndef __FIG2_H_
+#define __FIG2_H_
+
+#include <cstdint>
+#include <map>
+
+#include "fig/FIG.h"
+
+namespace FIC {
+
+class FIG2_Segments {
+ public:
+ void clear();
+ void load(const std::string& label);
+
+ size_t segment_count() const;
+ std::vector<uint8_t> advance_segment();
+ size_t current_segment_length() const;
+ size_t current_segment_index() const;
+
+ bool ready() const;
+ bool complete() const;
+ int toggle_flag() const;
+
+ private:
+ using vv = std::vector<std::vector<uint8_t> >;
+ vv segments;
+ vv::iterator current_segment_it;
+
+ std::string label_on_last_load;
+ bool toggle = true;
+};
+
+// FIG type 2/0, Multiplex Configuration Info (MCI),
+// Ensemble information
+class FIG2_0 : public IFIG
+{
+ public:
+ FIG2_0(FIGRuntimeInformation* rti) :
+ m_rti(rti) {}
+ virtual FillStatus fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate() const { return FIG_rate::B; }
+
+ virtual int figtype() const { return 2; }
+ virtual int figextension() const { return 0; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ FIG2_Segments m_segments;
+};
+
+// FIG type 2/1, programme service label and FIG type 2/5, data service label
+// share this code.
+class FIG2_1_and_5 : public IFIG
+{
+ public:
+ FIG2_1_and_5(FIGRuntimeInformation* rti, bool programme) :
+ m_rti(rti),
+ m_initialised(false),
+ m_programme(programme) {}
+
+ virtual FillStatus fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate() const { return FIG_rate::B; }
+
+ virtual int figtype() const { return 2;}
+ virtual int figextension() const { return m_programme ? 1 : 5; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ bool m_programme;
+ vec_sp_service::iterator service;
+ std::map<uint32_t, FIG2_Segments> segment_per_service;
+};
+
+// FIG type 2/4, service component label
+class FIG2_4 : public IFIG
+{
+ public:
+ FIG2_4(FIGRuntimeInformation* rti) :
+ m_rti(rti), m_initialised(false) {}
+ virtual FillStatus fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate() const { return FIG_rate::B; }
+
+ virtual int figtype() const { return 2; }
+ virtual int figextension() const { return 4; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ vec_sp_component::iterator component;
+ std::map<std::pair<uint32_t, uint8_t>, FIG2_Segments> segment_per_component;
+};
+
+#ifdef _WIN32
+# pragma pack(push)
+#endif
+
+struct FIGtype2 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+
+ uint8_t Extension:3;
+ uint8_t Rfu:1;
+ uint8_t SegmentIndex:3;
+ uint8_t ToggleFlag:1;
+} PACKED;
+
+struct FIGtype2_4_Programme_Identifier {
+ uint8_t SCIdS:4;
+ uint8_t rfa:3;
+ uint8_t PD:1;
+ uint16_t SId;
+} PACKED;
+
+struct FIGtype2_4_Data_Identifier {
+ uint8_t SCIdS:4;
+ uint8_t rfa:3;
+ uint8_t PD:1;
+ uint32_t SId;
+} PACKED;
+
+struct FIG2_Extended_Label {
+ uint8_t Rfa:4;
+ uint8_t SegmentCount:3;
+ uint8_t EncodingFlag:1;
+
+ uint16_t CharacterFlag;
+} PACKED;
+
+#ifdef _WIN32
+# pragma pack(pop)
+#endif
+
+} // namespace FIC
+
+#endif // __FIG2_H_
+
diff --git a/src/fig/FIGCarousel.cpp b/src/fig/FIGCarousel.cpp
index 390dcf3..8ea183c 100644
--- a/src/fig/FIGCarousel.cpp
+++ b/src/fig/FIGCarousel.cpp
@@ -94,7 +94,11 @@ FIGCarousel::FIGCarousel(std::shared_ptr<dabEnsemble> ensemble) :
m_fig0_18(&m_rti),
m_fig0_19(&m_rti),
m_fig0_21(&m_rti),
- m_fig0_24(&m_rti)
+ m_fig0_24(&m_rti),
+ m_fig2_0(&m_rti),
+ m_fig2_1(&m_rti, true),
+ m_fig2_5(&m_rti, false),
+ m_fig2_4(&m_rti)
{
/* Complete MCI except FIG0/8 should be in FIB0.
* EN 300 401 V1.4.1 Clause 6.1
@@ -130,6 +134,11 @@ FIGCarousel::FIGCarousel(std::shared_ptr<dabEnsemble> ensemble) :
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);
+
+ load_and_allocate(m_fig2_0, FIBAllocation::FIB_ANY);
+ load_and_allocate(m_fig2_1, FIBAllocation::FIB_ANY);
+ load_and_allocate(m_fig2_5, FIBAllocation::FIB_ANY);
+ load_and_allocate(m_fig2_4, 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 ac0574d..a07a855 100644
--- a/src/fig/FIGCarousel.h
+++ b/src/fig/FIGCarousel.h
@@ -32,6 +32,7 @@
#include "fig/FIG.h"
#include "fig/FIG0.h"
#include "fig/FIG1.h"
+#include "fig/FIG2.h"
#include <list>
#include <map>
#include <memory>
@@ -110,6 +111,10 @@ class FIGCarousel {
FIG0_19 m_fig0_19;
FIG0_21 m_fig0_21;
FIG0_24 m_fig0_24;
+ FIG2_0 m_fig2_0;
+ FIG2_1_and_5 m_fig2_1;
+ FIG2_1_and_5 m_fig2_5;
+ FIG2_4 m_fig2_4;
};
} // namespace FIC
diff --git a/src/utils.cpp b/src/utils.cpp
index 212c97f..5f81083 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -211,12 +211,11 @@ void printServices(const vector<shared_ptr<DabService> >& services)
for (auto service : services) {
etiLog.level(info) << "Service " << service->get_rc_name();
- etiLog.level(info) << " label: " <<
- service->label.long_label();
- etiLog.level(info) << " short label: " <<
- service->label.short_label();
-
+ etiLog.level(info) << " label: " << service->label.long_label();
+ etiLog.level(info) << " short label: " << service->label.short_label();
etiLog.log(info, " (0x%x)", service->label.flag());
+ etiLog.level(info) << " FIG2 label: " << service->label.fig2_label();
+
etiLog.log(info, " id: 0x%lx (%lu)", service->id,
service->id);
@@ -244,7 +243,7 @@ void printComponents(const vec_sp_component& components)
unsigned int index = 0;
for (const auto component : components) {
- etiLog.level(info) << "Component " << component->get_rc_name();
+ etiLog.level(info) << "Component " << component->get_rc_name();
printComponent(component);
++index;
}
@@ -256,12 +255,11 @@ void printComponent(const shared_ptr<DabComponent>& component)
component->serviceId, component->serviceId);
etiLog.log(info, " subchannel id: 0x%x (%u)",
component->subchId, component->subchId);
- etiLog.level(info) << " label: " <<
- component->label.long_label();
- etiLog.level(info) << " short label: " <<
- component->label.short_label();
-
+ etiLog.level(info) << " label: " << component->label.long_label();
+ etiLog.level(info) << " short label: " << component->label.short_label();
etiLog.log(info, " (0x%x)", component->label.flag());
+ etiLog.level(info) << " FIG2 label: " << component->label.fig2_label();
+
etiLog.log(info, " service component type: 0x%x (%u)", component->type,
component->type);
@@ -516,12 +514,11 @@ void printEnsemble(const shared_ptr<dabEnsemble>& ensemble)
etiLog.log(info, "Ensemble");
etiLog.log(info, " id: 0x%lx (%lu)", ensemble->id, ensemble->id);
etiLog.log(info, " ecc: 0x%x (%u)", ensemble->ecc, ensemble->ecc);
- etiLog.level(info) << " label: " <<
- ensemble->label.long_label();
- etiLog.level(info) << " short label: " <<
- ensemble->label.short_label();
-
+ etiLog.level(info) << " label: " << ensemble->label.long_label();
+ etiLog.level(info) << " short label: " << ensemble->label.short_label();
etiLog.log(info, " (0x%x)", ensemble->label.flag());
+ etiLog.level(info) << " FIG2 label: " << ensemble->label.fig2_label();
+
switch (ensemble->transmission_mode) {
case TransmissionMode_e::TM_I:
etiLog.log(info, " mode: TM I");
@@ -539,8 +536,9 @@ void printEnsemble(const shared_ptr<dabEnsemble>& ensemble)
if (ensemble->lto_auto) {
time_t now = time(nullptr);
- struct tm* ltime = localtime(&now);
- time_t now2 = timegm(ltime);
+ struct tm ltime;
+ localtime_r(&now, &ltime);
+ time_t now2 = timegm(&ltime);
etiLog.log(info, " lto: %2.1f hours", 0.5 * (now2 - now) / 1800);
}
else {