aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2020-07-13 13:54:24 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2020-07-13 13:54:24 +0200
commitf8ee21192238bb5858db9c473dde711e4584dc6a (patch)
tree930f8619c460b7484fe9d5420aaaf2f5d834e3ad /src
parent56b5c6ae12136c1c24a8e8f6d001d72bf01b0967 (diff)
downloaddabmux-f8ee21192238bb5858db9c473dde711e4584dc6a.tar.gz
dabmux-f8ee21192238bb5858db9c473dde711e4584dc6a.tar.bz2
dabmux-f8ee21192238bb5858db9c473dde711e4584dc6a.zip
Add possibility to set several user application types in FIG0/13
Diffstat (limited to 'src')
-rw-r--r--src/ConfigParser.cpp72
-rw-r--r--src/DabMultiplexer.cpp12
-rw-r--r--src/MuxElements.cpp2
-rw-r--r--src/MuxElements.h16
-rw-r--r--src/fig/FIG0_13.cpp119
-rw-r--r--src/fig/FIG0_13.h2
-rw-r--r--src/fig/FIG0structs.h4
-rw-r--r--src/utils.cpp112
-rw-r--r--src/utils.h11
9 files changed, 207 insertions, 143 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 5d73b55..ee7d69f 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -749,9 +749,6 @@ void parse_ptree(
throw runtime_error(ss.str());
}
- int figType = hexparse(pt_comp.get("figtype", "-1"));
- int packet_address = hexparse(pt_comp.get("address", "-1"));
- int packet_datagroup = pt_comp.get("datagroup", false);
uint8_t component_type = hexparse(pt_comp.get("type", "0"));
auto component = make_shared<DabComponent>(componentuid);
@@ -807,22 +804,67 @@ void parse_ptree(
" components are allowed to have labels.";
}
- if (figType != -1) {
- if (figType >= (1<<12)) {
- stringstream ss;
- ss << "Component with uid " << componentuid <<
- ": figtype '" << figType << "' is too large !";
- throw runtime_error(ss.str());
- }
+ auto pt_ua = pt_comp.get_child_optional("user-applications");
+ if (pt_ua) {
+ for (const auto& ua_entry : *pt_ua) {
+ const string ua_key = ua_entry.first;
+ const string ua_value = ua_entry.second.data();
+
+ if (ua_key != "userapp") {
+ etiLog.level(error) << "user-applications should only contain 'userapp' keys";
+ throw runtime_error("component user-applications definition error");
+ }
+
+ userApplication ua;
+
+ // Values from TS 101 756 Table 16
- if (component->isPacketComponent(ensemble->subchannels)) {
- component->packet.appType = figType;
+ if (ua_value == "slideshow") {
+ ua.uaType = FIG0_13_APPTYPE_SLIDESHOW;
+ // This was previously hardcoded in FIG0/13 and means "MOT, start of X-PAD data group"
+ ua.xpadAppType = 12;
+ }
+ else if (ua_value == "spi") {
+ ua.uaType = FIG0_13_APPTYPE_SPI;
+ ua.xpadAppType = 16;
+ }
+
+ component->audio.uaTypes.push_back(ua);
}
- else {
- component->audio.uaType = figType;
+ }
+ else {
+ // Setting only figtype is the old format which allows the definition of a single
+ // user application type only.
+ int figType = hexparse(pt_comp.get("figtype", "-1"));
+ if (figType != -1) {
+
+ etiLog.level(warn) << "The figtype setting is deprecated in favour of user-applications. Please see example configurations.";
+
+ if (figType >= (1<<12)) {
+ stringstream ss;
+ ss << "Component with uid " << componentuid <<
+ ": figtype '" << figType << "' is too large !";
+ throw runtime_error(ss.str());
+ }
+
+ userApplication ua;
+ ua.uaType = figType;
+
+ // This was previously hardcoded in FIG0/13 and means "MOT, start of X-PAD data group"
+ ua.xpadAppType = 12;
+
+ if (component->isPacketComponent(ensemble->subchannels)) {
+ component->packet.uaTypes.push_back(ua);
+ }
+ else {
+ component->audio.uaTypes.push_back(ua);
+ }
}
+ }
+ if (component->isPacketComponent(ensemble->subchannels)) {
+ int packet_address = hexparse(pt_comp.get("address", "-1"));
if (packet_address != -1) {
if (! component->isPacketComponent(ensemble->subchannels)) {
stringstream ss;
@@ -833,6 +875,8 @@ void parse_ptree(
component->packet.address = packet_address;
}
+
+ int packet_datagroup = pt_comp.get("datagroup", false);
if (packet_datagroup) {
if (! component->isPacketComponent(ensemble->subchannels)) {
stringstream ss;
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
index 5eca126..ec29211 100644
--- a/src/DabMultiplexer.cpp
+++ b/src/DabMultiplexer.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2019
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
@@ -743,17 +743,7 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs
void DabMultiplexer::print_info()
{
// Print settings before starting
- etiLog.log(info, "--- Multiplex configuration ---");
printEnsemble(ensemble);
-
- etiLog.log(info, "--- Subchannels list ---");
- printSubchannels(ensemble->subchannels);
-
- etiLog.log(info, "--- Services list ---");
- printServices(ensemble->services);
-
- etiLog.log(info, "--- Components list ---");
- printComponents(ensemble->components);
}
diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp
index cb2d545..1d95cc0 100644
--- a/src/MuxElements.cpp
+++ b/src/MuxElements.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2019
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
diff --git a/src/MuxElements.h b/src/MuxElements.h
index 77d417b..100e4d7 100644
--- a/src/MuxElements.h
+++ b/src/MuxElements.h
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2019
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
@@ -422,10 +422,20 @@ public:
struct dabProtection protection;
};
+/* For FIG 0/13 (EN 300 401 Clause 6.3.6) */
+struct userApplication {
+ /* This 11-bit field identifies the user application that shall be used to decode the data in the channel identified
+ * by SId and SCIdS. The interpretation of this field shall be as defined in ETSI TS 101 756 [3], table 16. */
+ uint16_t uaType = 0xFFFF;
+ /* X-PAD Application Type: this 5-bit field shall specify the lowest numbered application type used to transport
+ * this user application (see clause 7.4.3).
+ * Also See EN 300 401 Table 11 "X-PAD Application types" */
+ uint8_t xpadAppType;
+};
struct dabAudioComponent {
- uint16_t uaType = 0xFFFF; // User Application Type
+ std::vector<userApplication> uaTypes;
};
@@ -437,7 +447,7 @@ struct dabDataComponent {
struct dabPacketComponent {
uint16_t id = 0;
uint16_t address = 0;
- uint16_t appType = 0xFFFF;
+ std::vector<userApplication> uaTypes;
bool datagroup = false;
};
diff --git a/src/fig/FIG0_13.cpp b/src/fig/FIG0_13.cpp
index 314c6e1..582e28e 100644
--- a/src/fig/FIG0_13.cpp
+++ b/src/fig/FIG0_13.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2016
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
@@ -92,9 +92,14 @@ FillStatus FIG0_13::fill(uint8_t *buf, size_t max_size)
const auto type = (*subchannel)->type;
if ( m_transmit_programme and
(type == subchannel_type_t::DABPlusAudio or type == subchannel_type_t::DABAudio) and
- (*componentFIG0_13)->audio.uaType != 0xffff) {
+ (*componentFIG0_13)->audio.uaTypes.size() != 0) {
- const int required_size = 3+4+11;
+ const size_t num_apps = (*componentFIG0_13)->audio.uaTypes.size();
+
+ const size_t app_length = 2;
+ static_assert(sizeof(FIG0_13_shortAppInfo) == 3);
+ static_assert(sizeof(FIG0_13_app) == 4);
+ const int required_size = sizeof(FIG0_13_shortAppInfo) + num_apps * (sizeof(FIG0_13_app) + app_length);
if (fig0 == NULL) {
if (remaining < 2 + required_size) {
@@ -117,34 +122,42 @@ FillStatus FIG0_13::fill(uint8_t *buf, size_t max_size)
FIG0_13_shortAppInfo* info = (FIG0_13_shortAppInfo*)buf;
info->SId = htonl((*componentFIG0_13)->serviceId) >> 16;
info->SCIdS = (*componentFIG0_13)->SCIdS;
- info->No = 1;
- buf += 3;
- remaining -= 3;
- fig0->Length += 3;
-
- FIG0_13_app* app = (FIG0_13_app*)buf;
- app->setType((*componentFIG0_13)->audio.uaType);
- app->length = 2;
- app->xpad = htons(0x0c3c);
- /* xpad meaning
- CA = 0
- CAOrg = 0
- Rfu = 0
- AppTy(5) = 12 (MOT, start of X-PAD data group)
- DG = 0 (MSC data groups used)
- Rfu = 0
- DSCTy(6) = 60 (MOT)
- */
-
- buf += 2 + app->length;
- remaining -= 2 + app->length;
- fig0->Length += 2 + app->length;
+ info->No = num_apps;
+ buf += sizeof(FIG0_13_shortAppInfo);
+ remaining -= sizeof(FIG0_13_shortAppInfo);
+ fig0->Length += sizeof(FIG0_13_shortAppInfo);
+
+ for (const auto& ua : (*componentFIG0_13)->audio.uaTypes) {
+ FIG0_13_app* app = (FIG0_13_app*)buf;
+ app->setType(ua.uaType);
+ app->length = app_length;
+
+ const uint8_t dscty = 60; // TS 101 756 Table 2b (MOT)
+ app->xpad = htons((ua.xpadAppType << 8) | dscty);
+ /* xpad meaning
+ CA = 0
+ CAOrg = 0 (CAOrg field absent)
+ Rfu = 0
+ AppTy(5) = depending on config
+ DG = 0 (MSC data groups used)
+ Rfu = 0
+ DSCTy(6) = 60 (MOT)
+ */
+
+ buf += sizeof(FIG0_13_app);
+ remaining -= sizeof(FIG0_13_app);
+ fig0->Length += sizeof(FIG0_13_app);
+ }
}
- else if (!m_transmit_programme &&
- (*subchannel)->type == subchannel_type_t::Packet &&
- (*componentFIG0_13)->packet.appType != 0xffff) {
+ else if (not m_transmit_programme and
+ (*subchannel)->type == subchannel_type_t::Packet and
+ (*componentFIG0_13)->packet.uaTypes.size() != 0) {
- const int required_size = 5+2+2;
+ const size_t num_apps = (*componentFIG0_13)->audio.uaTypes.size();
+
+ const size_t app_length = 2;
+ const int required_size = sizeof(FIG0_13_longAppInfo) + num_apps * (sizeof(FIG0_13_app) + app_length);
+ /* is conservative because app_length can be 0 */
if (fig0 == NULL) {
if (remaining < 2 + required_size) {
@@ -167,26 +180,36 @@ FillStatus FIG0_13::fill(uint8_t *buf, size_t max_size)
FIG0_13_longAppInfo* info = (FIG0_13_longAppInfo*)buf;
info->SId = htonl((*componentFIG0_13)->serviceId);
info->SCIdS = (*componentFIG0_13)->SCIdS;
- info->No = 1;
- buf += 5;
- remaining -= 5;
- fig0->Length += 5;
-
- FIG0_13_app* app = (FIG0_13_app*)buf;
- app->setType((*componentFIG0_13)->packet.appType);
- if (app->typeLow == FIG0_13_APPTYPE_EPG) {
- app->length = 2;
- app->xpad = htons(0x0100);
- /* xpad used to hold two bytes of EPG profile information
- 01 = basic profile
- 00 = list terminator */
- }
- else {
- app->length = 0;
+ info->No = num_apps;
+ buf += sizeof(FIG0_13_longAppInfo);
+ remaining -= sizeof(FIG0_13_longAppInfo);
+ fig0->Length += sizeof(FIG0_13_longAppInfo);
+
+ for (const auto& ua : (*componentFIG0_13)->audio.uaTypes) {
+ FIG0_13_app* app = (FIG0_13_app*)buf;
+ app->setType(ua.uaType);
+
+ size_t effective_length = sizeof(FIG0_13_app);
+
+ if (ua.uaType == FIG0_13_APPTYPE_SPI) {
+ // TODO This should probably be user configurable...
+ app->length = app_length;
+ app->xpad = htons(0x0100);
+ /* xpad is actually not the "X-PAD data" as in Figure 25, but is the actual user application data.
+ * We just recycle the same structure, even though it's a bit ugly.
+ * It holds two bytes of EPG profile information:
+ * 01 = basic profile
+ * 00 = list terminator */
+ }
+ else {
+ app->length = 0;
+ effective_length = 1; // FIG0_13_app without xpad
+ }
+
+ buf += effective_length;
+ remaining -= effective_length;
+ fig0->Length += effective_length;
}
- buf += 2 + app->length;
- remaining -= 2 + app->length;
- fig0->Length += 2 + app->length;
}
}
diff --git a/src/fig/FIG0_13.h b/src/fig/FIG0_13.h
index bac5b25..18159f0 100644
--- a/src/fig/FIG0_13.h
+++ b/src/fig/FIG0_13.h
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2016
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
diff --git a/src/fig/FIG0structs.h b/src/fig/FIG0structs.h
index d022dc4..a82e59e 100644
--- a/src/fig/FIG0structs.h
+++ b/src/fig/FIG0structs.h
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2017
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
@@ -36,7 +36,7 @@
#define FIG0_13_APPTYPE_TPEG 0x4
#define FIG0_13_APPTYPE_DGPS 0x5
#define FIG0_13_APPTYPE_TMC 0x6
-#define FIG0_13_APPTYPE_EPG 0x7
+#define FIG0_13_APPTYPE_SPI 0x7
#define FIG0_13_APPTYPE_DABJAVA 0x8
#define FIG0_13_APPTYPE_JOURNALINE 0x441
diff --git a/src/utils.cpp b/src/utils.cpp
index 7cd441a..c2916d2 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2019
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
@@ -38,6 +38,8 @@ using namespace std;
static time_t dab_time_seconds = 0;
static int dab_time_millis = 0;
+static void printServices(const vector<shared_ptr<DabService> >& services);
+
void update_dab_time()
{
if (dab_time_seconds == 0) {
@@ -100,7 +102,7 @@ void header_message()
fprintf(stderr,
"(Communications Research Centre Canada)\n\n");
fprintf(stderr,
- "Copyright (C) 2019 Matthias P. Braendli\n");
+ "Copyright (C) 2020 Matthias P. Braendli\n");
fprintf(stderr,
"LICENCE: GPLv3+\n\n");
fprintf(stderr,
@@ -209,6 +211,7 @@ void printServices(const vector<shared_ptr<DabService> >& services)
{
int index = 0;
+ etiLog.log(info, "--- Services list ---");
for (auto service : services) {
etiLog.level(info) << "Service " << service->get_rc_name();
@@ -239,18 +242,7 @@ void printServices(const vector<shared_ptr<DabService> >& services)
}
}
-void printComponents(const vec_sp_component& components)
-{
- unsigned int index = 0;
-
- for (const auto component : components) {
- etiLog.level(info) << "Component " << component->get_rc_name();
- printComponent(component);
- ++index;
- }
-}
-
-void printComponent(const shared_ptr<DabComponent>& component)
+void printComponent(const shared_ptr<DabComponent>& component, const std::shared_ptr<dabEnsemble>& ensemble)
{
etiLog.log(info, " service id: 0x%x (%u)",
component->serviceId, component->serviceId);
@@ -264,55 +256,55 @@ void printComponent(const shared_ptr<DabComponent>& component)
etiLog.log(info, " service component type: 0x%x (%u)", component->type,
component->type);
- if (component->packet.appType != 0xFFFF) {
+ if (component->isPacketComponent(ensemble->subchannels)) {
etiLog.log(info, " (packet) id: 0x%x (%u)",
component->packet.id, component->packet.id);
etiLog.log(info, " (packet) address: %u",
component->packet.address);
-
- etiLog.log(info, " (packet) app type: %u",
- component->packet.appType);
-
etiLog.log(info, " (packet) datagroup: %u",
component->packet.datagroup);
- }
- else if (component->audio.uaType != 0xFFFF) {
- stringstream ss;
- ss << " (audio) app type: ";
- switch (component->audio.uaType) {
- case FIG0_13_APPTYPE_SLIDESHOW:
- ss << "MOT Slideshow";
- break;
- case FIG0_13_APPTYPE_WEBSITE:
- ss << "MOT Broadcast Website";
- break;
- case FIG0_13_APPTYPE_TPEG:
- ss << "TPEG";
- break;
- case FIG0_13_APPTYPE_DGPS:
- ss << "DGPS";
- break;
- case FIG0_13_APPTYPE_TMC:
- ss << "TMC";
- break;
- case FIG0_13_APPTYPE_EPG:
- ss << "EPG";
- break;
- case FIG0_13_APPTYPE_DABJAVA:
- ss << "DAB Java";
- break;
- case FIG0_13_APPTYPE_JOURNALINE:
- ss << "Journaline";
- break;
- default:
- ss << "Unknown: " << component->audio.uaType;
- break;
- }
- etiLog.level(info) << ss.str();
+ for (const auto& userapp : component->packet.uaTypes) {
+ etiLog.log(info, " (packet) app type: %u",
+ userapp.uaType);
+ }
}
else {
- etiLog.level(info) << " No app type defined";
+ for (const auto& userapp : component->audio.uaTypes) {
+ stringstream ss;
+ ss << " (audio) app type: ";
+ switch (userapp.uaType) {
+ case FIG0_13_APPTYPE_SLIDESHOW:
+ ss << "MOT Slideshow";
+ break;
+ case FIG0_13_APPTYPE_WEBSITE:
+ ss << "MOT Broadcast Website";
+ break;
+ case FIG0_13_APPTYPE_TPEG:
+ ss << "TPEG";
+ break;
+ case FIG0_13_APPTYPE_DGPS:
+ ss << "DGPS";
+ break;
+ case FIG0_13_APPTYPE_TMC:
+ ss << "TMC";
+ break;
+ case FIG0_13_APPTYPE_SPI:
+ ss << "SPI/EPG";
+ break;
+ case FIG0_13_APPTYPE_DABJAVA:
+ ss << "DAB Java";
+ break;
+ case FIG0_13_APPTYPE_JOURNALINE:
+ ss << "Journaline";
+ break;
+ default:
+ ss << "Unknown: " << userapp.uaType;
+ break;
+ }
+
+ etiLog.level(info) << ss.str();
+ }
}
}
@@ -322,6 +314,8 @@ void printSubchannels(const vec_sp_subchannel& subchannels)
int total_num_cu = 0;
+ etiLog.log(info, "--- Subchannels list ---");
+
for (auto subchannel : subchannels) {
dabProtection* protection = &subchannel->protection;
etiLog.level(info) << "Subchannel " << subchannel->uid;
@@ -509,6 +503,7 @@ static void printFrequencyInformation(const shared_ptr<dabEnsemble>& ensemble)
void printEnsemble(const shared_ptr<dabEnsemble>& ensemble)
{
+ etiLog.log(info, "--- Multiplex configuration ---");
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);
@@ -556,6 +551,15 @@ void printEnsemble(const shared_ptr<dabEnsemble>& ensemble)
printLinking(ensemble);
printFrequencyInformation(ensemble);
+
+ printSubchannels(ensemble->subchannels);
+ printServices(ensemble->services);
+
+ etiLog.log(info, "--- Components list ---");
+ for (const auto component : ensemble->components) {
+ etiLog.level(info) << "Component " << component->get_rc_name();
+ printComponent(component, ensemble);
+ }
}
long hexparse(const std::string& input)
diff --git a/src/utils.h b/src/utils.h
index c9beb8e..cf64c69 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2018
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
This file contains a set of utility functions that are used to show
@@ -68,17 +68,10 @@ void printUsageConfigfile(char *name, FILE* out = stderr);
* resp. subchannels*/
void printOutputs(const std::vector<std::shared_ptr<DabOutput> >& outputs);
-void printServices(const std::vector<std::shared_ptr<DabService> >& services);
-
-void printComponents(const vec_sp_component& components);
-
-void printSubchannels(const vec_sp_subchannel& subchannels);
-
/* Print information about the whole ensemble */
void printEnsemble(const std::shared_ptr<dabEnsemble>& ensemble);
-/* Print detailed component information */
-void printComponent(const std::shared_ptr<DabComponent>& component);
+void printSubchannels(const vec_sp_subchannel& subchannels);
long hexparse(const std::string& input);