aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2020-05-05 11:24:21 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2020-05-05 11:24:53 +0200
commit367b854e7bbf758cb3119162dece4669d00dabb1 (patch)
treedd93700aec3b34b227b463c783385ddf09b4346a
parenta345b9f848750099a2631ee47babcc10035381c4 (diff)
downloadODR-EDI2EDI-367b854e7bbf758cb3119162dece4669d00dabb1.tar.gz
ODR-EDI2EDI-367b854e7bbf758cb3119162dece4669d00dabb1.tar.bz2
ODR-EDI2EDI-367b854e7bbf758cb3119162dece4669d00dabb1.zip
Get EDI input working
-rw-r--r--Makefile.am2
-rw-r--r--lib/edi/ETIDecoder.cpp244
-rw-r--r--lib/edi/ETIDecoder.hpp150
-rw-r--r--lib/edi/common.cpp54
-rw-r--r--lib/edi/common.hpp20
-rw-r--r--lib/edi/eti.hpp114
-rw-r--r--src/main.cpp416
7 files changed, 793 insertions, 207 deletions
diff --git a/Makefile.am b/Makefile.am
index 88b7219..d910c8d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -25,6 +25,8 @@ odr_edi2edi_SOURCES = src/main.cpp \
lib/edi/PFT.hpp lib/edi/PFT.cpp \
lib/edi/buffer_unpack.hpp \
lib/edi/common.hpp lib/edi/common.cpp \
+ lib/edi/ETIDecoder.hpp lib/edi/ETIDecoder.cpp \
+ lib/edi/eti.hpp \
lib/edioutput/AFPacket.cpp \
lib/edioutput/AFPacket.h \
lib/edioutput/EDIConfig.h \
diff --git a/lib/edi/ETIDecoder.cpp b/lib/edi/ETIDecoder.cpp
new file mode 100644
index 0000000..5ae1e17
--- /dev/null
+++ b/lib/edi/ETIDecoder.cpp
@@ -0,0 +1,244 @@
+/*
+ Copyright (C) 2020
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://opendigitalradio.org
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "ETIDecoder.hpp"
+#include "buffer_unpack.hpp"
+#include "crc.h"
+#include "Log.h"
+#include <cstdio>
+#include <cassert>
+#include <sstream>
+
+namespace EdiDecoder {
+
+using namespace std;
+
+ETIDecoder::ETIDecoder(ETIDataCollector& data_collector) :
+ m_data_collector(data_collector),
+ m_dispatcher(std::bind(&ETIDecoder::packet_completed, this))
+{
+ using std::placeholders::_1;
+ using std::placeholders::_2;
+ m_dispatcher.register_tag("*ptr",
+ std::bind(&ETIDecoder::decode_starptr, this, _1, _2));
+ m_dispatcher.register_tag("deti",
+ std::bind(&ETIDecoder::decode_deti, this, _1, _2));
+ m_dispatcher.register_tag("est",
+ std::bind(&ETIDecoder::decode_estn, this, _1, _2));
+ m_dispatcher.register_tag("*dmy",
+ std::bind(&ETIDecoder::decode_stardmy, this, _1, _2));
+ m_dispatcher.register_tag("",
+ std::bind(&ETIDecoder::decode_all, this, _1, _2));
+}
+
+void ETIDecoder::set_verbose(bool verbose)
+{
+ m_dispatcher.set_verbose(verbose);
+}
+
+
+void ETIDecoder::push_bytes(const vector<uint8_t> &buf)
+{
+ m_dispatcher.push_bytes(buf);
+}
+
+void ETIDecoder::push_packet(const vector<uint8_t> &buf)
+{
+ m_dispatcher.push_packet(buf);
+}
+
+void ETIDecoder::setMaxDelay(int num_af_packets)
+{
+ m_dispatcher.setMaxDelay(num_af_packets);
+}
+
+#define AFPACKET_HEADER_LEN 10 // includes SYNC
+
+bool ETIDecoder::decode_starptr(const std::vector<uint8_t>& value, const tag_name_t& n)
+{
+ if (value.size() != 0x40 / 8) {
+ etiLog.log(warn, "Incorrect length %02lx for *PTR", value.size());
+ return false;
+ }
+
+ char protocol_sz[5];
+ protocol_sz[4] = '\0';
+ copy(value.begin(), value.begin() + 4, protocol_sz);
+ string protocol(protocol_sz);
+
+ uint16_t major = read_16b(value.begin() + 4);
+ uint16_t minor = read_16b(value.begin() + 6);
+
+ m_data_collector.update_protocol(protocol, major, minor);
+
+ return true;
+}
+
+bool ETIDecoder::decode_deti(const std::vector<uint8_t>& value, const tag_name_t& n)
+{
+ /*
+ uint16_t detiHeader = fct | (fcth << 8) | (rfudf << 13) | (ficf << 14) | (atstf << 15);
+ packet.push_back(detiHeader >> 8);
+ packet.push_back(detiHeader & 0xFF);
+ */
+
+ uint16_t detiHeader = read_16b(value.begin());
+
+ eti_fc_data fc;
+
+ fc.atstf = (detiHeader >> 15) & 0x1;
+ fc.ficf = (detiHeader >> 14) & 0x1;
+ bool rfudf = (detiHeader >> 13) & 0x1;
+ uint8_t fcth = (detiHeader >> 8) & 0x1F;
+ uint8_t fct = detiHeader & 0xFF;
+
+ fc.dflc = fcth * 250 + fct; // modulo 5000 counter
+
+ uint32_t etiHeader = read_32b(value.begin() + 2);
+
+ uint8_t stat = (etiHeader >> 24) & 0xFF;
+
+ fc.mid = (etiHeader >> 22) & 0x03;
+ fc.fp = (etiHeader >> 19) & 0x07;
+ uint8_t rfa = (etiHeader >> 17) & 0x3;
+ if (rfa != 0) {
+ etiLog.log(warn, "EDI deti TAG: rfa non-zero");
+ }
+
+ bool rfu = (etiHeader >> 16) & 0x1;
+ uint16_t mnsc = rfu ? 0xFFFF : etiHeader & 0xFFFF;
+
+ const size_t fic_length_words = (fc.ficf ? (fc.mid == 3 ? 32 : 24) : 0);
+ const size_t fic_length = 4 * fic_length_words;
+
+ const size_t expected_length = 2 + 4 +
+ (fc.atstf ? 1 + 4 + 3 : 0) +
+ fic_length +
+ (rfudf ? 3 : 0);
+
+ if (value.size() != expected_length) {
+ throw std::logic_error("EDI deti: Assertion error:"
+ "value.size() != expected_length: " +
+ to_string(value.size()) + " " +
+ to_string(expected_length));
+ }
+
+ m_data_collector.update_err(stat);
+ m_data_collector.update_mnsc(mnsc);
+
+ size_t i = 2 + 4;
+
+ if (fc.atstf) {
+ uint8_t utco = value[i];
+ i++;
+
+ uint32_t seconds = read_32b(value.begin() + i);
+ i += 4;
+
+ m_data_collector.update_edi_time(utco, seconds);
+ m_received_tag_data.timestamp.utco = utco;
+ m_received_tag_data.timestamp.seconds = seconds;
+
+ fc.tsta = read_24b(value.begin() + i);
+ i += 3;
+ }
+ else {
+ // Null timestamp, ETSI ETS 300 799, C.2.2
+ fc.tsta = 0xFFFFFF;
+ m_received_tag_data.timestamp.utco = 0;
+ m_received_tag_data.timestamp.seconds = 0;
+ }
+
+ m_received_tag_data.timestamp.tsta = fc.tsta;
+
+
+ if (fc.ficf) {
+ vector<uint8_t> fic(fic_length);
+ copy( value.begin() + i,
+ value.begin() + i + fic_length,
+ fic.begin());
+ i += fic_length;
+
+ m_data_collector.update_fic(move(fic));
+ }
+
+ if (rfudf) {
+ uint32_t rfud = read_24b(value.begin() + i);
+
+ // high 16 bits: RFU in LIDATA EOH
+ // low 8 bits: RFU in TIST (not supported)
+ m_data_collector.update_rfu(rfud >> 8);
+ if ((rfud & 0xFF) != 0xFF) {
+ etiLog.level(warn) << "EDI: RFU in TIST not supported";
+ }
+
+ i += 3;
+ }
+
+ m_data_collector.update_fc_data(fc);
+
+ return true;
+}
+
+bool ETIDecoder::decode_estn(const std::vector<uint8_t>& value, const tag_name_t& name)
+{
+ uint32_t sstc = read_24b(value.begin());
+
+ eti_stc_data stc;
+
+ const uint8_t n = name[3];
+ stc.stream_index = n - 1; // n is 1-indexed
+ stc.scid = (sstc >> 18) & 0x3F;
+ stc.sad = (sstc >> 8) & 0x3FF;
+ stc.tpl = (sstc >> 2) & 0x3F;
+ uint8_t rfa = sstc & 0x3;
+ if (rfa != 0) {
+ etiLog.level(warn) << "EDI: rfa field in ESTn tag non-null";
+ }
+
+ copy( value.begin() + 3,
+ value.end(),
+ back_inserter(stc.mst));
+
+ m_data_collector.add_subchannel(move(stc));
+
+ return true;
+}
+
+bool ETIDecoder::decode_stardmy(const std::vector<uint8_t>&, const tag_name_t&)
+{
+ return true;
+}
+
+bool ETIDecoder::decode_all(const std::vector<uint8_t>& value, const EdiDecoder::tag_name_t& n)
+{
+ ReceivedTag tag{ .name = n, .tag_data = value};
+ m_received_tag_data.all_tags.emplace_back(move(tag));
+ return true;
+}
+
+void ETIDecoder::packet_completed()
+{
+ ReceivedTagData td;
+ swap(td, m_received_tag_data);
+ m_data_collector.assemble(move(td));
+}
+
+}
diff --git a/lib/edi/ETIDecoder.hpp b/lib/edi/ETIDecoder.hpp
new file mode 100644
index 0000000..c21f3df
--- /dev/null
+++ b/lib/edi/ETIDecoder.hpp
@@ -0,0 +1,150 @@
+/*
+ Copyright (C) 2020
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://opendigitalradio.org
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "eti.hpp"
+#include "common.hpp"
+#include <cstdint>
+#include <deque>
+#include <string>
+#include <vector>
+
+namespace EdiDecoder {
+
+// Information for Frame Characterisation available in
+// EDI.
+//
+// Number of streams is given separately, and frame length
+// is calculated in the ETIDataCollector
+struct eti_fc_data {
+ bool atstf;
+ uint32_t tsta;
+ bool ficf;
+ uint16_t dflc;
+ uint8_t mid;
+ uint8_t fp;
+
+ uint8_t fct(void) const { return dflc % 250; }
+};
+
+// Information for a subchannel available in EDI
+struct eti_stc_data {
+ uint8_t stream_index;
+ uint8_t scid;
+ uint16_t sad;
+ uint8_t tpl;
+ std::vector<uint8_t> mst;
+
+ // Return the length of the MST in multiples of 64 bits
+ uint16_t stl(void) const { return mst.size() / 8; }
+};
+
+struct ReceivedTag {
+ tag_name_t name;
+ std::vector<uint8_t> tag_data;
+};
+
+struct ReceivedTagData {
+ std::vector<ReceivedTag> all_tags;
+ frame_timestamp_t timestamp;
+};
+
+
+/* A class that receives multiplex data must implement the interface described
+ * in the ETIDataCollector. This can be e.g. a converter to ETI, or something that
+ * prepares data structures for a modulator.
+ */
+class ETIDataCollector {
+ public:
+ // Tell the ETIWriter what EDI protocol we receive in *ptr.
+ // This is not part of the ETI data, but is used as check
+ virtual void update_protocol(
+ const std::string& proto,
+ uint16_t major,
+ uint16_t minor) = 0;
+
+ // Update the data for the frame characterisation
+ virtual void update_fc_data(const eti_fc_data& fc_data) = 0;
+
+ virtual void update_fic(std::vector<uint8_t>&& fic) = 0;
+
+ virtual void update_err(uint8_t err) = 0;
+
+ // In addition to TSTA in ETI, EDI also transports more time
+ // stamp information.
+ virtual void update_edi_time(uint32_t utco, uint32_t seconds) = 0;
+
+ virtual void update_mnsc(uint16_t mnsc) = 0;
+
+ virtual void update_rfu(uint16_t rfu) = 0;
+
+ virtual void add_subchannel(eti_stc_data&& stc) = 0;
+
+ // Tell the consumer that the AFPacket is complete, and include
+ // the raw received TAGs
+ virtual void assemble(ReceivedTagData&& tag_data) = 0;
+};
+
+/* The ETIDecoder takes care of decoding the EDI TAGs related to the transport
+ * of ETI(NI) data inside AF and PF packets.
+ *
+ * PF packets are handed over to the PFT decoder, which will in turn return
+ * AF packets. AF packets are directly handled (TAG extraction) here.
+ */
+class ETIDecoder {
+ public:
+ ETIDecoder(ETIDataCollector& data_collector);
+
+ void set_verbose(bool verbose);
+
+ /* Push bytes into the decoder. The buf can contain more
+ * than a single packet. This is useful when reading from streams
+ * (files, TCP)
+ */
+ void push_bytes(const std::vector<uint8_t> &buf);
+
+ /* Push a complete packet into the decoder. Useful for UDP and other
+ * datagram-oriented protocols.
+ */
+ void push_packet(const std::vector<uint8_t> &buf);
+
+ /* Set the maximum delay in number of AF Packets before we
+ * abandon decoding a given pseq.
+ */
+ void setMaxDelay(int num_af_packets);
+
+ private:
+ bool decode_starptr(const std::vector<uint8_t>& value, const tag_name_t& n);
+ bool decode_deti(const std::vector<uint8_t>& value, const tag_name_t& n);
+ bool decode_estn(const std::vector<uint8_t>& value, const tag_name_t& n);
+ bool decode_stardmy(const std::vector<uint8_t>& value, const tag_name_t& n);
+
+ bool decode_all(const std::vector<uint8_t>& value, const EdiDecoder::tag_name_t& n);
+
+ void packet_completed();
+
+ ETIDataCollector& m_data_collector;
+ TagDispatcher m_dispatcher;
+
+ ReceivedTagData m_received_tag_data;
+};
+
+}
diff --git a/lib/edi/common.cpp b/lib/edi/common.cpp
index 470b3ba..a12fd72 100644
--- a/lib/edi/common.cpp
+++ b/lib/edi/common.cpp
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2019
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
http://opendigitalradio.org
@@ -26,6 +26,7 @@
#include <cassert>
#include <cmath>
#include <cstdio>
+#include <cctype>
namespace EdiDecoder {
@@ -111,11 +112,30 @@ std::chrono::system_clock::time_point frame_timestamp_t::to_system_clock() const
return ts;
}
+std::string tag_name_to_human_readable(const tag_name_t& name)
+{
+ std::string s;
+ for (const uint8_t c : name) {
+ if (isprint(c)) {
+ s += (char)c;
+ }
+ else {
+ char escaped[5];
+ snprintf(escaped, 5, "\\x%02x", c);
+ s += escaped;
+ }
+ }
+ return s;
+}
TagDispatcher::TagDispatcher(
- std::function<void()>&& af_packet_completed, bool verbose) :
+ std::function<void()>&& af_packet_completed) :
m_af_packet_completed(move(af_packet_completed))
{
+}
+
+void TagDispatcher::set_verbose(bool verbose)
+{
m_pft.setVerbose(verbose);
}
@@ -326,31 +346,29 @@ bool TagDispatcher::decode_tagpacket(const vector<uint8_t> &payload)
break;
}
+ const array<uint8_t, 4> tag_name({
+ (uint8_t)tag_sz[0], (uint8_t)tag_sz[1], (uint8_t)tag_sz[2], (uint8_t)tag_sz[3]
+ });
vector<uint8_t> tag_value(taglength);
copy( payload.begin() + i+8,
payload.begin() + i+8+taglength,
tag_value.begin());
- bool tagsuccess = false;
+ bool tagsuccess = true;
bool found = false;
for (auto tag_handler : m_handlers) {
- if (tag_handler.first.size() == 4 and tag_handler.first == tag) {
+ if ( (tag_handler.first.size() == 4 and tag == tag_handler.first) or
+ (tag_handler.first.size() == 3 and tag.substr(0, 3) == tag_handler.first) or
+ (tag_handler.first.size() == 2 and tag.substr(0, 2) == tag_handler.first) or
+ (tag_handler.first.size() == 1 and tag.substr(0, 1) == tag_handler.first)) {
found = true;
- tagsuccess = tag_handler.second(tag_value, 0);
+ tagsuccess &= tag_handler.second(tag_value, tag_name);
}
- else if (tag_handler.first.size() == 3 and
- tag.substr(0, 3) == tag_handler.first) {
- found = true;
- uint8_t n = tag_sz[3];
- tagsuccess = tag_handler.second(tag_value, n);
- }
- else if (tag_handler.first.size() == 2 and
- tag.substr(0, 2) == tag_handler.first) {
- found = true;
- uint16_t n = 0;
- n = (uint16_t)(tag_sz[2]) << 8;
- n |= (uint16_t)(tag_sz[3]);
- tagsuccess = tag_handler.second(tag_value, n);
+ else if (tag_handler.first.empty()) {
+ /* Don't set the found flag because we still want to see the warning for
+ * tags that don't have a specific decoder
+ */
+ tagsuccess &= tag_handler.second(tag_value, tag_name);
}
}
diff --git a/lib/edi/common.hpp b/lib/edi/common.hpp
index 2a9c683..23bbaa8 100644
--- a/lib/edi/common.hpp
+++ b/lib/edi/common.hpp
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2019
+ Copyright (C) 2020
Matthias P. Braendli, matthias.braendli@mpb.li
http://opendigitalradio.org
@@ -55,6 +55,10 @@ struct decode_state_t {
size_t num_bytes_consumed;
};
+using tag_name_t = std::array<uint8_t, 4>;
+
+std::string tag_name_to_human_readable(const tag_name_t& name);
+
/* The TagDispatcher takes care of decoding EDI, with or without PFT, and
* will call functions when TAGs are encountered.
*
@@ -63,7 +67,10 @@ struct decode_state_t {
*/
class TagDispatcher {
public:
- TagDispatcher(std::function<void()>&& af_packet_completed, bool verbose);
+ TagDispatcher(std::function<void()>&& af_packet_completed);
+
+ void set_verbose(bool verbose);
+
/* Push bytes into the decoder. The buf can contain more
* than a single packet. This is useful when reading from streams
@@ -81,7 +88,14 @@ class TagDispatcher {
*/
void setMaxDelay(int num_af_packets);
- using tag_handler = std::function<bool(std::vector<uint8_t>, uint16_t)>;
+ /* Handler function for a tag. The first argument contains the tag value,
+ * the second argument contains the tag name */
+ using tag_handler = std::function<bool(std::vector<uint8_t>&, const tag_name_t&)>;
+
+ /* Register a handler for a tag. If the tag string can be length 0, 1, 2, 3 or 4.
+ * If is shorter than 4, it will perform a longest match on the tag name.
+ * If it is empty, it will match all tags.
+ */
void register_tag(const std::string& tag, tag_handler&& h);
private:
diff --git a/lib/edi/eti.hpp b/lib/edi/eti.hpp
new file mode 100644
index 0000000..372f098
--- /dev/null
+++ b/lib/edi/eti.hpp
@@ -0,0 +1,114 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://opendigitalradio.org
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <cstdint>
+#define PACKED __attribute__ ((packed))
+
+#include <ctime>
+
+namespace EdiDecoder {
+
+struct eti_SYNC {
+ uint32_t ERR:8;
+ uint32_t FSYNC:24;
+} PACKED;
+
+struct eti_FC {
+ uint32_t FCT:8;
+ uint32_t NST:7;
+ uint32_t FICF:1;
+ uint32_t FL_high:3;
+ uint32_t MID:2;
+ uint32_t FP:3;
+ uint32_t FL_low:8;
+ uint16_t getFrameLength();
+ void setFrameLength(uint16_t length);
+} PACKED;
+
+struct eti_STC {
+ uint32_t startAddress_high:2;
+ uint32_t SCID:6;
+ uint32_t startAddress_low:8;
+ uint32_t STL_high:2;
+ uint32_t TPL:6;
+ uint32_t STL_low:8;
+ void setSTL(uint16_t length);
+ uint16_t getSTL();
+ void setStartAddress(uint16_t address);
+ uint16_t getStartAddress();
+} PACKED;
+
+struct eti_EOH {
+ uint16_t MNSC;
+ uint16_t CRC;
+} PACKED;
+
+struct eti_EOF {
+ uint16_t CRC;
+ uint16_t RFU;
+} PACKED;
+
+struct eti_TIST {
+ uint32_t TIST;
+} PACKED;
+
+struct eti_MNSC_TIME_0 {
+ uint32_t type:4;
+ uint32_t identifier:4;
+ uint32_t rfa:8;
+} PACKED;
+
+struct eti_MNSC_TIME_1 {
+ uint32_t second_unit:4;
+ uint32_t second_tens:3;
+ uint32_t accuracy:1;
+
+ uint32_t minute_unit:4;
+ uint32_t minute_tens:3;
+ uint32_t sync_to_frame:1;
+} PACKED;
+
+struct eti_MNSC_TIME_2 {
+ uint32_t hour_unit:4;
+ uint32_t hour_tens:4;
+
+ uint32_t day_unit:4;
+ uint32_t day_tens:4;
+} PACKED;
+
+struct eti_MNSC_TIME_3 {
+ uint32_t month_unit:4;
+ uint32_t month_tens:4;
+
+ uint32_t year_unit:4;
+ uint32_t year_tens:4;
+} PACKED;
+
+struct eti_extension_TIME {
+ uint32_t TIME_SECONDS;
+} PACKED;
+
+}
diff --git a/src/main.cpp b/src/main.cpp
index 3005303..b3a0519 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -32,13 +32,12 @@
#include "edioutput/TagItems.h"
#include "edioutput/TagPacket.h"
#include "edioutput/Transport.h"
+#include "edi/ETIDecoder.hpp"
using namespace std;
constexpr long DEFAULT_BACKOFF = 5000;
-static edi::configuration_t edi_conf;
-
static void usage()
{
cerr << "Usage:" << endl;
@@ -73,218 +72,262 @@ static void usage()
/* There is some state inside the parsing of destination arguments,
* because several destinations can be given. */
-static std::shared_ptr<edi::udp_destination_t> edi_destination;
-static bool source_port_set = false;
-static bool source_addr_set = false;
-static bool ttl_set = false;
-static bool dest_addr_set = false;
-
-static void add_edi_destination(void)
-{
- if (not dest_addr_set) {
- throw std::runtime_error("Destination address not specified for destination number " +
- std::to_string(edi_conf.destinations.size() + 1));
- }
-
- edi_conf.destinations.push_back(move(edi_destination));
- edi_destination = std::make_shared<edi::udp_destination_t>();
-
- source_port_set = false;
- source_addr_set = false;
- ttl_set = false;
- dest_addr_set = false;
-}
-static void parse_destination_args(char option)
-{
- if (not edi_destination) {
- edi_destination = std::make_shared<edi::udp_destination_t>();
- }
+class Main : public EdiDecoder::ETIDataCollector {
+ public:
+ Main() : edi_decoder(*this)
+ {
+ }
- switch (option) {
- case 's':
- if (source_port_set) {
- add_edi_destination();
- }
- edi_destination->source_port = std::stoi(optarg);
- source_port_set = true;
- break;
- case 'S':
- if (source_addr_set) {
- add_edi_destination();
- }
- edi_destination->source_addr = optarg;
- source_addr_set = true;
- break;
- case 't':
- if (ttl_set) {
- add_edi_destination();
+ // Tell the ETIWriter what EDI protocol we receive in *ptr.
+ // This is not part of the ETI data, but is used as check
+ virtual void update_protocol(
+ const std::string& proto,
+ uint16_t major,
+ uint16_t minor) override { }
+
+ // Update the data for the frame characterisation
+ virtual void update_fc_data(const EdiDecoder::eti_fc_data& fc_data) override { }
+ virtual void update_fic(std::vector<uint8_t>&& fic) override { }
+ virtual void update_err(uint8_t err) override { }
+
+ // In addition to TSTA in ETI, EDI also transports more time
+ // stamp information.
+ virtual void update_edi_time(uint32_t utco, uint32_t seconds) override { }
+ virtual void update_mnsc(uint16_t mnsc) override { }
+ virtual void update_rfu(uint16_t rfu) override { }
+ virtual void add_subchannel(EdiDecoder::eti_stc_data&& stc) override { }
+
+ // Tell the ETIWriter that the AFPacket is complete
+ virtual void assemble(EdiDecoder::ReceivedTagData&& tag_data) override
+ {
+ etiLog.level(info) << "Received " << tag_data.all_tags.size() << " tags at " << tag_data.timestamp.to_unix_epoch();
+
+ for (const auto& tag : tag_data.all_tags) {
+ etiLog.level(debug) << " TAG " << EdiDecoder::tag_name_to_human_readable(tag.name);
}
- edi_destination->ttl = std::stoi(optarg);
- ttl_set = true;
- break;
- case 'd':
- if (dest_addr_set) {
- add_edi_destination();
- }
- edi_destination->dest_addr = optarg;
- dest_addr_set = true;
- break;
- default:
- throw std::logic_error("parse_destination_args invalid");
- }
-}
+ }
-static int start(int argc, char **argv)
-{
- edi_conf.enable_pft = true;
+ int start(int argc, char **argv)
+ {
+ edi_conf.enable_pft = true;
- if (argc == 0) {
- usage();
- return 1;
- }
+ if (argc == 0) {
+ usage();
+ return 1;
+ }
- int delay_ms = 500;
- bool drop_late_packets = false;
- uint32_t backoff_after_reset_ms = DEFAULT_BACKOFF;
- std::string startupcheck;
- std::string source;
-
- int ch = 0;
- while (ch != -1) {
- ch = getopt(argc, argv, "c:C:d:p:s:S:t:Pf:i:Dva:b:w:xh");
- switch (ch) {
- case -1:
- break;
- case 'c':
- source = optarg;
- break;
- case 'C':
- startupcheck = optarg;
- break;
- case 'd':
- case 's':
- case 'S':
- case 't':
- parse_destination_args(ch);
- break;
- case 'p':
- edi_conf.dest_port = std::stoi(optarg);
- break;
- case 'P':
- edi_conf.enable_pft = false;
- break;
- case 'f':
- edi_conf.fec = std::stoi(optarg);
- break;
- case 'i':
- {
- double interleave_ms = std::stod(optarg);
- if (interleave_ms != 0.0) {
- if (interleave_ms < 0) {
- throw std::runtime_error("EDI output: negative interleave value is invalid.");
+ int ch = 0;
+ while (ch != -1) {
+ ch = getopt(argc, argv, "c:C:d:p:s:S:t:Pf:i:Dva:b:w:xh");
+ switch (ch) {
+ case -1:
+ break;
+ case 'c':
+ source = optarg;
+ break;
+ case 'C':
+ startupcheck = optarg;
+ break;
+ case 'd':
+ case 's':
+ case 'S':
+ case 't':
+ parse_destination_args(ch);
+ break;
+ case 'p':
+ edi_conf.dest_port = std::stoi(optarg);
+ break;
+ case 'P':
+ edi_conf.enable_pft = false;
+ break;
+ case 'f':
+ edi_conf.fec = std::stoi(optarg);
+ break;
+ case 'i':
+ {
+ double interleave_ms = std::stod(optarg);
+ if (interleave_ms != 0.0) {
+ if (interleave_ms < 0) {
+ throw std::runtime_error("EDI output: negative interleave value is invalid.");
+ }
+
+ auto latency_rounded = lround(interleave_ms / 24.0);
+ if (latency_rounded * 24 > 30000) {
+ throw std::runtime_error("EDI output: interleaving set for more than 30 seconds!");
+ }
+
+ edi_conf.latency_frames = latency_rounded;
+ }
}
+ break;
+ case 'D':
+ edi_conf.dump = true;
+ break;
+ case 'v':
+ edi_conf.verbose = true;
+ break;
+ case 'a':
+ edi_conf.tagpacket_alignment = std::stoi(optarg);
+ break;
+ case 'b':
+ backoff_after_reset_ms = std::stoi(optarg);
+ break;
+ case 'w':
+ delay_ms = std::stoi(optarg);
+ break;
+ case 'x':
+ drop_late_packets = true;
+ break;
+ case 'h':
+ default:
+ usage();
+ return 1;
+ }
+ }
- auto latency_rounded = lround(interleave_ms / 24.0);
- if (latency_rounded * 24 > 30000) {
- throw std::runtime_error("EDI output: interleaving set for more than 30 seconds!");
- }
+ if (not startupcheck.empty()) {
+ etiLog.level(info) << "Running startup check '" << startupcheck << "'";
+ int wstatus = system(startupcheck.c_str());
- edi_conf.latency_frames = latency_rounded;
+ if (WIFEXITED(wstatus)) {
+ if (WEXITSTATUS(wstatus) == 0) {
+ etiLog.level(info) << "Startup check ok";
+ }
+ else {
+ etiLog.level(error) << "Startup check failed, returned " << WEXITSTATUS(wstatus);
+ return 1;
}
}
- break;
- case 'D':
- edi_conf.dump = true;
- break;
- case 'v':
- edi_conf.verbose = true;
- break;
- case 'a':
- edi_conf.tagpacket_alignment = std::stoi(optarg);
- break;
- case 'b':
- backoff_after_reset_ms = std::stoi(optarg);
- break;
- case 'w':
- delay_ms = std::stoi(optarg);
- break;
- case 'x':
- drop_late_packets = true;
- break;
- case 'h':
- default:
- usage();
+ else {
+ etiLog.level(error) << "Startup check failed, child didn't terminate normally";
+ return 1;
+ }
+ }
+
+ add_edi_destination();
+
+ if (source.empty()) {
+ etiLog.level(error) << "source option is missing";
return 1;
- }
- }
+ }
+
+ const auto pos_colon = source.find(":");
+ if (pos_colon == string::npos or pos_colon == 0) {
+ etiLog.level(error) << "source does not contain host:port";
+ return 1;
+ }
- if (not startupcheck.empty()) {
- etiLog.level(info) << "Running startup check '" << startupcheck << "'";
- int wstatus = system(startupcheck.c_str());
+ const string connect_to_host = source.substr(0, pos_colon);
+ const int connect_to_port = stod(source.substr(pos_colon+1));
- if (WIFEXITED(wstatus)) {
- if (WEXITSTATUS(wstatus) == 0) {
- etiLog.level(info) << "Startup check ok";
+ if (edi_conf.dest_port == 0) {
+ etiLog.level(error) << "No EDI destination port defined";
+ return 1;
}
- else {
- etiLog.level(error) << "Startup check failed, returned " << WEXITSTATUS(wstatus);
+
+ if (edi_conf.destinations.empty()) {
+ etiLog.level(error) << "No EDI destinations set";
return 1;
}
- }
- else {
- etiLog.level(error) << "Startup check failed, child didn't terminate normally";
- return 1;
- }
- }
- add_edi_destination();
+ edi_decoder.set_verbose(edi_conf.verbose);
+ etiLog.level(info) << "Setting up EDI2EDI with delay " << delay_ms << " ms. " <<
+ (drop_late_packets ? "Will" : "Will not") << " drop late packets";
- if (source.empty()) {
- etiLog.level(error) << "source option is missing";
- return 1;
- }
- const auto pos_colon = source.find(":");
- if (pos_colon == string::npos or pos_colon == 0) {
- etiLog.level(error) << "source does not contain host:port";
- return 1;
- }
+ Socket::TCPSocket sock;
+ etiLog.level(info) << "Connecting to TCP " << connect_to_host << ":" << connect_to_port;
+ sock.connect(connect_to_host, connect_to_port);
- const string connect_to_host = source.substr(0, pos_colon-1);
- const int connect_to_port = stod(source.substr(pos_colon+1));
+ ssize_t ret = 0;
+ do {
+ const size_t bufsize = 32;
+ std::vector<uint8_t> buf(bufsize);
+ ret = sock.recv(buf.data(), buf.size(), 0);
+ if (ret > 0) {
+ buf.resize(ret);
+ std::vector<uint8_t> frame;
+ edi_decoder.push_bytes(buf);
+ }
+ } while (ret > 0);
- if (edi_conf.dest_port == 0) {
- etiLog.level(error) << "No EDI destination port defined";
- return 1;
- }
+ return 0;
+ }
- if (edi_conf.destinations.empty()) {
- etiLog.level(error) << "No EDI destinations set";
- return 1;
- }
+ private:
- etiLog.level(info) << "Setting up EDI2EDI with delay " << delay_ms << " ms. " <<
- (drop_late_packets ? "Will" : "Will not") << " drop late packets";
+ void add_edi_destination(void)
+ {
+ if (not dest_addr_set) {
+ throw std::runtime_error("Destination address not specified for destination number " +
+ std::to_string(edi_conf.destinations.size() + 1));
+ }
+ edi_conf.destinations.push_back(move(edi_destination));
+ edi_destination = std::make_shared<edi::udp_destination_t>();
- Socket::TCPSocket sock;
- etiLog.level(info) << "Connecting to TCP " << connect_to_host << ":" << connect_to_port;
- sock.connect(connect_to_host, connect_to_port);
+ source_port_set = false;
+ source_addr_set = false;
+ ttl_set = false;
+ dest_addr_set = false;
+ }
- ssize_t ret = 0;
- do {
- const size_t bufsize = 32;
- std::vector<uint8_t> buf(bufsize);
- ret = sock.recv(buf.data(), buf.size(), 0);
- if (ret > 0) {
- buf.resize(ret);
- std::vector<uint8_t> frame;
- decoder.push_bytes(buf);
+ void parse_destination_args(char option)
+ {
+ if (not edi_destination) {
+ edi_destination = std::make_shared<edi::udp_destination_t>();
+ }
+
+ switch (option) {
+ case 's':
+ if (source_port_set) {
+ add_edi_destination();
+ }
+ edi_destination->source_port = std::stoi(optarg);
+ source_port_set = true;
+ break;
+ case 'S':
+ if (source_addr_set) {
+ add_edi_destination();
+ }
+ edi_destination->source_addr = optarg;
+ source_addr_set = true;
+ break;
+ case 't':
+ if (ttl_set) {
+ add_edi_destination();
+ }
+ edi_destination->ttl = std::stoi(optarg);
+ ttl_set = true;
+ break;
+ case 'd':
+ if (dest_addr_set) {
+ add_edi_destination();
+ }
+ edi_destination->dest_addr = optarg;
+ dest_addr_set = true;
+ break;
+ default:
+ throw std::logic_error("parse_destination_args invalid");
+ }
}
- } while (ret > 0);
-}
+
+ std::shared_ptr<edi::udp_destination_t> edi_destination;
+ bool source_port_set = false;
+ bool source_addr_set = false;
+ bool ttl_set = false;
+ bool dest_addr_set = false;
+ edi::configuration_t edi_conf;
+ EdiDecoder::ETIDecoder edi_decoder;
+ int delay_ms = 500;
+ bool drop_late_packets = false;
+ uint32_t backoff_after_reset_ms = DEFAULT_BACKOFF;
+ std::string startupcheck;
+ std::string source;
+};
+
int main(int argc, char **argv)
{
@@ -299,7 +342,8 @@ int main(int argc, char **argv)
int ret = 1;
try {
- ret = start(argc, argv);
+ Main m;
+ ret = m.start(argc, argv);
// To make sure things get printed to stderr
std::this_thread::sleep_for(std::chrono::milliseconds(300));