aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--decoder/STIWriter.cpp134
-rw-r--r--decoder/STIWriter.hpp76
-rw-r--r--test/main.cpp105
4 files changed, 285 insertions, 31 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b52b116..6d79c7e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,6 +49,7 @@ list(APPEND edilib_cpp_sources
decoder/ETIDecoder.cpp
decoder/STIDecoder.cpp
decoder/ETIWriter.cpp
+ decoder/STIWriter.cpp
decoder/PFT.cpp
decoder/eti.cpp
decoder/common.cpp
diff --git a/decoder/STIWriter.cpp b/decoder/STIWriter.cpp
new file mode 100644
index 0000000..2376f51
--- /dev/null
+++ b/decoder/STIWriter.cpp
@@ -0,0 +1,134 @@
+/*
+ Copyright (C) 2019
+ 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 "STIWriter.hpp"
+#include "crc.h"
+#include "Log.h"
+#include <cstdio>
+#include <cassert>
+#include <stdexcept>
+#include <sstream>
+#include <ctime>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+
+namespace EdiDecoder {
+
+using namespace std;
+
+void STIWriter::update_protocol(
+ const std::string& proto,
+ uint16_t major,
+ uint16_t minor)
+{
+ m_proto_valid = (proto == "DSTI" and major == 0 and minor == 0);
+
+ if (not m_proto_valid) {
+ throw std::invalid_argument("Wrong EDI protocol");
+ }
+}
+
+void STIWriter::reinit()
+{
+ m_proto_valid = false;
+ m_management_data_valid = false;
+ m_stat_valid = false;
+ m_time_valid = false;
+ m_payload_valid = false;
+}
+
+void STIWriter::update_stat(uint8_t stat, uint16_t spid)
+{
+ m_stat = stat;
+ m_spid = spid;
+ m_stat_valid = true;
+
+ if (m_stat != 0xFF) {
+ etiLog.log(warn, "STI errorlevel %02x", m_stat);
+ }
+}
+
+void STIWriter::update_rfad(std::array<uint8_t, 9> rfad)
+{
+ (void)rfad;
+}
+
+void STIWriter::update_sti_management(const sti_management_data& data)
+{
+ m_management_data = data;
+ m_management_data_valid = true;
+}
+
+void STIWriter::add_payload(sti_payload_data&& payload)
+{
+ m_payload = move(payload);
+ m_payload_valid = false;
+}
+
+void STIWriter::update_edi_time(
+ uint32_t utco,
+ uint32_t seconds)
+{
+ if (not m_proto_valid) {
+ throw std::logic_error("Cannot update time before protocol");
+ }
+
+ m_utco = utco;
+ m_seconds = seconds;
+
+ // TODO check validity
+ m_time_valid = true;
+
+}
+
+
+void STIWriter::assemble()
+{
+ if (not m_proto_valid) {
+ throw std::logic_error("Cannot assemble STI before protocol");
+ }
+
+ if (not m_stat_valid) {
+ throw std::logic_error("Cannot assemble STI before STAT");
+ }
+
+ if (not m_management_data_valid) {
+ throw std::logic_error("Cannot assemble STI before management data");
+ }
+
+ if (not m_payload_valid) {
+ throw std::logic_error("Cannot assemble STI without frame data");
+ }
+
+ // TODO check time validity
+}
+
+std::vector<uint8_t> STIWriter::getFrame()
+{
+ if (not m_payload_valid) {
+ return {};
+ }
+
+ return m_payload.istd;
+}
+
+}
+
diff --git a/decoder/STIWriter.hpp b/decoder/STIWriter.hpp
new file mode 100644
index 0000000..579009f
--- /dev/null
+++ b/decoder/STIWriter.hpp
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2019
+ 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 "STIDecoder.hpp"
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <list>
+
+namespace EdiDecoder {
+
+class STIWriter : public STIDataCollector {
+ 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);
+
+ virtual void update_stat(uint8_t stat, uint16_t spid);
+
+ virtual void update_edi_time(
+ uint32_t utco,
+ uint32_t seconds);
+
+ virtual void update_rfad(std::array<uint8_t, 9> rfad);
+ virtual void update_sti_management(const sti_management_data& data);
+ virtual void add_payload(sti_payload_data&& payload);
+
+ virtual void assemble(void);
+
+ // Return the assembled frame or an empty frame if not ready
+ std::vector<uint8_t> getFrame();
+
+ private:
+ void reinit(void);
+
+ bool m_proto_valid = false;
+
+ bool m_management_data_valid = false;
+ sti_management_data m_management_data;
+
+ bool m_stat_valid = false;
+ uint8_t m_stat = 0;
+ uint16_t m_spid = 0;
+
+ bool m_time_valid = false;
+ uint32_t m_utco = 0;
+ uint32_t m_seconds = 0;
+
+ bool m_payload_valid = false;
+ sti_payload_data m_payload;
+};
+
+}
+
diff --git a/test/main.cpp b/test/main.cpp
index 9d76050..dc09adc 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -30,16 +30,19 @@
#include "ETIDecoder.hpp"
#include "ETIWriter.hpp"
+#include "STIDecoder.hpp"
+#include "STIWriter.hpp"
#include "UdpSocket.h"
#include "ThreadsafeQueue.h"
#include "TimestampDecoder.h"
struct options_t {
std::string edi_source;
- std::string eti_file;
+ std::string out_file;
int verbose;
bool enable_packet_loss;
+ bool decode_sti;
int packet_loss_inv_rate;
int max_delay;
@@ -98,11 +101,13 @@ class UdpReceiver {
static void printUsage(char *name)
{
- fprintf(stderr, "Usage:\n %s <edi_source> [-v] [-o eti_file] [-e inv_rate] [-m delay]\n", name);
+ fprintf(stderr, "Usage:\n %s <edi_source> [-v] [-s] [-o file] [-e inv_rate] [-m delay]\n", name);
fprintf(stderr, " edi_source is either a file, udp://:port, or tcp://addr:port\n");
fprintf(stderr, " -v Verbose mode (can be given several times)\n");
+ fprintf(stderr, " -s Decode STI instead of ETI\n");
fprintf(stderr, " -e Enable packet loss simulator with probability 1/inv_rate\n");
fprintf(stderr, " -m Set max delay in AF Packets before we timeout\n");
+ fprintf(stderr, " -o Save decoded ETI or STI to file\n");
}
static options_t parseargs(int argc, char **argv)
@@ -114,7 +119,7 @@ static options_t parseargs(int argc, char **argv)
options.enable_packet_loss = false;
while (true) {
- int c = getopt(argc, argv, "e:hm:o:v");
+ int c = getopt(argc, argv, "e:hm:o:sv");
if (c == -1) {
break;
}
@@ -128,7 +133,10 @@ static options_t parseargs(int argc, char **argv)
options.max_delay = strtol(optarg, nullptr, 10);
break;
case 'o':
- options.eti_file = optarg;
+ options.out_file = optarg;
+ break;
+ case 's':
+ options.decode_sti = true;
break;
case '?':
case 'h':
@@ -192,18 +200,22 @@ int main(int argc, char **argv)
return 1;
}
- FILE* fd_eti = nullptr;
- if (not options.eti_file.empty()) {
- fd_eti = fopen(options.eti_file.c_str(), "w");
+ FILE* fd_out = nullptr;
+ if (not options.out_file.empty()) {
+ fd_out = fopen(options.out_file.c_str(), "w");
}
double offset = 0;
TimestampDecoder ts_dec(offset);
- EdiDecoder::ETIWriter writer;
- EdiDecoder::ETIDecoder decoder(writer, options.verbose > 1);
+ EdiDecoder::ETIWriter eti_writer;
+ EdiDecoder::ETIDecoder eti_decoder(eti_writer, options.verbose > 1);
+
+ EdiDecoder::STIWriter sti_writer;
+ EdiDecoder::STIDecoder sti_decoder(sti_writer, options.verbose > 1);
if (options.max_delay) {
- decoder.setMaxDelay(options.max_delay);
+ eti_decoder.setMaxDelay(options.max_delay);
+ sti_decoder.setMaxDelay(options.max_delay);
}
@@ -223,20 +235,37 @@ int main(int argc, char **argv)
if (options.enable_packet_loss) {
int rand_num = rand() % options.packet_loss_inv_rate;
if (rand_num != 0) {
- decoder.push_packet(buf);
+ if (options.decode_sti) {
+ sti_decoder.push_packet(buf);
+ }
+ else {
+ eti_decoder.push_packet(buf);
+ }
}
}
else {
- decoder.push_packet(buf);
+ if (options.decode_sti) {
+ sti_decoder.push_packet(buf);
+ }
+ else {
+ eti_decoder.push_packet(buf);
+ }
}
- const auto eti = writer.getEtiFrame();
- if (not eti.frame.empty() and options.verbose > 0) {
- analyse_timestamp(ts_dec, writer, eti);
+ std::vector<uint8_t> frame;
+ if (options.decode_sti) {
+ frame = sti_writer.getFrame();
+ }
+ else {
+ const auto eti = eti_writer.getEtiFrame();
+ if (not eti.frame.empty() and options.verbose > 0) {
+ analyse_timestamp(ts_dec, eti_writer, eti);
+ }
+ frame = eti.frame;
}
- if (fd_eti and not eti.frame.empty()) {
- fwrite(eti.frame.data(), eti.frame.size(), 1, fd_eti);
+ if (fd_out and not frame.empty()) {
+ fwrite(frame.data(), frame.size(), 1, fd_out);
}
}
}
@@ -278,16 +307,23 @@ int main(int argc, char **argv)
ret = ::recv(sock, buf.data(), buf.size(), 0);
if (ret > 0) {
buf.resize(ret);
- decoder.push_bytes(buf);
-
- const auto eti = writer.getEtiFrame();
- if (not eti.frame.empty() and options.verbose > 0) {
- analyse_timestamp(ts_dec, writer, eti);
+ std::vector<uint8_t> frame;
+ if (options.decode_sti) {
+ sti_decoder.push_bytes(buf);
+ frame = sti_writer.getFrame();
}
- if (fd_eti) {
- if (not eti.frame.empty()) {
- fwrite(eti.frame.data(), eti.frame.size(), 1, fd_eti);
+ else {
+ eti_decoder.push_bytes(buf);
+
+ const auto eti = eti_writer.getEtiFrame();
+ if (not eti.frame.empty() and options.verbose > 0) {
+ analyse_timestamp(ts_dec, eti_writer, eti);
}
+ frame = eti.frame;
+ }
+
+ if (fd_out and not frame.empty()) {
+ fwrite(frame.data(), frame.size(), 1, fd_out);
}
}
else if (ret == -1) {
@@ -313,13 +349,20 @@ int main(int argc, char **argv)
if (bytes_read > 0) {
buf.resize(bytes_read);
- decoder.push_bytes(buf);
+ std::vector<uint8_t> frame;
+ if (options.decode_sti) {
+ sti_decoder.push_bytes(buf);
+ frame = sti_writer.getFrame();
+ }
+ else {
+ eti_decoder.push_bytes(buf);
- const auto eti = writer.getEtiFrame();
- if (fd_eti) {
- if (not eti.frame.empty()) {
- fwrite(eti.frame.data(), eti.frame.size(), 1, fd_eti);
- }
+ const auto eti = eti_writer.getEtiFrame();
+ frame = eti.frame;
+ }
+
+ if (fd_out and not frame.empty()) {
+ fwrite(frame.data(), frame.size(), 1, fd_out);
}
}
else {