summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ConfigParser.cpp91
-rw-r--r--src/Makefile.am1
-rw-r--r--src/MuxElements.cpp37
-rw-r--r--src/MuxElements.h44
-rw-r--r--src/fig/FIG0.h1
-rw-r--r--src/fig/FIG0_6.cpp204
-rw-r--r--src/fig/FIG0_6.h85
-rw-r--r--src/fig/FIG0structs.h19
-rw-r--r--src/utils.cpp37
9 files changed, 517 insertions, 2 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 6a359b7..2ca83d3 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2014, 2015
+ Copyright (C) 2016
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
@@ -142,6 +142,94 @@ uint16_t get_announcement_flag_from_ptree(
return flags;
}
+// Parse the linkage section
+void parse_linkage(boost::property_tree::ptree& pt,
+ std::shared_ptr<dabEnsemble> ensemble,
+ std::shared_ptr<BaseRemoteController> rc
+ )
+{
+ using boost::property_tree::ptree;
+ using boost::property_tree::ptree_error;
+
+ auto pt_linking = pt.get_child_optional("linking");
+ if (pt_linking)
+ {
+ for (const auto& it : *pt_linking) {
+ const string setuid = it.first;
+ const ptree pt_set = it.second;
+
+ uint16_t lsn = hexparse(pt_set.get("lsn", "0"));
+ if (lsn == 0) {
+ etiLog.level(error) << "LSN for linking set " <<
+ setuid << " invalid or missing";
+ throw runtime_error("Invalid service linking definition");
+ }
+
+ bool active = pt_set.get("active", false);
+ bool hard = pt_set.get("hard", true);
+ bool international = pt_set.get("international", false);
+
+ string service_uid = pt_set.get("keyservice", "");
+ if (service_uid.empty()) {
+ etiLog.level(error) << "Key Service for linking set " <<
+ setuid << " invalid or missing";
+ throw runtime_error("Invalid service linking definition");
+ }
+
+ auto linkageset = make_shared<LinkageSet>(setuid, lsn, hard, international);
+ linkageset->active = active;
+ linkageset->keyservice = service_uid; // TODO check if it exists
+
+ auto pt_list = pt_set.get_child_optional("list");
+ if (not pt_list) {
+ etiLog.level(error) << "list missing in linking set " <<
+ setuid;
+ throw runtime_error("Invalid service linking definition");
+ }
+
+ for (const auto& it : *pt_list) {
+ const string linkuid = it.first;
+ const ptree pt_link = it.second;
+
+ ServiceLink link;
+
+ string link_type = pt_link.get("type", "");
+ if (link_type == "dab") link.type = ServiceLinkType::DAB;
+ else if (link_type == "fm") link.type = ServiceLinkType::FM;
+ else if (link_type == "drm") link.type = ServiceLinkType::DRM;
+ else if (link_type == "amss") link.type = ServiceLinkType::AMSS;
+ else {
+ etiLog.level(error) << "Invalid type " << link_type <<
+ " for link " << linkuid;
+ throw runtime_error("Invalid service linking definition");
+ }
+
+ link.id = hexparse(pt_link.get("id", "0"));
+ if (link.id == 0) {
+ etiLog.level(error) << "id for link " <<
+ linkuid << " invalid or missing";
+ throw runtime_error("Invalid service linking definition");
+ }
+
+ if (international) {
+ link.ecc = hexparse(pt_link.get("ecc", "0"));
+ if (link.ecc == 0) {
+ etiLog.level(error) << "ecc for link " <<
+ linkuid << " invalid or missing";
+ throw runtime_error("Invalid service linking definition");
+ }
+ }
+ else {
+ link.ecc = 0;
+ }
+
+ linkageset->id_list.push_back(link);
+ }
+ ensemble->linkagesets.push_back(linkageset);
+ }
+ }
+}
+
void parse_ptree(boost::property_tree::ptree& pt,
std::shared_ptr<dabEnsemble> ensemble,
std::shared_ptr<BaseRemoteController> rc
@@ -556,6 +644,7 @@ void parse_ptree(boost::property_tree::ptree& pt,
}
+ parse_linkage(pt, ensemble, rc);
}
void setup_subchannel_from_ptree(DabSubchannel* subchan,
diff --git a/src/Makefile.am b/src/Makefile.am
index 3476bbd..5edd0a4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -103,6 +103,7 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \
fig/FIG0_2.cpp fig/FIG0_2.h \
fig/FIG0_3.cpp fig/FIG0_3.h \
fig/FIG0_5.cpp fig/FIG0_5.h \
+ fig/FIG0_6.cpp fig/FIG0_6.h \
fig/FIG0_8.cpp fig/FIG0_8.h \
fig/FIG0_9.cpp fig/FIG0_9.h \
fig/FIG0_10.cpp fig/FIG0_10.h \
diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp
index 773ec17..656e294 100644
--- a/src/MuxElements.cpp
+++ b/src/MuxElements.cpp
@@ -662,4 +662,41 @@ unsigned short DabSubchannel::getSizeDWord(void) const
return (bitrate * 3) >> 3;
}
+LinkageSet::LinkageSet(string name, uint16_t lsn, bool hard, bool international) :
+ RemoteControllable(name),
+ lsn(lsn),
+ active(false),
+ hard(hard),
+ international(international)
+{
+ RC_ADD_PARAMETER(active, "Activate this linkage set [0 or 1]");
+}
+
+void LinkageSet::set_parameter(const string& parameter, const string& value)
+{
+ if (parameter == "active") {
+ stringstream ss;
+ ss << value;
+ ss >> active;
+ }
+ else {
+ stringstream ss;
+ ss << "Parameter '" << parameter <<
+ "' is not exported by controllable " << get_rc_name();
+ throw ParameterError(ss.str());
+ }
+}
+const string LinkageSet::get_parameter(const string& parameter) const
+{
+ stringstream ss;
+ if (parameter == "active") {
+ ss << active;
+ }
+ else {
+ ss << "Parameter '" << parameter <<
+ "' is not exported by controllable " << get_rc_name();
+ throw ParameterError(ss.str());
+ }
+ return ss.str();
+}
diff --git a/src/MuxElements.h b/src/MuxElements.h
index 7056121..2f602ba 100644
--- a/src/MuxElements.h
+++ b/src/MuxElements.h
@@ -191,8 +191,9 @@ class DabLabel
class DabService;
class DabComponent;
-
class DabSubchannel;
+class LinkageSet;
+
class dabEnsemble : public RemoteControllable {
public:
dabEnsemble()
@@ -228,6 +229,7 @@ class dabEnsemble : public RemoteControllable {
std::vector<DabSubchannel*> subchannels;
std::vector<std::shared_ptr<AnnouncementCluster> > clusters;
+ std::list<std::shared_ptr<LinkageSet> > linkagesets;
};
@@ -421,6 +423,46 @@ class DabService : public RemoteControllable
DabService(const DabService& other);
};
+enum class ServiceLinkType {DAB, FM, DRM, AMSS};
+
+/* Represent one link inside a linkage set */
+struct ServiceLink {
+ ServiceLinkType type;
+ uint16_t id;
+ uint8_t ecc;
+};
+
+/* Represents a linkage set linkage sets according to
+ * TS 103 176 Clause 5.2.3 "Linkage sets". This information will
+ * be encoded in FIG 0/6.
+ */
+class LinkageSet : public RemoteControllable {
+ public:
+ LinkageSet(std::string name, uint16_t lsn, bool hard, bool international);
+
+ std::list<ServiceLink> id_list;
+
+ /* Linkage Set Number is a 12-bit number that identifies the linkage
+ * set in a country (requires coordination between multiplex operators
+ * in a country)
+ */
+ uint16_t lsn;
+
+ bool active; // Remote-controllable
+ bool hard;
+ bool international;
+
+ std::string keyservice; // TODO replace by pointer to service
+
+ private:
+ /* Remote control */
+ virtual void set_parameter(const std::string& parameter,
+ const std::string& value);
+
+ /* Getting a parameter always returns a string. */
+ virtual const std::string get_parameter(const std::string& parameter) const;
+};
+
std::vector<DabSubchannel*>::iterator getSubchannel(
std::vector<DabSubchannel*>& subchannels, int id);
diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h
index 927efc1..446e1ac 100644
--- a/src/fig/FIG0.h
+++ b/src/fig/FIG0.h
@@ -31,6 +31,7 @@
#include "fig/FIG0_2.h"
#include "fig/FIG0_3.h"
#include "fig/FIG0_5.h"
+#include "fig/FIG0_6.h"
#include "fig/FIG0_8.h"
#include "fig/FIG0_9.h"
#include "fig/FIG0_10.h"
diff --git a/src/fig/FIG0_6.cpp b/src/fig/FIG0_6.cpp
new file mode 100644
index 0000000..f404a47
--- /dev/null
+++ b/src/fig/FIG0_6.cpp
@@ -0,0 +1,204 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ 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/FIG0_6.h"
+#include "utils.h"
+
+namespace FIC {
+
+FIG0_6::FIG0_6(FIGRuntimeInformation *rti) :
+ m_rti(rti),
+ m_initialised(false)
+{
+}
+
+FillStatus FIG0_6::fill(uint8_t *buf, size_t max_size)
+{
+ FillStatus fs;
+ ssize_t remaining = max_size;
+ auto ensemble = m_rti->ensemble;
+
+ if (not m_initialised) {
+ linkageSetFIG0_6 = m_rti->ensemble->linkagesets.end();
+ m_initialised = true;
+ }
+
+ FIGtype0* fig0 = NULL;
+
+ for (; linkageSetFIG0_6 != ensemble->linkagesets.end();
+ ++linkageSetFIG0_6) {
+
+ const bool PD = false;
+ const bool ILS = (*linkageSetFIG0_6)->international;
+
+ // need to add key service to num_ids
+ const size_t num_ids = 1 + (*linkageSetFIG0_6)->id_list.size();
+
+ const size_t headersize = sizeof(struct FIGtype0_6_header);
+ const int required_size = sizeof(struct FIGtype0_6) +
+ (num_ids > 0 ?
+ headersize + (PD == 0 ? (ILS == 0 ? 2*num_ids : 3*num_ids) : 4*num_ids) :
+ 0);
+
+ if (fig0 == NULL) {
+ if (remaining < 2 + required_size) {
+ break;
+ }
+ fig0 = (FIGtype0*)buf;
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = PD;
+ fig0->Extension = 5;
+
+ buf += 2;
+ remaining -= 2;
+ }
+ else if (remaining < required_size) {
+ break;
+ }
+
+ FIGtype0_6 *fig0_6 = (FIGtype0_6*)buf;
+
+ fig0_6->IdListFlag = (num_ids > 0);
+ fig0_6->LA = (*linkageSetFIG0_6)->active;
+ fig0_6->SH = (*linkageSetFIG0_6)->hard;
+ fig0_6->ILS = ILS;
+ fig0_6->LSN = (*linkageSetFIG0_6)->lsn;
+
+ fig0->Length += sizeof(struct FIGtype0_6);
+ buf += sizeof(struct FIGtype0_6);
+ remaining -= sizeof(struct FIGtype0_6);
+
+ if (num_ids > 0) {
+ FIGtype0_6_header *header = (FIGtype0_6_header*)buf;
+ header->rfu = 0;
+ if (num_ids > 0x0F) {
+ etiLog.log(error, "Too large number of links for linkage set 0x%04x",
+ (*linkageSetFIG0_6)->lsn);
+ throw MuxInitException();
+ }
+
+ header->IdLQ = 0; // TODO not only DAB
+ header->rfa = 0;
+ header->num_ids = num_ids;
+
+ fig0->Length += headersize;
+ buf += headersize;
+ remaining -= headersize;
+
+ // TODO insert key service first
+ const std::string keyserviceuid =(*linkageSetFIG0_6)->keyservice;
+ const auto& keyservice = std::find_if(
+ ensemble->services.begin(),
+ ensemble->services.end(),
+ [&](const std::shared_ptr<DabService> srv) {
+ return srv->uid == keyserviceuid;
+ });
+
+ if (keyservice == ensemble->services.end()) {
+ etiLog.log(error, "Invalid key service %s in linkage set 0x%04x",
+ keyserviceuid.c_str(), (*linkageSetFIG0_6)->lsn);
+ throw MuxInitException();
+ }
+ for (const auto& l : (*linkageSetFIG0_6)->id_list) {
+ if (l.type != ServiceLinkType::DAB) {
+ etiLog.log(error, "TODO only DAB links supported. (linkage set 0x%04x)",
+ (*linkageSetFIG0_6)->lsn);
+ throw MuxInitException();
+ }
+ }
+
+ if (not PD and not ILS) {
+ buf[0] = (*keyservice)->id >> 8;
+ buf[1] = (*keyservice)->id & 0xFF;
+ fig0->Length += 2;
+ buf += 2;
+ remaining -= 2;
+
+ for (const auto& l : (*linkageSetFIG0_6)->id_list) {
+ buf[0] = l.id >> 8;
+ buf[1] = l.id & 0xFF;
+ fig0->Length += 2;
+ buf += 2;
+ remaining -= 2;
+ }
+ }
+ if (not PD and ILS) {
+ buf[0] = ensemble->ecc;
+ buf[1] = (*keyservice)->id >> 8;
+ buf[2] = (*keyservice)->id & 0xFF;
+ fig0->Length += 3;
+ buf += 3;
+ remaining -= 3;
+
+ for (const auto& l : (*linkageSetFIG0_6)->id_list) {
+ buf[0] = l.ecc;
+ buf[1] = l.id >> 8;
+ buf[2] = l.id & 0xFF;
+ fig0->Length += 3;
+ buf += 3;
+ remaining -= 3;
+ }
+ }
+ else { // PD == true
+ // TODO if IdLQ is 11, MSB shall be zero
+ buf[0] = (*keyservice)->id >> 24;
+ buf[1] = (*keyservice)->id >> 16;
+ buf[2] = (*keyservice)->id >> 8;
+ buf[3] = (*keyservice)->id & 0xFF;
+ fig0->Length += 4;
+ buf += 4;
+ remaining -= 4;
+
+ for (const auto& l : (*linkageSetFIG0_6)->id_list) {
+ buf[0] = l.id >> 24;
+ buf[1] = l.id >> 16;
+ buf[2] = l.id >> 8;
+ buf[3] = l.id & 0xFF;
+ fig0->Length += 4;
+ buf += 4;
+ remaining -= 4;
+ }
+ }
+ }
+
+ fig0->Length += required_size;
+ buf += required_size;
+ remaining -= required_size;
+ }
+
+ if (linkageSetFIG0_6 == ensemble->linkagesets.end()) {
+ linkageSetFIG0_6 = ensemble->linkagesets.begin();
+ fs.complete_fig_transmitted = true;
+ }
+
+ fs.num_bytes_written = max_size - remaining;
+ return fs;
+}
+
+}
+
diff --git a/src/fig/FIG0_6.h b/src/fig/FIG0_6.h
new file mode 100644
index 0000000..db55c06
--- /dev/null
+++ b/src/fig/FIG0_6.h
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ 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 <list>
+#include <memory>
+
+#include "fig/FIG0structs.h"
+#include "fig/TransitionHandler.h"
+
+namespace FIC {
+
+// FIG type 0/6
+// Service Linking
+//
+// This feature shall use the SIV signalling (see clause 5.2.2.1). The database
+// shall be divided by use of a database key. Changes to the database shall be
+// signalled using the CEI. The first service in the list of services in each
+// part of the database, as divided by the database key, shall be a service
+// carried in the ensemble. This service is called the key service.
+//
+// The database key comprises the OE and P/D flags and the S/H, ILS, and LSN
+// fields.
+class FIG0_6 : public IFIG
+{
+ public:
+ FIG0_6(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 6; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ std::list<std::shared_ptr<LinkageSet> >::iterator linkageSetFIG0_6;
+};
+
+// FIG0/6 needs a change indicator, which is a short-form FIG (i.e. without the list)
+// and with C/N 1. Since this has another rate, it's implemented in another class.
+//
+// This is signalled once per second for a period of five seconds
+// (TS 103 176 5.2.4.3).
+class FIG0_6_CEI : public IFIG
+{
+ public:
+ FIG0_6_CEI(FIGRuntimeInformation* rti);
+ virtual FillStatus fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::B; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 6; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ std::list<std::shared_ptr<LinkageSet> >::iterator linkageSetFIG0_6;
+};
+
+}
diff --git a/src/fig/FIG0structs.h b/src/fig/FIG0structs.h
index a1e79d3..4b9b7e0 100644
--- a/src/fig/FIG0structs.h
+++ b/src/fig/FIG0structs.h
@@ -152,6 +152,25 @@ struct FIGtype0_5_short {
uint8_t language;
} PACKED;
+struct FIGtype0_6 {
+ uint16_t IdListFlag:1;
+ uint16_t LA:1; // Linkage actuator
+ uint16_t SH:1; // 0=Soft link / 1=Hard link
+ uint16_t ILS:1; // 0=national / 1=international
+ uint16_t LSN:12; // Linkage Set Number
+} PACKED;
+
+#define FIG0_6_IDLQ_DAB 0x0
+#define FIG0_6_IDLQ_RDS 0x1
+#define FIG0_6_IDLQ_DRM_AMSS 0x3
+
+struct FIGtype0_6_header {
+ uint8_t rfu:1; // must be 0
+ uint8_t IdLQ:2; // Identifier List Qualifier, see above defines
+ uint8_t rfa:1; // must be 0
+ uint8_t num_ids:4; // number of Ids to follow in the list
+} PACKED;
+
struct FIGtype0_8_short {
uint8_t SCIdS:4;
uint8_t rfa_1:3;
diff --git a/src/utils.cpp b/src/utils.cpp
index 02fbc3f..baf15d5 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -552,6 +552,41 @@ void printSubchannels(vector<DabSubchannel*>& subchannels)
}
}
+static void printLinking(const shared_ptr<dabEnsemble> ensemble)
+{
+ etiLog.log(info, " Linkage Sets");
+ for (const auto& linkageset : ensemble->linkagesets) {
+ etiLog.level(info) << " set " << linkageset->get_rc_name();
+ etiLog.log(info, " LSN 0x%04x", linkageset->lsn);
+ etiLog.level(info) << " active " << (linkageset->active ? "true" : "false");
+ etiLog.level(info) << " " << (linkageset->hard ? "hard" : "soft");
+ etiLog.level(info) << " international " << (linkageset->international ? "true" : "false");
+ etiLog.level(info) << " key service " << linkageset->keyservice;
+
+ etiLog.level(info) << " ID list";
+ for (const auto& link : linkageset->id_list) {
+ switch (link.type) {
+ case ServiceLinkType::DAB:
+ etiLog.level(info) << " type DAB";
+ break;
+ case ServiceLinkType::FM:
+ etiLog.level(info) << " type FM";
+ break;
+ case ServiceLinkType::DRM:
+ etiLog.level(info) << " type DRM";
+ break;
+ case ServiceLinkType::AMSS:
+ etiLog.level(info) << " type AMSS";
+ break;
+ }
+ etiLog.log(info, " id 0x%04x", link.id);
+ if (linkageset->international) {
+ etiLog.log(info, " ecc 0x%04x", link.ecc);
+ }
+ }
+ }
+}
+
void printEnsemble(const shared_ptr<dabEnsemble> ensemble)
{
etiLog.log(info, "Ensemble");
@@ -584,5 +619,7 @@ void printEnsemble(const shared_ptr<dabEnsemble> ensemble)
etiLog.level(info) << cluster->tostring();
}
}
+
+ printLinking(ensemble);
}