aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2022-06-08 10:31:05 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2022-06-08 10:31:05 +0200
commit0054b36059daa009d2249285446b466afbe002e2 (patch)
treecdb276fe1c369307b4f265c25362f975eb436471
parent33f07f59eaa7feb49e77044ff10c0400a009b85e (diff)
downloadODR-EDI2EDI-next.tar.gz
ODR-EDI2EDI-next.tar.bz2
ODR-EDI2EDI-next.zip
Autoconf: add check to handle -latomicnext
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am18
-rw-r--r--configure.ac34
-rw-r--r--src/EDIReceiver.cpp98
-rw-r--r--src/EDIReceiver.hpp63
-rw-r--r--src/edimcast2edi.cpp242
6 files changed, 446 insertions, 11 deletions
diff --git a/.gitignore b/.gitignore
index 3405b86..f942389 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+libedi2edi.a
+
*.o
*.Po
diff --git a/Makefile.am b/Makefile.am
index 0440179..4d59bc4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,15 +6,25 @@ else
GITVERSION_FLAGS =
endif
-bin_PROGRAMS=odr-edi2edi
+bin_PROGRAMS=odr-edi2edi odr-edimcast2edi
+noinst_LIBRARIES = libedi2edi.a
INCLUDE=-Ilib
odr_edi2edi_CFLAGS =-Wall $(INCLUDE) $(GITVERSION_FLAGS)
odr_edi2edi_CXXFLAGS =-Wall -std=c++14 $(INCLUDE) $(GITVERSION_FLAGS)
-odr_edi2edi_LDADD = -lpthread
-odr_edi2edi_SOURCES = src/main.h src/main.cpp \
- src/receiver.h src/receiver.cpp \
+odr_edi2edi_LDADD = -lpthread libedi2edi.a $(LIBATOMIC_LIBS)
+odr_edi2edi_SOURCES = src/main.h src/main.cpp
+
+odr_edimcast2edi_CFLAGS =-Wall $(INCLUDE) $(GITVERSION_FLAGS)
+odr_edimcast2edi_CXXFLAGS =-Wall -std=c++14 $(INCLUDE) $(GITVERSION_FLAGS)
+odr_edimcast2edi_LDADD = -lpthread libedi2edi.a $(LIBATOMIC_LIBS)
+odr_edimcast2edi_SOURCES = src/edimcast2edi.cpp \
+ src/EDIReceiver.hpp src/EDIReceiver.cpp
+
+libedi2edi_a_CFLAGS =-Wall $(INCLUDE) $(GITVERSION_FLAGS)
+libedi2edi_a_CXXFLAGS =-Wall -std=c++14 $(INCLUDE) $(GITVERSION_FLAGS)
+libedi2edi_a_SOURCES = src/receiver.h src/receiver.cpp \
src/EDISender.h src/EDISender.cpp \
lib/crc.h lib/crc.c \
lib/Globals.cpp \
diff --git a/configure.ac b/configure.ac
index 2ec6d11..8f3723d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,20 +1,21 @@
-# Copyright (C) 2021 Matthias P. Braendli, http://opendigitalradio.org
+# Copyright (C) 2022 Matthias P. Braendli, http://opendigitalradio.org
-
-AC_PREREQ(2.69)
-AC_INIT([ODR-EDI2EDI], [0.4.0], [matthias.braendli@mpb.li])
+AC_PREREQ([2.69])
+AC_INIT([ODR-EDI2EDI],[0.4.0],[matthias.braendli@mpb.li])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
-AC_CANONICAL_SYSTEM
+AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
AC_CONFIG_SRCDIR([src/main.cpp])
-AC_CONFIG_HEADER([config.h])
+AC_CONFIG_HEADERS([config.h])
AM_SILENT_RULES([yes])
# Checks for programs.
+AM_PROG_AR
AC_PROG_CXX
AC_PROG_CC
AM_PROG_CC_C_O
+AC_PROG_RANLIB
AC_PROG_INSTALL
AX_CXX_COMPILE_STDCXX(14,noext,mandatory)
@@ -23,7 +24,9 @@ AX_CXX_COMPILE_STDCXX(14,noext,mandatory)
AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
# Checks for header files.
-AC_HEADER_STDC
+AC_CHECK_INCLUDES_DEFAULT
+AC_PROG_EGREP
+
AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h unistd.h])
AC_LANG_PUSH([C++])
@@ -53,8 +56,25 @@ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[
AC_DEFINE(HAVE_SO_NOSIGPIPE, 1, [Define this symbol if you have SO_NOSIGPIPE]) ],
[ AC_MSG_RESULT(no) ])
+# On some platforms, std::atomic needs a helper library
+AC_MSG_CHECKING(whether -latomic is needed)
+AC_LINK_IFELSE([AC_LANG_SOURCE([[
+ #include <atomic>
+ #include <cstdint>
+ std::atomic<std::int64_t> v;
+ int main() {
+ return v;
+ }
+]])], STD_ATOMIC_NEED_LIBATOMIC=no, STD_ATOMIC_NEED_LIBATOMIC=yes)
+AC_MSG_RESULT($STD_ATOMIC_NEED_LIBATOMIC)
+if test "x$STD_ATOMIC_NEED_LIBATOMIC" = xyes; then
+ LIBATOMIC_LIBS="-latomic"
+fi
+AC_SUBST([LIBATOMIC_LIBS])
+
AC_LANG_POP([C++])
+
AM_CONDITIONAL([IS_GIT_REPO], [test -d '.git'])
AC_CONFIG_FILES([Makefile])
diff --git a/src/EDIReceiver.cpp b/src/EDIReceiver.cpp
new file mode 100644
index 0000000..99be5d3
--- /dev/null
+++ b/src/EDIReceiver.cpp
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2022
+ 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 "EDIReceiver.hpp"
+#include "Log.h"
+#include <cstdio>
+#include <cassert>
+#include <sstream>
+
+namespace EdiDecoder {
+
+using namespace std;
+
+EDIReceiver::EDIReceiver() :
+ m_dispatcher(std::bind(&EDIReceiver::packet_completed, this))
+{
+ using std::placeholders::_1;
+ using std::placeholders::_2;
+ m_dispatcher.register_tag("*ptr",
+ std::bind(&EDIReceiver::decode_starptr, this, _1, _2));
+ m_dispatcher.register_tag("*dmy",
+ std::bind(&EDIReceiver::decode_stardmy, this, _1, _2));
+
+ m_dispatcher.register_tagpacket_handler(std::bind(&EDIReceiver::tagpacket_handler, this, _1));
+}
+
+void EDIReceiver::set_verbose(bool verbose)
+{
+ m_dispatcher.set_verbose(verbose);
+}
+
+
+void EDIReceiver::push_packet(Packet &pack)
+{
+ m_dispatcher.push_packet(pack);
+}
+
+void EDIReceiver::setMaxDelay(int num_af_packets)
+{
+ m_dispatcher.setMaxDelay(num_af_packets);
+}
+
+bool EDIReceiver::decode_starptr(const std::vector<uint8_t>& value, const tag_name_t&)
+{
+ 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);
+ m_protocol = protocol;
+
+ /*
+ uint16_t major = read_16b(value.begin() + 4);
+ uint16_t minor = read_16b(value.begin() + 6);
+ */
+
+ return true;
+}
+
+bool EDIReceiver::decode_stardmy(const std::vector<uint8_t>&, const tag_name_t&)
+{
+ return true;
+}
+
+void EDIReceiver::packet_completed()
+{
+ if (m_protocol != "DETI") {
+ etiLog.level(info) << "Received frame with unknown protocol " << m_protocol;
+ }
+}
+
+void EDIReceiver::tagpacket_handler(const std::vector<uint8_t>& tagpacket)
+{
+ move(tagpacket.begin(), tagpacket.end(), received_tagpackets.end());
+}
+
+}
diff --git a/src/EDIReceiver.hpp b/src/EDIReceiver.hpp
new file mode 100644
index 0000000..150a5c0
--- /dev/null
+++ b/src/EDIReceiver.hpp
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 2022
+ 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 "edi/common.hpp"
+#include <cstdint>
+#include <deque>
+#include <string>
+#include <vector>
+
+namespace EdiDecoder {
+
+class EDIReceiver {
+ public:
+ using subchannel_handler = std::function<void(std::vector<uint8_t>&&, frame_timestamp_t, uint16_t /*dlfc*/)>;
+
+ EDIReceiver();
+
+ void set_verbose(bool verbose);
+
+ /* Push a complete packet into the decoder. Useful for UDP and other
+ * datagram-oriented protocols.
+ */
+ void push_packet(Packet &pack);
+
+ /* Set the maximum delay in number of AF Packets before we
+ * abandon decoding a given pseq.
+ */
+ void setMaxDelay(int num_af_packets);
+
+ std::vector<uint8_t> received_tagpackets;
+
+ private:
+ void packet_completed();
+ void tagpacket_handler(const std::vector<uint8_t>& tagpacket);
+
+ std::string m_protocol = "";
+
+ bool decode_starptr(const std::vector<uint8_t>& value, const tag_name_t& n);
+ bool decode_stardmy(const std::vector<uint8_t>&, const tag_name_t&);
+
+ TagDispatcher m_dispatcher;
+};
+
+}
diff --git a/src/edimcast2edi.cpp b/src/edimcast2edi.cpp
new file mode 100644
index 0000000..8a73d71
--- /dev/null
+++ b/src/edimcast2edi.cpp
@@ -0,0 +1,242 @@
+/*
+ Copyright (C) 2022
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
+ */
+/*
+ This file is part of the ODR-mmbTools.
+
+ 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <algorithm>
+#include <chrono>
+#include <iostream>
+#include <iomanip>
+#include <iterator>
+#include <memory>
+#include <thread>
+#include <vector>
+#include <unordered_map>
+#include <cmath>
+#include <cstring>
+#include <fcntl.h>
+#include <getopt.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include "Log.h"
+#include "main.h"
+#include "edioutput/TagItems.h"
+#include "edioutput/TagPacket.h"
+#include "edioutput/Transport.h"
+#include "EDIReceiver.hpp"
+
+using namespace std;
+
+volatile sig_atomic_t running = 1;
+
+void signal_handler(int signum)
+{
+ if (signum == SIGTERM) {
+ fprintf(stderr, "Received SIGTERM\n");
+ exit(0);
+ }
+ //killpg(0, SIGPIPE);
+ running = 0;
+}
+
+static void usage()
+{
+ cerr << "\nUsage:\n";
+ cerr << "odr-edimcast2edi [options]\n\n";
+ cerr << "Receive EDI over multicast, remove PFT layer and make AF layer available as TCP server\n\n";
+
+ cerr << " -v Increase verbosity (Can be given more than once).\n";
+ cerr << " --version the version and quit.\n\n";
+
+ cerr << "Input settings\n";
+ cerr << " -p PORT Receive UDP on PORT\n";
+ cerr << " -b BINDTO Bind receive socket to BINDTO address\n";
+ cerr << " -m ADDRESS Receive from multicast ADDRESS\n\n";
+
+ cerr << "Output settings\n";
+ cerr << " -l PORT Listen on port PORT\n\n";
+
+ cerr << "It is best practice to run this tool under a process supervisor that will restart it automatically.\n";
+}
+
+static const struct option longopts[] = {
+ {"live-stats-port", required_argument, 0, 2},
+ {0, 0, 0, 0}
+};
+
+int main(int argc, char **argv)
+{
+ // Version handling is done very early to ensure nothing else but the version gets printed out
+ if (argc == 2 and strcmp(argv[1], "--version") == 0) {
+ fprintf(stdout, "%s\n",
+#if defined(GITVERSION)
+ GITVERSION
+#else
+ PACKAGE_VERSION
+#endif
+ );
+ return 0;
+ }
+
+ cerr << "ODR-EDIMCAST2EDI " <<
+#if defined(GITVERSION)
+ GITVERSION <<
+#else
+ PACKAGE_VERSION <<
+#endif
+ " starting up" << endl;
+
+ if (argc == 1) {
+ usage();
+ return 1;
+ }
+
+ int verbosity = 0;
+
+ edi::configuration_t edi_conf;
+ edi_conf.enable_pft = false;
+
+ unsigned int rx_port = 0;
+ string rx_bindto;
+ string rx_mcastaddr;
+
+ int ch = 0;
+ int index = 0;
+ while (ch != -1) {
+ ch = getopt_long(argc, argv, "b:l:m:p:v", longopts, &index);
+ switch (ch) {
+ case 2: // --live-stats-port
+ //int live_stats_port = stoi(optarg);
+ break;
+ case 'b':
+ rx_bindto = optarg;
+ break;
+ case 'l':
+ {
+ auto edi_destination = make_shared<edi::tcp_server_t>();
+ edi_destination->listen_port = stoi(optarg);
+ edi_conf.destinations.push_back(move(edi_destination));
+ }
+ break;
+ case 'm':
+ rx_mcastaddr = optarg;
+ break;
+ case 'p':
+ rx_port = stoi(optarg);
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'h':
+ default:
+ usage();
+ return 1;
+ }
+ }
+
+ edi_conf.verbose = verbosity > 1;
+
+ if (edi_conf.destinations.empty()) {
+ etiLog.level(error) << "No EDI destinations set";
+ return 1;
+ }
+
+ int ret = 1;
+
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = &signal_handler;
+
+ const int sigs[] = {SIGHUP, SIGQUIT, SIGINT, SIGTERM};
+ for (int sig : sigs) {
+ if (sigaction(sig, &sa, nullptr) == -1) {
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+ }
+
+ try {
+ edi::Sender edi_sender(edi_conf);
+
+ Socket::UDPReceiver rx;
+
+ EdiDecoder::EDIReceiver edi_rx;
+
+ rx.add_receive_port(rx_port, rx_bindto, rx_mcastaddr);
+
+ while (running) {
+ vector<Socket::UDPReceiver::ReceivedPacket> rx_packets;
+ try {
+ rx_packets = rx.receive(100);
+ }
+ catch (const Socket::UDPReceiver::Interrupted&) {
+ running = false;
+ }
+ catch (const Socket::UDPReceiver::Timeout&) {
+ }
+
+ for (auto& rp : rx_packets) {
+ auto received_from = rp.received_from;
+ EdiDecoder::Packet p;
+ p.buf = move(rp.packetdata);
+ p.received_on_port = rp.port_received_on;
+ edi_rx.push_packet(p);
+ }
+ }
+
+ /*
+ if (edi_conf.enabled()) {
+ edi::TagPacket edi_tagpacket(0);
+
+ if (tp.seq.seq_valid) {
+ _edi_sender->override_af_sequence(tp.seq.seq);
+ }
+
+ if (tp.seq.pseq_valid) {
+ _edi_sender->override_pft_sequence(tp.seq.pseq);
+ }
+ else if (tp.seq.seq_valid) {
+ // If the source isn't using PFT, set PSEQ = SEQ so that multihoming
+ // with several EDI2EDI instances could work.
+ _edi_sender->override_pft_sequence(tp.seq.seq);
+ }
+
+ edi_tagpacket.raw_tagpacket = move(tp.tagpacket);
+ _edi_sender->write(edi_tagpacket);
+ num_frames.fetch_add(1);
+ }
+ */
+ // To make sure things get printed to stderr
+ this_thread::sleep_for(chrono::milliseconds(300));
+ }
+ catch (const runtime_error &e) {
+ etiLog.level(error) << "Runtime error: " << e.what();
+ }
+ catch (const logic_error &e) {
+ etiLog.level(error) << "Logic error! " << e.what();
+ }
+
+ return ret;
+}
+