aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml10
-rw-r--r--INSTALL.md21
-rw-r--r--LICENCE5
-rw-r--r--Makefile.am2
-rw-r--r--TODO3
-rw-r--r--configure.ac12
-rw-r--r--doc/advanced.mux7
-rw-r--r--doc/example.mux13
-rw-r--r--src/ClockTAI.cpp3
-rw-r--r--src/ConfigParser.cpp5
-rw-r--r--src/DabMultiplexer.cpp10
-rw-r--r--src/DabMux.cpp3
-rw-r--r--src/InetAddress.cpp127
-rw-r--r--src/InetAddress.h43
-rw-r--r--src/Makefile.am134
-rw-r--r--src/ParserCmdline.cpp4
-rw-r--r--src/ReedSolomon.cpp2
-rw-r--r--src/TcpServer.cpp243
-rw-r--r--src/TcpServer.h84
-rw-r--r--src/TcpSocket.cpp385
-rw-r--r--src/TcpSocket.h127
-rw-r--r--src/ThreadsafeQueue.h144
-rw-r--r--src/UdpSocket.cpp504
-rw-r--r--src/UdpSocket.h197
-rw-r--r--src/dabInputBridgeUdp.cpp6
-rw-r--r--src/dabInputDmbFile.cpp3
-rw-r--r--src/dabInputDmbUdp.cpp1
-rw-r--r--src/dabInputSlip.cpp412
-rw-r--r--src/dabInputSlip.h52
-rw-r--r--src/dabOutput/dabOutput.h84
-rw-r--r--src/dabOutput/dabOutputTcp.cpp257
-rw-r--r--src/dabOutput/dabOutputUdp.cpp14
-rw-r--r--src/fec/LICENSE502
-rw-r--r--src/fec/README.md12
-rw-r--r--src/fec/char.h24
-rw-r--r--src/fec/decode_rs.h298
-rw-r--r--src/fec/decode_rs_char.c22
-rw-r--r--src/fec/encode_rs.h58
-rw-r--r--src/fec/encode_rs_char.c15
-rw-r--r--src/fec/fec.h30
-rw-r--r--src/fec/init_rs.h106
-rw-r--r--src/fec/init_rs_char.c35
-rw-r--r--src/fec/rs-common.h26
-rw-r--r--src/fig/FIG0.cpp19
-rw-r--r--src/fig/FIG0.h2
-rw-r--r--src/utils.cpp5
47 files changed, 2005 insertions, 2067 deletions
diff --git a/.gitignore b/.gitignore
index 54dc3e2..0492be0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,7 @@ config.h.in~
config.h.in
.deps
.dirstamp
+*.plist
cscope.out
ctags
diff --git a/.travis.yml b/.travis.yml
index 791c9c2..ecd00c1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,16 +21,6 @@ compiler:
script:
- |
- pushd /tmp
- git clone https://github.com/Opendigitalradio/ka9q-fec.git
- cd ka9q-fec
- mkdir build
- cd build
- cmake ..
- make
- sudo make install
- popd
- - |
./bootstrap.sh
CC=gcc-5 CXX=g++-5 ./configure --disable-output-edi
make
diff --git a/INSTALL.md b/INSTALL.md
index 0046f9e..4abc06f 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,8 +1,6 @@
Required dependencies:
======================
-* libfec from Phil Karn, with compatibility patch:
- [ka9q-fec](https://github.com/Opendigitalradio/ka9q-fec)
* Boost 1.48 or later
* ZeroMQ 4 from [http://www.zeromq.org](http://www.zeromq.org).
Please prefer the zeromq from your distribution, but mind that some distributions
@@ -12,17 +10,6 @@ Required dependencies:
Simple install procedure using tarball release:
===============================================
-Install libfec
---------------
-
- % git clone https://github.com/Opendigitalradio/ka9q-fec.git
- % cd ka9q-fec
- % ./bootstrap
- % ./configure # Run the configure script
- % make # Build the library
- [as root]
- % make install # Install the library
-
Install odr-dabmux
------------------
@@ -75,11 +62,3 @@ following command for a complete list:
% ./configure --help
-Notes about libfec
-==================
-The original libfec version from
-[ka9q.net](http://www.ka9q.net/code/fec/fec-3.0.1.tar.bz2)
-does not compile on x86\_64 nor on ARM. That is the reason why the patched
-version is suggested.
-
-On x86 systems, the original version can also be used.
diff --git a/LICENCE b/LICENCE
index 7597069..d4fa339 100644
--- a/LICENCE
+++ b/LICENCE
@@ -23,3 +23,8 @@ You should have received a copy of the GNU General Public License
along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+KA9Q FEC routines
+-----------------
+
+src/fec/ contains code from KA9Q's fec library. Please see
+src/fec/README.md and src/fec/LICENSING
diff --git a/Makefile.am b/Makefile.am
index b1578f9..131ba7c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,5 +23,5 @@ SUBDIRS =src lib
ACLOCAL_AMFLAGS = -I m4
-EXTRA_DIST =COPYING NEWS README.md INSTALL.md LICENCE AUTHORS ChangeLog TODO doc
+EXTRA_DIST =COPYING NEWS README.md INSTALL.md LICENCE AUTHORS ChangeLog TODO doc src/fec/README.md src/fec/LICENCE
diff --git a/TODO b/TODO
index a3a7fee..0e5c458 100644
--- a/TODO
+++ b/TODO
@@ -46,6 +46,9 @@ pointers that do dynamic dispatch. Refactoring this to proper classes and
documenting it properly will simplify the addition of new input formats,
facilitate runtime configurability and clarify the usages of the inputs.
+Also, all inputs using UDP are now broken.
+
+Find out what purpose the bridge input serves.
Communicate Leap Seconds
------------------------
diff --git a/configure.ac b/configure.ac
index 03c34d8..c04492c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,11 +58,7 @@ AX_CXX_COMPILE_STDCXX_11(noext,mandatory)
AC_CHECK_LIB([pthread], [pthread_create], [], AC_MSG_ERROR([libpthread is required]))
AX_BOOST_BASE([1.48.0], [], AC_MSG_ERROR([BOOST 1.48 or later is required]))
-# we need to check for libm before checking for fec
AC_CHECK_LIB([m], [sin])
-AC_SEARCH_LIBS([init_rs_char], [fec], [], [
- AC_MSG_ERROR([unable to find libfec])
-])
# Checks for header files.
AC_MSG_CHECKING([for OS type])
@@ -131,12 +127,6 @@ AC_ARG_ENABLE([input_test],
[], [enable_input_test=no])
AS_IF([test "x$enable_input_test" = "xyes"],
[AC_DEFINE(HAVE_INPUT_TEST, [1], [Define if TEST input is enabled])])
-# SLIP
-AC_ARG_ENABLE([input_slip],
- [AS_HELP_STRING([--enable-input-slip], [Enable SLIP input])],
- [], [enable_input_slip=no])
-AS_IF([test "x$enable_input_slip" = "xyes"],
- [AC_DEFINE(HAVE_INPUT_SLIP, [1], [Define if SLIP input is enabled])])
# UDP
AC_ARG_ENABLE([input_udp],
[AS_HELP_STRING([--enable-input-udp], [Enable UDP input])],
@@ -267,7 +257,7 @@ echo
echo "Inputs:"
enabled=""
disabled=""
-for output in prbs test slip udp fifo file
+for output in prbs test udp fifo file
do
eval var=\$enable_input_$output
AS_IF([test "x$var" = "xyes"],
diff --git a/doc/advanced.mux b/doc/advanced.mux
index 99ed4c8..4543998 100644
--- a/doc/advanced.mux
+++ b/doc/advanced.mux
@@ -276,20 +276,21 @@ components {
; FIG 0/13 is only transmitted when figtype is defined.
; The -d option on the command line is:
;datagroup (true|false)
- ; and defaults to false.
+ ; and defaults to false. You should normally set
+ ;datagroup true
+ ; if your packet mode subchannel is tranferring an MOT application such
+ ; as EPG or Slideshow.
}
comp-lu {
service srv-lu
subchannel sub-lu
-
figtype 0x2
}
comp-ri {
service srv-ri
subchannel sub-ri
-
figtype 0x2
}
}
diff --git a/doc/example.mux b/doc/example.mux
index c7f1f2d..5a65829 100644
--- a/doc/example.mux
+++ b/doc/example.mux
@@ -153,10 +153,18 @@ outputs {
; Output RAW ETI NI to standard output
stdout "fifo:///dev/stdout?type=raw"
- ; ZeroMQ output example
+ ; ZeroMQ output example
+ ; This output does not back-pressure the multiplexer.
; Listen on all interfaces, on port 9100
;zmq "zmq+tcp://*:9100"
+ ; Output ETI-over-TCP. This is like piping a RAW ETI NI data stream
+ ; into a TCP socket, except that the output can handle simultaneous
+ ; connections.
+ ; 0.0.0.0 means "listen on all interfaces"
+ ; This output does not back-pressure the multiplexer.
+ ;tcp "tcp://0.0.0.0:9200"
+
; Throttle output to real-time (one ETI frame every 24ms)
;throttle "simul://"
@@ -167,6 +175,7 @@ outputs {
; For an output to a pipe, the data consumer at the other end of the pipe
; will dictate the multiplexing rate to ODR-DabMux.
;
- ; If you use the zmq output, you must also enable a simul:// output!
+ ; If you use the zmq+tcp:// or the tcp:// output,
+ ; you must also enable a simul:// output!
}
diff --git a/src/ClockTAI.cpp b/src/ClockTAI.cpp
index 5f9b750..5e89d9f 100644
--- a/src/ClockTAI.cpp
+++ b/src/ClockTAI.cpp
@@ -48,6 +48,7 @@
#ifdef HAVE_CURL
# include <curl/curl.h>
#endif
+#include <array>
#include <string>
#include <iostream>
#include <algorithm>
@@ -114,7 +115,7 @@ int ClockTAI::parse_tai_offset()
boost::regex regex_bulletin("([0-9]{4}) ([A-Z]{3}) +([0-9]+) =JD +[0-9.]+ +TAI-UTC= *([0-9.]+)");
/* regex groups: Year Month Day Julian date Offset */
- const std::array<std::string,12> months{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
+ const std::array<std::string, 12> months{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
/* I'm not certain about the format they would use if the day is a two-digit number. Will they keep
* two spaces after the month? The regex should be resilient enough in that case.
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index b6d1482..733b5df 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -61,7 +61,6 @@
#include "dabInputEnhancedFifo.h"
#include "dabInputUdp.h"
#include "dabInputBridgeUdp.h"
-#include "dabInputSlip.h"
#include "dabInputTest.h"
#include "dabInputPrbs.h"
#include "dabInputRawFile.h"
@@ -744,10 +743,6 @@ void setup_subchannel_from_ptree(DabSubchannel* subchan,
} else if (proto == "udp") {
operations = dabInputBridgeUdpOperations;
#endif // defined(HAVE_INPUT_UDP)
-#if defined(HAVE_INPUT_SLIP)
- } else if (proto == "slip") {
- operations = dabInputSlipOperations;
-#endif // defined(HAVE_INPUT_SLIP)
#endif // defined(HAVE_FORMAT_BRIDGE)
}
} else if (type == "data") {
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
index 12876aa..3fe3078 100644
--- a/src/DabMultiplexer.cpp
+++ b/src/DabMultiplexer.cpp
@@ -98,16 +98,10 @@ void DabMultiplexer::set_edi_config(const edi_configuration_t& new_edi_conf)
if (edi_conf.enabled()) {
for (auto& edi_destination : edi_conf.destinations) {
- auto edi_output = std::make_shared<UdpSocket>();
- int err = edi_output->create(edi_destination.source_port);
-
- if (err) {
- etiLog.level(error) << "EDI socket creation failed!";
- throw MuxInitException();
- }
+ auto edi_output = std::make_shared<UdpSocket>(edi_destination.source_port);
if (not edi_destination.source_addr.empty()) {
- err = edi_output->setMulticastSource(edi_destination.source_addr.c_str());
+ int err = edi_output->setMulticastSource(edi_destination.source_addr.c_str());
if (err) {
etiLog.level(error) << "EDI socket set source failed!";
throw MuxInitException();
diff --git a/src/DabMux.cpp b/src/DabMux.cpp
index 0420e34..9699ea6 100644
--- a/src/DabMux.cpp
+++ b/src/DabMux.cpp
@@ -105,7 +105,6 @@ typedef DWORD32 uint32_t;
#include "dabInputEnhancedFifo.h"
#include "dabInputUdp.h"
#include "dabInputBridgeUdp.h"
-#include "dabInputSlip.h"
#include "dabInputTest.h"
#include "dabInputPrbs.h"
#include "dabInputRawFile.h"
@@ -500,8 +499,6 @@ int main(int argc, char *argv[])
outputs.clear();
- UdpSocket::clean();
-
if (returnCode != 0) {
etiLog.log(emerg, "...aborting\n");
} else {
diff --git a/src/InetAddress.cpp b/src/InetAddress.cpp
index 3fc33ad..90cdd06 100644
--- a/src/InetAddress.cpp
+++ b/src/InetAddress.cpp
@@ -1,6 +1,11 @@
/*
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
*/
/*
This file is part of ODR-DabMux.
@@ -22,12 +27,8 @@
#include "InetAddress.h"
#include <iostream>
#include <stdio.h>
-
-#ifdef _WIN32
-#else
-# include <errno.h>
-# include <string.h>
-#endif
+#include <errno.h>
+#include <string.h>
#ifdef TRACE_ON
# ifndef TRACE_CLASS
@@ -127,31 +128,33 @@ void InetAddress::setPort(int port)
* @return 0 if ok
* -1 if error
*/
-int InetAddress::setAddress(const char *name)
+int InetAddress::setAddress(const std::string& name)
{
- TRACE_CLASS("InetAddress", "setAddress(char*)");
- if (name) {
- if (atoi(name)) { // If it start with a number
- if ((addr.sin_addr.s_addr = inet_addr(name)) == INADDR_NONE) {
+ TRACE_CLASS("InetAddress", "setAddress(string)");
+ if (!name.empty()) {
+ if (atoi(name.c_str())) { // If it start with a number
+ if ((addr.sin_addr.s_addr = inet_addr(name.c_str())) == INADDR_NONE) {
addr.sin_addr.s_addr = htons(INADDR_ANY);
inetErrNo = 0;
inetErrMsg = "Invalid address";
- inetErrDesc = name;
+ inetErrDesc = name.c_str();
return -1;
}
- } else { // Assume it's a real name
- hostent *host = gethostbyname(name);
+ }
+ else { // Assume it's a real name
+ hostent *host = gethostbyname(name.c_str());
if (host) {
addr.sin_addr = *(in_addr *)(host->h_addr);
} else {
addr.sin_addr.s_addr = htons(INADDR_ANY);
inetErrNo = 0;
inetErrMsg = "Could not find address";
- inetErrDesc = name;
+ inetErrDesc = name.c_str();
return -1;
}
}
- } else {
+ }
+ else {
addr.sin_addr.s_addr = INADDR_ANY;
}
return 0;
@@ -161,100 +164,8 @@ int InetAddress::setAddress(const char *name)
void setInetError(const char* description)
{
inetErrNo = 0;
-#ifdef _WIN32
- inetErrNo = WSAGetLastError();
- switch (inetErrNo) {
- case WSANOTINITIALISED:
- inetErrMsg = "WSANOTINITIALISED A successful WSAStartup must occur before using this function.";
- break;
- case WSAENETDOWN:
- inetErrMsg = "WSAENETDOWN The network subsystem has failed.";
- break;
- case WSAEFAULT:
- inetErrMsg = "WSAEFAULT The buf or from parameters are not part of the user address space, or the fromlen parameter is too small to accommodate the peer address.";
- break;
- case WSAEINTR:
- inetErrMsg = "WSAEINTR The (blocking) call was canceled through WSACancelBlockingCall.";
- break;
- case WSAEINPROGRESS:
- inetErrMsg = "WSAEINPROGRESS A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.";
- break;
- case WSAEINVAL:
- inetErrMsg = "WSAEINVAL The socket has not been bound with bind, or an unknown flag was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled, or (for byte stream-style sockets only) len was zero or negative.";
- break;
- case WSAEISCONN:
- inetErrMsg = "WSAEISCONN The socket is connected. This function is not permitted with a connected socket, whether the socket is connection-oriented or connectionless.";
- break;
- case WSAENETRESET:
- inetErrMsg = "WSAENETRESET The connection has been broken due to the \"keep-alive\" activity detecting a failure while the operation was in progress.";
- break;
- case WSAENOTSOCK:
- inetErrMsg = "WSAENOTSOCK The descriptor is not a socket.";
- break;
- case WSAEOPNOTSUPP:
- inetErrMsg = "WSAEOPNOTSUPP MSG_OOB was specified, but the socket is not stream-style such as type SOCK_STREAM, out-of-band data is not supported in the communication domain associated with this socket, or the socket is unidirectional and supports only send operations.";
- break;
- case WSAESHUTDOWN:
- inetErrMsg = "WSAESHUTDOWN The socket has been shut down; it is not possible to recvfrom on a socket after shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH.";
- break;
- case WSAEWOULDBLOCK:
- inetErrMsg = "WSAEWOULDBLOCK The socket is marked as nonblocking and the recvfrom operation would block.";
- break;
- case WSAEMSGSIZE:
- inetErrMsg = "WSAEMSGSIZE The message was too large to fit into the specified buffer and was truncated.";
- break;
- case WSAETIMEDOUT:
- inetErrMsg = "WSAETIMEDOUT The connection has been dropped, because of a network failure or because the system on the other end went down without notice.";
- break;
- case WSAECONNRESET:
- inetErrMsg = "WSAECONNRESET";
- break;
- case WSAEACCES:
- inetErrMsg = "WSAEACCES The requested address is a broadcast address, but the appropriate flag was not set. Call setsockopt with the SO_BROADCAST parameter to allow the use of the broadcast address.";
- break;
- case WSAENOBUFS:
- inetErrMsg = "WSAENOBUFS No buffer space is available.";
- break;
- case WSAENOTCONN:
- inetErrMsg = "WSAENOTCONN The socket is not connected (connection-oriented sockets only)";
- break;
- case WSAEHOSTUNREACH:
- inetErrMsg = "WSAEHOSTUNREACH The remote host cannot be reached from this host at this time.";
- break;
- case WSAECONNABORTED:
- inetErrMsg = "WSAECONNABORTED The virtual circuit was terminated due to a time-out or other failure. The application should close the socket as it is no longer usable.";
- break;
- case WSAEADDRNOTAVAIL:
- inetErrMsg = "WSAEADDRNOTAVAIL The remote address is not a valid address, for example, ADDR_ANY.";
- break;
- case WSAEAFNOSUPPORT:
- inetErrMsg = "WSAEAFNOSUPPORT Addresses in the specified family cannot be used with this socket.";
- break;
- case WSAEDESTADDRREQ:
- inetErrMsg = "WSAEDESTADDRREQ A destination address is required.";
- break;
- case WSAENETUNREACH:
- inetErrMsg = "WSAENETUNREACH The network cannot be reached from this host at this time.";
- break;
- case WSAEMFILE:
- inetErrMsg = "No more socket descriptors are available.";
- break;
- case WSAEPROTONOSUPPORT:
- inetErrMsg = "The specified protocol is not supported.";
- break;
- case WSAEPROTOTYPE:
- inetErrMsg = "The specified protocol is the wrong type for this socket.";
- break;
- case WSAESOCKTNOSUPPORT:
- inetErrMsg = "The specified socket type is not supported in this address family.";
- break;
- default:
- inetErrMsg = "Unknown";
- };
-#else
inetErrNo = errno;
inetErrMsg = strerror(inetErrNo);
-#endif
inetErrDesc = description;
}
diff --git a/src/InetAddress.h b/src/InetAddress.h
index 266b1fd..0ccc70b 100644
--- a/src/InetAddress.h
+++ b/src/InetAddress.h
@@ -1,6 +1,11 @@
/*
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
*/
/*
This file is part of ODR-DabMux.
@@ -26,33 +31,17 @@
# include "config.h"
#endif
-// General libraries
#include <stdlib.h>
-// Linux librairies
-#ifndef _WIN32
-// # include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <unistd.h>
-# include <netdb.h>
-# include <arpa/inet.h>
-# include <pthread.h>
-# define SOCKET int
-# define INVALID_SOCKET -1
-# define closesocket ::close
-// Windows librairies
-#else
-# include <winsock.h>
-# ifdef _MSC_VER
-# pragma comment(lib, "wsock32.lib")
-# elif defined(__BORLANDC__)
-# pragma(lib, "mswsock.lib")
-# endif
-# ifndef IN_MULTICAST
-# define IN_MULTICAST(a) ((((unsigned long) (a)) & 0xf0000000) == 0xe0000000)
-# endif
-#endif
-// General definitions
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <string>
+
+#define SOCKET int
+#define INVALID_SOCKET -1
#define INVALID_PORT -1
@@ -79,7 +68,7 @@ class InetAddress {
sockaddr *getAddress();
const char *getHostAddress();
int getPort();
- int setAddress(const char *name);
+ int setAddress(const std::string& name);
void setPort(int port);
bool isMulticastAddress();
diff --git a/src/Makefile.am b/src/Makefile.am
index c572ef3..996ebb8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,10 +26,7 @@ else
GITVERSION_FLAGS =
endif
-FEC_FLAGS =
-FEC_LIBS =-lfec
-
-bin_PROGRAMS=odr-dabmux odr-bridgetest zmqinput-keygen
+bin_PROGRAMS=odr-dabmux zmqinput-keygen
if HAVE_OUTPUT_RAW_TEST
bin_PROGRAMS+=odr-zmq2farsync
@@ -45,72 +42,73 @@ endif
odr_dabmux_CFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
odr_dabmux_CXXFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
-odr_dabmux_LDADD =$(FEC_LIBS) $(ZMQ_LIBS) $(CURL_LIBS) \
- -lpthread -lboost_thread -lboost_system -lboost_regex
+odr_dabmux_LDADD =$(ZMQ_LIBS) $(CURL_LIBS) \
+ -lpthread -lboost_thread -lboost_system -lboost_regex
+
odr_dabmux_SOURCES =DabMux.cpp DabMux.h \
DabMultiplexer.cpp DabMultiplexer.h \
- dabInput.h dabInput.cpp \
- dabInputBridgeUdp.h dabInputBridgeUdp.cpp \
- dabInputDabplusFifo.h dabInputDabplusFifo.cpp \
- dabInputDabplusFile.h dabInputDabplusFile.cpp \
- dabInputDmbFile.h dabInputDmbFile.cpp \
- dabInputDmbUdp.h dabInputDmbUdp.cpp \
- dabInputEnhancedFifo.h dabInputEnhancedFifo.cpp \
- dabInputEnhancedPacketFile.h dabInputEnhancedPacketFile.cpp \
- dabInputFifo.h dabInputFifo.cpp \
- dabInputFile.h dabInputFile.cpp \
- dabInputMpegFifo.h dabInputMpegFifo.cpp \
- dabInputMpegFile.h dabInputMpegFile.cpp \
- dabInputPacketFile.h dabInputPacketFile.cpp \
- dabInputPrbs.h dabInputPrbs.cpp \
- dabInputRawFile.h dabInputRawFile.cpp \
- dabInputRawFifo.h dabInputRawFifo.cpp \
- dabInputSlip.h dabInputSlip.cpp \
- dabInputTest.h dabInputTest.cpp \
- dabInputUdp.h dabInputUdp.cpp \
- dabInputZmq.h dabInputZmq.cpp \
- dabOutput/dabOutput.h \
- dabOutput/dabOutputFile.cpp \
- dabOutput/dabOutputFifo.cpp \
- dabOutput/dabOutputRaw.cpp \
- dabOutput/dabOutputSimul.cpp \
- dabOutput/dabOutputTcp.cpp \
- dabOutput/dabOutputUdp.cpp \
- dabOutput/dabOutputZMQ.cpp \
- dabOutput/edi/AFPacket.cpp dabOutput/edi/AFPacket.h \
- dabOutput/edi/TagItems.cpp dabOutput/edi/TagItems.h \
- dabOutput/edi/TagPacket.cpp dabOutput/edi/TagPacket.h \
- dabOutput/edi/PFT.cpp dabOutput/edi/PFT.h \
- ClockTAI.h ClockTAI.cpp \
- ConfigParser.cpp ConfigParser.h \
- Dmb.h Dmb.cpp \
- Eti.h Eti.cpp \
- InetAddress.h InetAddress.cpp \
- Interleaver.h Interleaver.cpp \
- Log.h Log.cpp \
- ManagementServer.h ManagementServer.cpp \
- MuxElements.cpp MuxElements.h \
- ParserCmdline.cpp ParserCmdline.h \
- PcDebug.h \
- ReedSolomon.h ReedSolomon.cpp \
- RemoteControl.cpp RemoteControl.h \
- TcpServer.h TcpServer.cpp \
- TcpSocket.h TcpSocket.cpp \
- UdpSocket.h UdpSocket.cpp \
- bridge.h bridge.c \
- crc.h crc.c \
- fig/FIG.h fig/FIG.cpp \
- fig/FIG0.cpp fig/FIG0.h \
- fig/FIG1.cpp fig/FIG1.h \
- fig/FIGCarousel.cpp fig/FIGCarousel.h \
- mpeg.h mpeg.c \
- prbs.h prbs.c \
- utils.cpp utils.h \
- zmq.hpp
-
-odr_bridgetest_CFLAGS =-DBRIDGE_TEST
-odr_bridgetest_SOURCES =bridge.c \
- crc.c crc.h
+ dabInput.h dabInput.cpp \
+ dabInputBridgeUdp.h dabInputBridgeUdp.cpp \
+ dabInputDabplusFifo.h dabInputDabplusFifo.cpp \
+ dabInputDabplusFile.h dabInputDabplusFile.cpp \
+ dabInputDmbFile.h dabInputDmbFile.cpp \
+ dabInputDmbUdp.h dabInputDmbUdp.cpp \
+ dabInputEnhancedFifo.h dabInputEnhancedFifo.cpp \
+ dabInputEnhancedPacketFile.h dabInputEnhancedPacketFile.cpp \
+ dabInputFifo.h dabInputFifo.cpp \
+ dabInputFile.h dabInputFile.cpp \
+ dabInputMpegFifo.h dabInputMpegFifo.cpp \
+ dabInputMpegFile.h dabInputMpegFile.cpp \
+ dabInputPacketFile.h dabInputPacketFile.cpp \
+ dabInputPrbs.h dabInputPrbs.cpp \
+ dabInputRawFile.h dabInputRawFile.cpp \
+ dabInputRawFifo.h dabInputRawFifo.cpp \
+ dabInputTest.h dabInputTest.cpp \
+ dabInputUdp.h dabInputUdp.cpp \
+ dabInputZmq.h dabInputZmq.cpp \
+ dabOutput/dabOutput.h \
+ dabOutput/dabOutputFile.cpp \
+ dabOutput/dabOutputFifo.cpp \
+ dabOutput/dabOutputRaw.cpp \
+ dabOutput/dabOutputSimul.cpp \
+ dabOutput/dabOutputTcp.cpp \
+ dabOutput/dabOutputUdp.cpp \
+ dabOutput/dabOutputZMQ.cpp \
+ dabOutput/edi/AFPacket.cpp dabOutput/edi/AFPacket.h \
+ dabOutput/edi/TagItems.cpp dabOutput/edi/TagItems.h \
+ dabOutput/edi/TagPacket.cpp dabOutput/edi/TagPacket.h \
+ dabOutput/edi/PFT.cpp dabOutput/edi/PFT.h \
+ ClockTAI.h ClockTAI.cpp \
+ ConfigParser.cpp ConfigParser.h \
+ Dmb.h Dmb.cpp \
+ Eti.h Eti.cpp \
+ InetAddress.h InetAddress.cpp \
+ Interleaver.h Interleaver.cpp \
+ Log.h Log.cpp \
+ ManagementServer.h ManagementServer.cpp \
+ MuxElements.cpp MuxElements.h \
+ ParserCmdline.cpp ParserCmdline.h \
+ PcDebug.h \
+ ReedSolomon.h ReedSolomon.cpp \
+ RemoteControl.cpp RemoteControl.h \
+ TcpSocket.h TcpSocket.cpp \
+ UdpSocket.h UdpSocket.cpp \
+ ThreadsafeQueue.h \
+ bridge.h bridge.c \
+ crc.h crc.c \
+ fig/FIG.h fig/FIG.cpp \
+ fig/FIG0.cpp fig/FIG0.h \
+ fig/FIG1.cpp fig/FIG1.h \
+ fig/FIGCarousel.cpp fig/FIGCarousel.h \
+ mpeg.h mpeg.c \
+ prbs.h prbs.c \
+ utils.cpp utils.h \
+ zmq.hpp \
+ fec/char.h fec/rs-common.h \
+ fec/decode_rs_char.c fec/decode_rs.h \
+ fec/encode_rs_char.c fec/encode_rs.h \
+ fec/fec.h \
+ fec/init_rs_char.c fec/init_rs.h
zmqinput_keygen_SOURCES = zmqinput-keygen.c
zmqinput_keygen_LDADD = $(ZMQ_LIBS)
diff --git a/src/ParserCmdline.cpp b/src/ParserCmdline.cpp
index 723efd6..8ce6f7a 100644
--- a/src/ParserCmdline.cpp
+++ b/src/ParserCmdline.cpp
@@ -284,10 +284,6 @@ bool parse_cmdline(char **argv,
} else if (strcmp((*subchannel)->inputProto, "udp") == 0) {
operations = dabInputBridgeUdpOperations;
#endif // defined(HAVE_INPUT_UDP)
-#if defined(HAVE_INPUT_SLIP)
- } else if (strcmp((*subchannel)->inputProto, "slip") == 0) {
- operations = dabInputSlipOperations;
-#endif // defined(HAVE_INPUT_SLIP)
#endif // defined(HAVE_FORMAT_BRIDGE)
}
} else if (c == 'D') {
diff --git a/src/ReedSolomon.cpp b/src/ReedSolomon.cpp
index c72fb14..69e1191 100644
--- a/src/ReedSolomon.cpp
+++ b/src/ReedSolomon.cpp
@@ -26,7 +26,7 @@
#include <string.h> // For memcpy
extern "C" {
-#include <fec.h>
+#include "fec/fec.h"
}
#include <assert.h>
#include <stdlib.h>
diff --git a/src/TcpServer.cpp b/src/TcpServer.cpp
deleted file mode 100644
index 20ecffb..0000000
--- a/src/TcpServer.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the Queen in
- Right of Canada (Communications Research Center Canada)
- */
-/*
- 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 "TcpServer.h"
-#include <iostream>
-#include <stdio.h>
-#include <errno.h>
-
-#ifdef _WIN32
-#else
-# include <sys/socket.h>
-#endif
-
-#ifdef TRACE_ON
-# ifndef TRACE_CLASS
-# define TRACE_CLASS(class, func) cout <<"-" <<(class) <<"\t(" <<this <<")::" <<(func) <<endl
-# define TRACE_STATIC(class, func) cout <<"-" <<(class) <<"\t(static)::" <<(func) <<endl
-# endif
-#else
-# ifndef TRACE_CLASS
-# define TRACE_CLASS(class, func)
-# define TRACE_STATIC(class, func)
-# endif
-#endif
-
-
-/// Must be call once before doing any operation on sockets
-int TcpServer::init()
-{
-#ifdef _WIN32
- WSADATA wsaData;
- WORD wVersionRequested = wVersionRequested = MAKEWORD( 2, 2 );
-
- int res = WSAStartup( wVersionRequested, &wsaData );
- if (res) {
- setInetError("Can't initialize winsock");
- return -1;
- }
-#endif
- return 0;
-}
-
-
-/// Must be call once before leaving application
-int TcpServer::clean()
-{
-#ifdef _WIN32
- int res = WSACleanup();
- if (res) {
- setInetError("Can't initialize winsock");
- return -1;
- }
-#endif
- return 0;
-}
-
-
-/**
- * Two step constructor. Create must be called prior to use this
- * socket.
- */
-TcpServer::TcpServer() :
- listenSocket(INVALID_SOCKET)
-{
- TRACE_CLASS("TcpServer", "TcpServer()");
-}
-
-
-/**
- * One step constructor.
- * @param port The port number on which the socket will be bind
- * @param name The IP address on which the socket will be bind.
- * It is used to bind the socket on a specific interface if
- * the computer have many NICs.
- */
-TcpServer::TcpServer(int port, const char *name) :
- listenSocket(INVALID_SOCKET)
-{
- TRACE_CLASS("TcpServer", "TcpServer(int, char*)");
- create(port, name);
-}
-
-
-/**
- * Two step initializer. This function must be called after the constructor
- * without argument as been called.
- * @param port The port number on which the socket will be bind
- * @param name The IP address on which the socket will be bind.
- * It is used to bind the socket on a specific interface if
- * the computer have many NICs.
- * @return 0 if ok
- * -1 if error
- */
-int TcpServer::create(int port, const char *name)
-{
- TRACE_CLASS("TcpServer", "create(int, char*)");
- if (listenSocket != INVALID_SOCKET)
- closesocket(listenSocket);
- address.setAddress(name);
- address.setPort(port);
- if ((listenSocket = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
- setInetError("Can't create socket");
- return -1;
- }
- reuseopt_t reuse = 1;
- if (setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
- == SOCKET_ERROR) {
- setInetError("Can't reuse address");
- return -1;
- }
- if (bind(listenSocket, address.getAddress(), sizeof(sockaddr_in)) == SOCKET_ERROR) {
- setInetError("Can't bind socket");
- closesocket(listenSocket);
- listenSocket = INVALID_SOCKET;
- return -1;
- }
- return 0;
-}
-
-
-/**
- * Close the underlying socket.
- * @return 0 if ok
- * -1 if error
- */
-int TcpServer::close()
-{
- TRACE_CLASS("TcpServer", "close()");
- if (listenSocket != INVALID_SOCKET) {
- int res = closesocket(listenSocket);
- if (res != 0) {
- setInetError("Can't close socket");
- return -1;
- }
- listenSocket = INVALID_SOCKET;
- }
- return 0;
-}
-
-
-/// Destructor
-TcpServer::~TcpServer()
-{
- TRACE_CLASS("TcpServer", "~TcpServer()");
- close();
-}
-
-
-int TcpServer::listen()
-{
- TRACE_CLASS("TcpServer", "listen()");
- if (::listen(listenSocket, 1) == SOCKET_ERROR) {
- setInetError("Can't listen on socket");
- return -1;
- }
- return 0;
-}
-
-
-TcpSocket* TcpServer::accept()
-{
- SOCKET socket;
- TcpSocket* client = NULL;
- InetAddress addr;
- socklen_t addrLen = sizeof(sockaddr_in);
-
- socket = ::accept(listenSocket, addr.getAddress(), &addrLen);
- if (socket == SOCKET_ERROR) {
- setInetError("Can't accept connection on socket");
- } else {
- client = new TcpSocket();
- client->setSocket(socket, addr);
- }
- return client;
-}
-
-/*
-WSAEINTR
-WSAEBADF
-WSAEACCES
-WSAEFAULT
-WSAEINVAL
-WSAEMFILE
-WSAEWOULDBLOCK
-WSAEINPROGRESS
-WSAEALREADY
-WSAENOTSOCK
-WSAEDESTADDRREQ
-WSAEMSGSIZE
-WSAEPROTOTYPE
-WSAENOPROTOOPT
-WSAEPROTONOSUPPORT
-WSAESOCKTNOSUPPORT
-WSAEOPNOTSUPP
-WSAEPFNOSUPPORT
-WSAEAFNOSUPPORT
-WSAEADDRINUSE
-WSAEADDRNOTAVAIL
-WSAENETDOWN
-WSAENETUNREACH
-WSAENETRESET
-WSAECONNABORTED
-WSAECONNRESET
-WSAENOBUFS
-WSAEISCONN
-WSAENOTCONN
-WSAESHUTDOWN
-WSAETOOMANYREFS
-WSAETIMEDOUT
-WSAECONNREFUSED
-WSAELOOP
-WSAENAMETOOLONG
-WSAEHOSTDOWN
-WSAEHOSTUNREACH
-WSAENOTEMPTY
-WSAEPROCLIM
-WSAEUSERS
-WSAEDQUOT
-WSAESTALE
-WSAEREMOTE
-WSAEDISCON
-WSASYSNOTREADY
-WSAVERNOTSUPPORTED
-WSANOTINITIALISED
-*/
diff --git a/src/TcpServer.h b/src/TcpServer.h
deleted file mode 100644
index bff7e2e..0000000
--- a/src/TcpServer.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the Queen in
- Right of Canada (Communications Research Center Canada)
- */
-/*
- 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/>.
- */
-
-#ifndef _TCPSERVER
-#define _TCPSERVER
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "InetAddress.h"
-#ifdef _WIN32
-# include <winsock.h>
-# define socklen_t int
-# define reuseopt_t char
-#else
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <unistd.h>
-# include <netdb.h>
-# include <arpa/inet.h>
-# include <pthread.h>
-# define SOCKET int
-# define INVALID_SOCKET -1
-# define SOCKET_ERROR -1
-# define reuseopt_t int
-#endif
-//#define INVALID_PORT -1
-
-#include <iostream>
-#include "TcpSocket.h"
-//#include "SocketSelector.h"
-
-/**
- * This class represents a socket for sending and receiving UDP packets.
- *
- * A UDP socket is the sending or receiving point for a packet delivery service.
- * Each packet sent or received on a datagram socket is individually
- * addressed and routed. Multiple packets sent from one machine to another may
- * be routed differently, and may arrive in any order.
- * @author Pascal Charest pascal.charest@crc.ca
- */
-class TcpServer {
- friend class SocketSelector;
-public:
- TcpServer();
- TcpServer(int port, const char *name = NULL);
- ~TcpServer();
-
- static int init();
- static int clean();
-
- int create(int port, const char *name = NULL);
- int close();
- int listen();
- TcpSocket* accept();
-
-
- protected:
- /// The address on which the socket is binded.
- InetAddress address;
- /// The low-level socket used by system functions.
- SOCKET listenSocket;
-};
-
-#endif // _TCPSERVER
diff --git a/src/TcpSocket.cpp b/src/TcpSocket.cpp
index 75b320f..13efece 100644
--- a/src/TcpSocket.cpp
+++ b/src/TcpSocket.cpp
@@ -1,6 +1,11 @@
/*
Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the Queen in
Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
*/
/*
This file is part of ODR-DabMux.
@@ -20,86 +25,72 @@
*/
#include "TcpSocket.h"
+#include "Log.h"
#include <iostream>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
+#include <stdint.h>
-#ifdef _WIN32
-#else
-# include <unistd.h>
-#endif
-
-#ifdef TRACE_ON
-# ifndef TRACE_CLASS
-# define TRACE_CLASS(class, func) cout <<"-" <<(class) <<"\t(" <<this <<")::" <<(func) <<endl
-# define TRACE_STATIC(class, func) cout <<"-" <<(class) <<"\t(static)::" <<(func) <<endl
-# endif
-#else
-# ifndef TRACE_CLASS
-# define TRACE_CLASS(class, func)
-# define TRACE_STATIC(class, func)
-# endif
-#endif
-
+using namespace std;
-/// Must be call once before doing any operation on sockets
-int TcpSocket::init()
+TcpSocket::TcpSocket() :
+ m_sock(INVALID_SOCKET)
{
-#ifdef _WIN32
- WSADATA wsaData;
- WORD wVersionRequested = wVersionRequested = MAKEWORD( 2, 2 );
-
- int res = WSAStartup( wVersionRequested, &wsaData );
- if (res) {
- setInetError("Can't initialize winsock");
- return -1;
- }
-#endif
- return 0;
+ if ((m_sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
+ throw std::runtime_error("Can't create socket");
+ }
}
-
-/// Must be call once before leaving application
-int TcpSocket::clean()
+TcpSocket::TcpSocket(int port, const string& name) :
+ m_sock(INVALID_SOCKET)
{
-#ifdef _WIN32
- int res = WSACleanup();
- if (res) {
- setInetError("Can't initialize winsock");
- return -1;
- }
-#endif
- return 0;
-}
+ if ((m_sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
+ throw std::runtime_error("Can't create socket");
+ }
+ reuseopt_t reuse = 1;
+ if (setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
+ == SOCKET_ERROR) {
+ throw std::runtime_error("Can't reuse address");
+ }
-/**
- * Two step constructor. Create must be called prior to use this
- * socket.
- */
-TcpSocket::TcpSocket() :
- listenSocket(INVALID_SOCKET)
-{
- TRACE_CLASS("TcpSocket", "TcpSocket()");
+ m_own_address.setAddress(name);
+ m_own_address.setPort(port);
+
+ if (bind(m_sock, m_own_address.getAddress(), sizeof(sockaddr_in)) == SOCKET_ERROR) {
+ ::close(m_sock);
+ m_sock = INVALID_SOCKET;
+ throw std::runtime_error("Can't bind socket");
+ }
}
+TcpSocket::TcpSocket(SOCKET sock, InetAddress own, InetAddress remote) :
+ m_own_address(own),
+ m_remote_address(remote),
+ m_sock(sock) { }
-/**
- * One step constructor.
- * @param port The port number on which the socket will be bind
- * @param name The IP address on which the socket will be bind.
- * It is used to bind the socket on a specific interface if
- * the computer have many NICs.
- */
-TcpSocket::TcpSocket(int port, char *name) :
- listenSocket(INVALID_SOCKET)
+// The move constructors must ensure the moved-from
+// TcpSocket won't destroy our socket handle
+TcpSocket::TcpSocket(TcpSocket&& other)
{
- TRACE_CLASS("TcpSocket", "TcpSocket(int, char*)");
- create(port, name);
+ m_sock = other.m_sock;
+ other.m_sock = INVALID_SOCKET;
+
+ m_own_address = other.m_own_address;
+ m_remote_address = other.m_remote_address;
}
+TcpSocket& TcpSocket::operator=(TcpSocket&& other)
+{
+ m_sock = other.m_sock;
+ other.m_sock = INVALID_SOCKET;
+
+ m_own_address = other.m_own_address;
+ m_remote_address = other.m_remote_address;
+ return *this;
+}
/**
* Close the underlying socket.
@@ -108,259 +99,79 @@ TcpSocket::TcpSocket(int port, char *name) :
*/
int TcpSocket::close()
{
- TRACE_CLASS("TcpSocket", "close()");
- if (listenSocket != INVALID_SOCKET) {
- int res = closesocket(listenSocket);
+ if (m_sock != INVALID_SOCKET) {
+ int res = ::close(m_sock);
if (res != 0) {
setInetError("Can't close socket");
return -1;
}
- listenSocket = INVALID_SOCKET;
+ m_sock = INVALID_SOCKET;
}
- return 0;
-}
-
-
-/**
- * Two step initializer. This function must be called after the constructor
- * without argument as been called.
- * @param port The port number on which the socket will be bind
- * @param name The IP address on which the socket will be bind.
- * It is used to bind the socket on a specific interface if
- * the computer have many NICs.
- * @return 0 if ok
- * -1 if error
- */
-int TcpSocket::create(int port, char *name)
-{
- TRACE_CLASS("TcpSocket", "create(int, char*)");
- if (listenSocket != INVALID_SOCKET)
- closesocket(listenSocket);
- address.setAddress(name);
- address.setPort(port);
- if ((listenSocket = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
- setInetError("Can't create socket");
- return -1;
- }
- reuseopt_t reuse = 1;
- if (setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
- == SOCKET_ERROR) {
- setInetError("Can't reuse address");
- return -1;
- }
- if (bind(listenSocket, address.getAddress(), sizeof(sockaddr_in)) == SOCKET_ERROR) {
- setInetError("Can't bind socket");
- closesocket(listenSocket);
- listenSocket = INVALID_SOCKET;
- return -1;
- }
- return 0;
-}
-
-
-/// Destructor
-TcpSocket::~TcpSocket() {
- TRACE_CLASS("TcpSocket", "~TcpSocket()");
- close();
-}
-
-
-/**
- * Receive an telnet packet, i.e a TCP stream ending with carriage return.
- * @param data The buffer that will receive data.
- * @param size The buffer size.
- * @return > 0 if ok, -1 (SOCKET_ERROR) if error
- */
-int TcpSocket::telnetRead(void* data, int size)
-{
- TRACE_CLASS("TcpSocket", "read(void*, size)");
- int ret;
-
- printf("selectCall\n");
-
- printf("readread 1\n");
- char *line=GetLine(listenSocket);
- ret=strlen(line);
- printf("readread 2\n");
- if (ret <= size)
- {
- strcpy((char*)data, line);
- }
- else
- {
-// size_t n = size;
- strcpy((char*)data, line);
- ret = size;
- }
- printf("TELNET READ returned %d\n", ret);
- return ret;
+ return 0;
}
-/**
- * Receive a TCP stream.
- * @param data The buffer that will receive data.
- * @param size The buffer size.
- * @return > 0 if ok, -1 (SOCKET_ERROR) if error
- */
-int TcpSocket::read(void* data, int size)
+TcpSocket::~TcpSocket()
{
- TRACE_CLASS("TcpSocket", "read(void*, size)");
-
- int ret = recv(listenSocket, (char*)data, size, 0);
- if (ret == SOCKET_ERROR) {
- setInetError("Can't receive TCP packet");
- return -1;
- }
- return ret;
+ close();
}
-
-#define MAX 512
-char* TcpSocket::GetLine(int fd)
+ssize_t TcpSocket::recv(void* data, size_t size)
{
- static char line[MAX];
- static char netread[MAX] = "";
- int n, len;
- char *p;
-
- len = strlen(netread);
-
- /* look for \r\n in netread buffer */
- p = strstr(netread, "\r\n");
- if (p == NULL) {
- /* fill buff - no \r\n found */
- //n = ::read(fd, (void*)(netread+len), (size_t)(MAX-len));
- n = recv(fd, (netread+len), MAX-len, 0);
- if (n == SOCKET_ERROR) {
- setInetError("Can't receive TCP packet");
- return NULL;
+ ssize_t ret = ::recv(m_sock, (char*)data, size, 0);
+ if (ret == SOCKET_ERROR) {
+ stringstream ss;
+ ss << "TCP Socket recv error: " << strerror(errno);
+ throw std::runtime_error(ss.str());
}
- len += n;
- netread[len] = '\0';
- if (n>0)
- return GetLine(fd);
- }
- if (p!=NULL)
- {
- *p = '\0';
- strcpy(line, netread);
- /* copy rest of buf down */
- memmove(netread, p+2, strlen(p+2)+1);
- }
- return line;
+ return ret;
}
-/**
- * Send an TCP packet.
- * @param data The buffer taht will be sent.
- * @param size Number of bytes to send.
- * return 0 if ok, -1 if error
- */
-int TcpSocket::write(const void* data, int size)
+ssize_t TcpSocket::send(const void* data, size_t size)
{
-#ifdef DUMP
- TRACE_CLASS("TcpSocket", "write(const void*, int)");
-#endif
+ /* Without MSG_NOSIGNAL the process would receive a SIGPIPE and die */
+ ssize_t ret = ::send(m_sock, (const char*)data, size, MSG_NOSIGNAL);
- // ignore BROKENPIPE signal (we handle it instead)
-// void* old_sigpipe = signal ( SIGPIPE, SIG_IGN );
- // try to send data
- int ret = send(listenSocket, (const char*)data, size, 0 /*MSG_NOSIGNAL*/ );
- // restore the BROKENPIPE handling
-// signal ( SIGPIPE, (__sighandler_t)old_sigpipe );
- if (ret == SOCKET_ERROR) {
- setInetError("Can't send TCP packet");
- return -1;
- }
- return ret;
+ if (ret == SOCKET_ERROR) {
+ stringstream ss;
+ ss << "TCP Socket send error: " << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+ return ret;
}
-
-int TcpSocket::setDestination(InetAddress &addr)
+void TcpSocket::listen()
{
- address = addr;
- int ret = connect(listenSocket, addr.getAddress(), sizeof(*addr.getAddress()));
- // A etre verifier: code de retour differend entre Linux et Windows
- return ret;
+ if (::listen(m_sock, 1) == SOCKET_ERROR) {
+ stringstream ss;
+ ss << "TCP Socket listen error: " << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
}
-
-void TcpSocket::setSocket(SOCKET socket, InetAddress &addr)
+TcpSocket TcpSocket::accept()
{
- if (listenSocket != INVALID_SOCKET)
- closesocket(listenSocket);
- listenSocket = socket;
- address = addr;
+ InetAddress remote_addr;
+ socklen_t addrLen = sizeof(sockaddr_in);
+
+ SOCKET socket = ::accept(m_sock, remote_addr.getAddress(), &addrLen);
+ if (socket == SOCKET_ERROR) {
+ stringstream ss;
+ ss << "TCP Socket accept error: " << strerror(errno);
+ throw std::runtime_error(ss.str());
+ }
+ else {
+ TcpSocket client(socket, m_own_address, remote_addr);
+ return client;
+ }
}
-
-InetAddress TcpSocket::getAddress()
+InetAddress TcpSocket::getOwnAddress() const
{
- return address;
+ return m_own_address;
}
-
-int TcpSocket::PeekCount()
+InetAddress TcpSocket::getRemoteAddress() const
{
- int count;
- char tempBuffer[3];
- int size=3;
-
- count = recv(listenSocket, tempBuffer, size, MSG_PEEK);
- if (count == -1)
- {
- printf("ERROR WHEN PEEKING SOCKET\n");
- }
- return count;
+ return m_remote_address;
}
-
-/*
-WSAEINTR
-WSAEBADF
-WSAEACCES
-WSAEFAULT
-WSAEINVAL
-WSAEMFILE
-WSAEWOULDBLOCK
-WSAEINPROGRESS
-WSAEALREADY
-WSAENOTSOCK
-WSAEDESTADDRREQ
-WSAEMSGSIZE
-WSAEPROTOTYPE
-WSAENOPROTOOPT
-WSAEPROTONOSUPPORT
-WSAESOCKTNOSUPPORT
-WSAEOPNOTSUPP
-WSAEPFNOSUPPORT
-WSAEAFNOSUPPORT
-WSAEADDRINUSE
-WSAEADDRNOTAVAIL
-WSAENETDOWN
-WSAENETUNREACH
-WSAENETRESET
-WSAECONNABORTED
-WSAECONNRESET
-WSAENOBUFS
-WSAEISCONN
-WSAENOTCONN
-WSAESHUTDOWN
-WSAETOOMANYREFS
-WSAETIMEDOUT
-WSAECONNREFUSED
-WSAELOOP
-WSAENAMETOOLONG
-WSAEHOSTDOWN
-WSAEHOSTUNREACH
-WSAENOTEMPTY
-WSAEPROCLIM
-WSAEUSERS
-WSAEDQUOT
-WSAESTALE
-WSAEREMOTE
-WSAEDISCON
-WSASYSNOTREADY
-WSAVERNOTSUPPORTED
-WSANOTINITIALISED
-*/
diff --git a/src/TcpSocket.h b/src/TcpSocket.h
index c667cfd..f1354a7 100644
--- a/src/TcpSocket.h
+++ b/src/TcpSocket.h
@@ -1,6 +1,11 @@
/*
Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the Queen in
Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
*/
/*
This file is part of ODR-DabMux.
@@ -27,71 +32,73 @@
#endif
#include "InetAddress.h"
-#ifdef _WIN32
-# include <winsock.h>
-# define socklen_t int
-# define reuseopt_t char
-#else
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <unistd.h>
-# include <netdb.h>
-# include <arpa/inet.h>
-# include <pthread.h>
-# define SOCKET int
-# define INVALID_SOCKET -1
-# define SOCKET_ERROR -1
-# define reuseopt_t int
-#endif
-//#define INVALID_PORT -1
-
-//# include "SocketSelector.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#define SOCKET int
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+#define reuseopt_t int
#include <iostream>
+#include <string>
/**
- * This class represents a socket for sending and receiving UDP packets.
- *
- * A UDP socket is the sending or receiving point for a packet delivery service.
- * Each packet sent or received on a datagram socket is individually
- * addressed and routed. Multiple packets sent from one machine to another may
- * be routed differently, and may arrive in any order.
- * @author Pascal Charest pascal.charest@crc.ca
+ * This class represents a TCP socket.
*/
-class TcpSocket {
- friend class SocketSelector;
- public:
- TcpSocket();
- TcpSocket(int port, char *name = NULL);
- ~TcpSocket();
-
- static int init();
- static int clean();
-
- int create(int port = 0, char *name = NULL);
- int close();
-
- int write(const void* data, int size);
- int read(void* data, int size);
- int telnetRead(void* data, int size);
- /**
- * Connects the socket on a specific address. Only data from this address
- * will be received.
- * @param addr The address to connect the socket
- * @warning Not implemented yet.
- */
- int setDestination(InetAddress &addr);
- InetAddress getAddress();
- void setSocket(SOCKET socket, InetAddress &addr);
- char* GetLine(int fd);
-
- int PeekCount();
-
- protected:
- /// The address on which the socket is binded.
- InetAddress address;
- /// The low-level socket used by system functions.
- SOCKET listenSocket;
+class TcpSocket
+{
+ public:
+ /** Create a new socket that does nothing */
+ TcpSocket();
+
+ /** Create a new socket listening for incoming connections.
+ * @param port The port number on which the socket will listen.
+ * @param name The IP address on which the socket will be bound.
+ * It is used to bind the socket on a specific interface if
+ * the computer have many NICs.
+ */
+ TcpSocket(int port, const std::string& name);
+ ~TcpSocket();
+ TcpSocket(TcpSocket&& other);
+ TcpSocket& operator=(TcpSocket&& other);
+
+ int close();
+
+ /** Send data over the TCP connection.
+ * @param data The buffer that will be sent.
+ * @param size Number of bytes to send.
+ * return number of bytes sent or -1 if error
+ */
+ ssize_t send(const void* data, size_t size);
+
+ /** Receive data from the socket.
+ * @param data The buffer that will receive data.
+ * @param size The buffer size.
+ * @return number of bytes received or -1 (SOCKET_ERROR) if error
+ */
+ ssize_t recv(void* data, size_t size);
+
+ void listen(void);
+ TcpSocket accept(void);
+
+ /** Retrieve address this socket is bound to */
+ InetAddress getOwnAddress() const;
+ InetAddress getRemoteAddress() const;
+
+ private:
+ TcpSocket(SOCKET sock, InetAddress own, InetAddress remote);
+ TcpSocket(const TcpSocket& other) = delete;
+ TcpSocket& operator=(const TcpSocket& other) = delete;
+
+ /// The address on which the socket is bound.
+ InetAddress m_own_address;
+ InetAddress m_remote_address;
+ /// The low-level socket used by system functions.
+ SOCKET m_sock;
};
#endif // _TCPSOCKET
diff --git a/src/ThreadsafeQueue.h b/src/ThreadsafeQueue.h
new file mode 100644
index 0000000..d40c472
--- /dev/null
+++ b/src/ThreadsafeQueue.h
@@ -0,0 +1,144 @@
+/*
+ Copyright (C) 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in
+ Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2013, 2014
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ An implementation for a threadsafe queue using boost thread library
+
+ When creating a ThreadsafeQueue, one can specify the minimal number
+ of elements it must contain before it is possible to take one
+ element out.
+ */
+/*
+ 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/>.
+ */
+
+#ifndef THREADSAFE_QUEUE_H
+#define THREADSAFE_QUEUE_H
+
+#include <boost/thread.hpp>
+#include <queue>
+
+/* This queue is meant to be used by two threads. One producer
+ * that pushes elements into the queue, and one consumer that
+ * retrieves the elements.
+ *
+ * The queue can make the consumer block until an element
+ * is available.
+ */
+
+template<typename T>
+class ThreadsafeQueue
+{
+public:
+ /* Push one element into the queue, and notify another thread that
+ * might be waiting.
+ *
+ * returns the new queue size.
+ */
+ size_t push(T const& val)
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ the_queue.push(val);
+ size_t queue_size = the_queue.size();
+ lock.unlock();
+
+ the_rx_notification.notify_one();
+
+ return queue_size;
+ }
+
+ /* Push one element into the queue, but wait until the
+ * queue size goes below the threshold.
+ *
+ * Notify waiting thread.
+ *
+ * returns the new queue size.
+ */
+ size_t push_wait_if_full(T const& val, size_t threshold)
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ while (the_queue.size() >= threshold) {
+ the_tx_notification.wait(lock);
+ }
+ the_queue.push(val);
+ size_t queue_size = the_queue.size();
+ lock.unlock();
+
+ the_rx_notification.notify_one();
+
+ return queue_size;
+ }
+
+ /* Send a notification for the receiver thread */
+ void notify(void)
+ {
+ the_rx_notification.notify_one();
+ }
+
+ bool empty() const
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ return the_queue.empty();
+ }
+
+ size_t size() const
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ return the_queue.size();
+ }
+
+ bool try_pop(T& popped_value)
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ if (the_queue.empty()) {
+ return false;
+ }
+
+ popped_value = the_queue.front();
+ the_queue.pop();
+
+ lock.unlock();
+ the_tx_notification.notify_one();
+
+ return true;
+ }
+
+ void wait_and_pop(T& popped_value, size_t prebuffering = 1)
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ while (the_queue.size() < prebuffering) {
+ the_rx_notification.wait(lock);
+ }
+
+ popped_value = the_queue.front();
+ the_queue.pop();
+
+ lock.unlock();
+ the_tx_notification.notify_one();
+ }
+
+private:
+ std::queue<T> the_queue;
+ mutable boost::mutex the_mutex;
+ boost::condition_variable the_rx_notification;
+ boost::condition_variable the_tx_notification;
+};
+
+#endif
+
diff --git a/src/UdpSocket.cpp b/src/UdpSocket.cpp
index 8ac3706..020e3f5 100644
--- a/src/UdpSocket.cpp
+++ b/src/UdpSocket.cpp
@@ -2,7 +2,9 @@
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
Queen in Right of Canada (Communications Research Center Canada)
- Copyright (C) 2015 Matthias P. Braendli
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
http://www.opendigitalradio.org
*/
/*
@@ -30,228 +32,126 @@
#include <fcntl.h>
#include <string.h>
-#ifdef TRACE_ON
-# ifndef TRACE_CLASS
-# define TRACE_CLASS(class, func) cout <<"-" <<(class) <<"\t(" <<this <<")::" <<(func) <<endl
-# define TRACE_STATIC(class, func) cout <<"-" <<(class) <<"\t(static)::" <<(func) <<endl
-# endif
-#else
-# ifndef TRACE_CLASS
-# define TRACE_CLASS(class, func)
-# define TRACE_STATIC(class, func)
-# endif
-#endif
-
-
-/// Must be call once before doing any operation on sockets
-int UdpSocket::init()
-{
-#ifdef _WIN32
- WSADATA wsaData;
- WORD wVersionRequested = wVersionRequested = MAKEWORD( 2, 2 );
-
- int res = WSAStartup( wVersionRequested, &wsaData );
- if (res) {
- setInetError("Can't initialize winsock");
- return -1;
- }
-#endif
- return 0;
-}
-
+using namespace std;
-/// Must be call once before leaving application
-int UdpSocket::clean()
+UdpSocket::UdpSocket() :
+ listenSocket(INVALID_SOCKET)
{
-#ifdef _WIN32
- int res = WSACleanup();
- if (res) {
- setInetError("Can't initialize winsock");
- return -1;
- }
-#endif
- return 0;
+ init_sock(0, "");
}
-
-/**
- * Two step constructor. Create must be called prior to use this
- * socket.
- */
-UdpSocket::UdpSocket() :
- listenSocket(INVALID_SOCKET)
+UdpSocket::UdpSocket(int port) :
+ listenSocket(INVALID_SOCKET)
{
- TRACE_CLASS("UdpSocket", "UdpSocket()");
+ init_sock(port, "");
}
-
-/**
- * One step constructor.
- * @param port The port number on which the socket will be bind
- * @param name The IP address on which the socket will be bind.
- * It is used to bind the socket on a specific interface if
- * the computer have many NICs.
- */
-UdpSocket::UdpSocket(int port, char *name) :
- listenSocket(INVALID_SOCKET)
+UdpSocket::UdpSocket(int port, const std::string& name) :
+ listenSocket(INVALID_SOCKET)
{
- TRACE_CLASS("UdpSocket", "UdpSocket(int, char*)");
- create(port, name);
+ init_sock(port, name);
}
-/**
- * This functin set blocking mode. The socket can be blocking or not,
- * depending of the parametre. By default, the socket is blocking.
- * @param block If true, set the socket blocking, otherwise set non-blocking
- * @return 0 if ok
- * -1 if error
- */
int UdpSocket::setBlocking(bool block)
{
-#ifdef _WIN32
- unsigned long res = block ? 0 : 1;
- if (ioctlsocket(listenSocket, FIONBIO, &res) != 0) {
+ int res;
+ if (block)
+ res = fcntl(listenSocket, F_SETFL, 0);
+ else
+ res = fcntl(listenSocket, F_SETFL, O_NONBLOCK);
+ if (res == SOCKET_ERROR) {
setInetError("Can't change blocking state of socket");
return -1;
- }
+ }
return 0;
-#else
- int res;
- if (block)
- res = fcntl(listenSocket, F_SETFL, 0);
- else
- res = fcntl(listenSocket, F_SETFL, O_NONBLOCK);
- if (res == SOCKET_ERROR) {
- setInetError("Can't change blocking state of socket");
- return -1;
- }
- return 0;
-#endif
}
-
-/**
- * Two step initializer. This function must be called after the constructor
- * without argument as been called.
- * @param port The port number on which the socket will be bind
- * @param name The IP address on which the socket will be bind.
- * It is used to bind the socket on a specific interface if
- * the computer have many NICs.
- * @return 0 if ok
- * -1 if error
- */
-int UdpSocket::create(int port, char *name)
+int UdpSocket::init_sock(int port, const std::string& name)
{
- TRACE_CLASS("UdpSocket", "create(int, char*)");
- if (listenSocket != INVALID_SOCKET)
- closesocket(listenSocket);
- address.setAddress(name);
- address.setPort(port);
- if ((listenSocket = socket(PF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
- setInetError("Can't create socket");
- return -1;
- }
- reuseopt_t reuse = 1;
- if (setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
- == SOCKET_ERROR) {
- setInetError("Can't reuse address");
- return -1;
- }
-
- if (bind(listenSocket, address.getAddress(), sizeof(sockaddr_in)) == SOCKET_ERROR) {
- setInetError("Can't bind socket");
- closesocket(listenSocket);
- listenSocket = INVALID_SOCKET;
- return -1;
- }
- return 0;
+ if (listenSocket != INVALID_SOCKET) {
+ ::close(listenSocket);
+ }
+
+ if ((listenSocket = socket(PF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
+ setInetError("Can't create socket");
+ return -1;
+ }
+ reuseopt_t reuse = 1;
+ if (setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
+ == SOCKET_ERROR) {
+ setInetError("Can't reuse address");
+ return -1;
+ }
+
+ if (port) {
+ address.setAddress(name);
+ address.setPort(port);
+
+ if (bind(listenSocket, address.getAddress(), sizeof(sockaddr_in)) == SOCKET_ERROR) {
+ setInetError("Can't bind socket");
+ ::close(listenSocket);
+ listenSocket = INVALID_SOCKET;
+ return -1;
+ }
+ }
+ return 0;
}
-/// Destructor
-UdpSocket::~UdpSocket() {
- TRACE_CLASS("UdpSocket", "~UdpSocket()");
- if (listenSocket != INVALID_SOCKET)
- closesocket(listenSocket);
+UdpSocket::~UdpSocket()
+{
+ if (listenSocket != INVALID_SOCKET) {
+ ::close(listenSocket);
+ }
}
-/**
- * Receive an UDP packet.
- * @param packet The packet that will receive data. The address will be set
- * to the source address.
- * @return 0 if ok, -1 if error
- */
-int UdpSocket::receive(UdpPacket &packet)
+int UdpSocket::receive(UdpPacket& packet)
{
- TRACE_CLASS("UdpSocket", "receive(UdpPacket)");
- socklen_t addrSize;
- addrSize = sizeof(*packet.getAddress().getAddress());
- int ret = recvfrom(listenSocket, packet.getData(), packet.getSize() - packet.getOffset(), 0,
- packet.getAddress().getAddress(), &addrSize);
- if (ret == SOCKET_ERROR) {
- packet.setLength(0);
-#ifndef _WIN32
- if (errno == EAGAIN)
+ socklen_t addrSize;
+ addrSize = sizeof(*packet.getAddress().getAddress());
+ ssize_t ret = recvfrom(listenSocket,
+ packet.getData(),
+ packet.getSize(),
+ 0,
+ packet.getAddress().getAddress(),
+ &addrSize);
+
+ if (ret == SOCKET_ERROR) {
+ packet.setSize(0);
+ if (errno == EAGAIN) {
+ return 0;
+ }
+ setInetError("Can't receive UDP packet");
+ return -1;
+ }
+
+ packet.setSize(ret);
return 0;
-#endif
- setInetError("Can't receive UDP packet");
- return -1;
- }
- packet.setLength(ret);
- if (ret == (long)packet.getSize()) {
- packet.setSize(packet.getSize() << 1);
- }
- return 0;
}
-/**
- * Send an UDP packet.
- * @param packet The UDP packet to be sent. It includes the data and the
- * destination address
- * return 0 if ok, -1 if error
- */
-int UdpSocket::send(UdpPacket &packet)
+int UdpSocket::send(UdpPacket& packet)
{
-#ifdef DUMP
- TRACE_CLASS("UdpSocket", "send(UdpPacket)");
-#endif
- int ret = sendto(listenSocket, packet.getData(), packet.getLength(), 0,
- packet.getAddress().getAddress(), sizeof(*packet.getAddress().getAddress()));
- if (ret == SOCKET_ERROR
-#ifndef _WIN32
- && errno != ECONNREFUSED
-#endif
- ) {
- setInetError("Can't send UDP packet");
- return -1;
- }
- return 0;
+ int ret = sendto(listenSocket, packet.getData(), packet.getSize(), 0,
+ packet.getAddress().getAddress(), sizeof(*packet.getAddress().getAddress()));
+ if (ret == SOCKET_ERROR && errno != ECONNREFUSED) {
+ setInetError("Can't send UDP packet");
+ return -1;
+ }
+ return 0;
}
-/**
- * Send an UDP packet
- *
- * return 0 if ok, -1 if error
- */
-int UdpSocket::send(std::vector<uint8_t> data, InetAddress destination)
+int UdpSocket::send(const std::vector<uint8_t>& data, InetAddress destination)
{
-#ifdef DUMP
- TRACE_CLASS("UdpSocket", "send(vector<uint8_t>)");
-#endif
- int ret = sendto(listenSocket, &data[0], data.size(), 0,
- destination.getAddress(), sizeof(*destination.getAddress()));
- if (ret == SOCKET_ERROR
-#ifndef _WIN32
- && errno != ECONNREFUSED
-#endif
- ) {
- setInetError("Can't send UDP packet");
- return -1;
- }
- return 0;
+ int ret = sendto(listenSocket, &data[0], data.size(), 0,
+ destination.getAddress(), sizeof(*destination.getAddress()));
+ if (ret == SOCKET_ERROR && errno != ECONNREFUSED) {
+ setInetError("Can't send UDP packet");
+ return -1;
+ }
+ return 0;
}
@@ -263,36 +163,22 @@ st address to join.
*/
int UdpSocket::joinGroup(char* groupname)
{
- TRACE_CLASS("UdpSocket", "joinGroup(char*)");
-#ifdef _WIN32
- ip_mreq group;
-#else
- ip_mreqn group;
-#endif
- if ((group.imr_multiaddr.s_addr = inet_addr(groupname)) == INADDR_NONE) {
- setInetError(groupname);
- return -1;
- }
- if (!IN_MULTICAST(ntohl(group.imr_multiaddr.s_addr))) {
- setInetError("Not a multicast address");
- return -1;
- }
-#ifdef _WIN32
- group.imr_interface.s_addr = 0;
- if (setsockopt(listenSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&group, sizeof(group))
- == SOCKET_ERROR) {
- setInetError("Can't join multicast group");
- return -1;
- }
-#else
- group.imr_address.s_addr = htons(INADDR_ANY);;
- group.imr_ifindex = 0;
- if (setsockopt(listenSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group))
- == SOCKET_ERROR) {
- setInetError("Can't join multicast group");
- }
-#endif
- return 0;
+ ip_mreqn group;
+ if ((group.imr_multiaddr.s_addr = inet_addr(groupname)) == INADDR_NONE) {
+ setInetError(groupname);
+ return -1;
+ }
+ if (!IN_MULTICAST(ntohl(group.imr_multiaddr.s_addr))) {
+ setInetError("Not a multicast address");
+ return -1;
+ }
+ group.imr_address.s_addr = htons(INADDR_ANY);;
+ group.imr_ifindex = 0;
+ if (setsockopt(listenSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group))
+ == SOCKET_ERROR) {
+ setInetError("Can't join multicast group");
+ }
+ return 0;
}
int UdpSocket::setMulticastTTL(int ttl)
@@ -323,188 +209,38 @@ int UdpSocket::setMulticastSource(const char* source_addr)
return 0;
}
+UdpPacket::UdpPacket() { }
-/**
- * Constructs an UDP packet.
- * @param initSize The initial size of the data buffer
- */
-UdpPacket::UdpPacket(unsigned int initSize) :
- dataBuf(new char[initSize]),
- length(0),
- size(initSize),
- offset(0)
-{
- TRACE_CLASS("UdpPacket", "UdpPacket(unsigned int)");
- if (dataBuf == NULL)
- size = 0;
-}
+UdpPacket::UdpPacket(size_t initSize) :
+ m_buffer(initSize)
+{ }
-/// Destructor
-UdpPacket::~UdpPacket()
+void UdpPacket::setSize(size_t newSize)
{
- TRACE_CLASS("UdpPacket", "~UdpPacket()");
- if (dataBuf != NULL) {
- delete []dataBuf;
- dataBuf = NULL;
- }
+ m_buffer.resize(newSize);
}
-
-/**
- * Changes size of the data buffer size. \a Length + \a offset data will be copied
- * in the new buffer.
- * @warning The pointer to data will be changed
- * @param newSize The new data buffer size
- */
-void UdpPacket::setSize(unsigned newSize)
-{
- TRACE_CLASS("UdpPacket", "setSize(unsigned)");
- char *tmp = new char[newSize];
- if (length > newSize)
- length = newSize;
- if (tmp) {
- memcpy(tmp, dataBuf, length);
- delete []dataBuf;
- dataBuf = tmp;
- size = newSize;
- }
-}
-
-/**
- * Give the pointer to data. It is ajusted with the \a offset.
- * @warning This pointer change. when the \a size of the buffer and the \a offset change.
- * @return The pointer
- */
-char *UdpPacket::getData()
+uint8_t* UdpPacket::getData()
{
- return dataBuf + offset;
+ return &m_buffer[0];
}
-/**
- * Add some data at the end of data buffer and adjust size.
- * @param data Pointer to the data to add
- * @param size Size in bytes of new data
- */
-void UdpPacket::addData(const void *data, unsigned size)
+void UdpPacket::addData(const void *data, size_t size)
{
- if (length + size > this->size) {
- setSize(this->size << 1);
- }
- memcpy(dataBuf + length, data, size);
- length += size;
+ uint8_t *d = (uint8_t*)data;
+ std::copy(d, d + size, std::back_inserter(m_buffer));
}
-
-/**
- * Returns the length of useful data. Data before the \a offset are ignored.
- * @return The data length
- */
-unsigned long UdpPacket::getLength()
+size_t UdpPacket::getSize()
{
- return length - offset;
+ return m_buffer.size();
}
-
-/**
- * Returns the size of the data buffer.
- * @return The data buffer size
- */
-unsigned long UdpPacket::getSize()
+InetAddress UdpPacket::getAddress()
{
- return size;
+ return address;
}
-
-/**
- * Returns the offset value.
- * @return The offset value
- */
-unsigned long UdpPacket::getOffset()
-{
- return offset;
-}
-
-
-/**
- * Sets the data length value. Data before the \a offset are ignored.
- * @param len The new length of data
- */
-void UdpPacket::setLength(unsigned long len)
-{
- length = len + offset;
-}
-
-
-/**
- * Sets the data offset. Data length is ajusted to ignore data before the \a offset.
- * @param val The new data offset.
- */
-void UdpPacket::setOffset(unsigned long val)
-{
- offset = val;
- if (offset > length)
- length = offset;
-}
-
-
-/**
- * Returns the UDP address of the data.
- * @return The UDP address
- */
-InetAddress &UdpPacket::getAddress()
-{
- return address;
-}
-
-/*
-WSAEINTR
-WSAEBADF
-WSAEACCES
-WSAEFAULT
-WSAEINVAL
-WSAEMFILE
-WSAEWOULDBLOCK
-WSAEINPROGRESS
-WSAEALREADY
-WSAENOTSOCK
-WSAEDESTADDRREQ
-WSAEMSGSIZE
-WSAEPROTOTYPE
-WSAENOPROTOOPT
-WSAEPROTONOSUPPORT
-WSAESOCKTNOSUPPORT
-WSAEOPNOTSUPP
-WSAEPFNOSUPPORT
-WSAEAFNOSUPPORT
-WSAEADDRINUSE
-WSAEADDRNOTAVAIL
-WSAENETDOWN
-WSAENETUNREACH
-WSAENETRESET
-WSAECONNABORTED
-WSAECONNRESET
-WSAENOBUFS
-WSAEISCONN
-WSAENOTCONN
-WSAESHUTDOWN
-WSAETOOMANYREFS
-WSAETIMEDOUT
-WSAECONNREFUSED
-WSAELOOP
-WSAENAMETOOLONG
-WSAEHOSTDOWN
-WSAEHOSTUNREACH
-WSAENOTEMPTY
-WSAEPROCLIM
-WSAEUSERS
-WSAEDQUOT
-WSAESTALE
-WSAEREMOTE
-WSAEDISCON
-WSASYSNOTREADY
-WSAVERNOTSUPPORTED
-WSANOTINITIALISED
-*/
diff --git a/src/UdpSocket.h b/src/UdpSocket.h
index 07e9f0e..535499e 100644
--- a/src/UdpSocket.h
+++ b/src/UdpSocket.h
@@ -2,7 +2,9 @@
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the
Queen in Right of Canada (Communications Research Center Canada)
- Copyright (C) 2015 Matthias P. Braendli
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
http://www.opendigitalradio.org
*/
/*
@@ -30,23 +32,16 @@
#endif
#include "InetAddress.h"
-#ifdef _WIN32
-# include <winsock.h>
-# define socklen_t int
-# define reuseopt_t char
-#else
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <unistd.h>
-# include <netdb.h>
-# include <arpa/inet.h>
-# include <pthread.h>
-# define SOCKET int
-# define INVALID_SOCKET -1
-# define SOCKET_ERROR -1
-# define reuseopt_t int
-#endif
-//#define INVALID_PORT -1
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#define SOCKET int
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+#define reuseopt_t int
#include <stdlib.h>
#include <iostream>
@@ -62,77 +57,111 @@ class UdpPacket;
* Each packet sent or received on a datagram socket is individually
* addressed and routed. Multiple packets sent from one machine to another may
* be routed differently, and may arrive in any order.
- * @author Pascal Charest pascal.charest@crc.ca
*/
-class UdpSocket {
- public:
- UdpSocket();
- UdpSocket(int port, char *name = NULL);
- ~UdpSocket();
- UdpSocket(const UdpSocket& other) = delete;
- const UdpSocket& operator=(const UdpSocket& other) = delete;
-
- static int init();
- static int clean();
-
- int create(int port = 0, char *name = NULL);
-
- int send(UdpPacket &packet);
- int send(const std::vector<uint8_t> data);
- int send(std::vector<uint8_t> data, InetAddress destination);
- int receive(UdpPacket &packet);
- int joinGroup(char* groupname);
- int setMulticastSource(const char* source_addr);
- int setMulticastTTL(int ttl);
- /**
- * Connects the socket on a specific address. Only data from this address
- * will be received.
- * @param addr The address to connect the socket
- * @warning Not implemented yet.
- */
- void connect(InetAddress &addr);
- int setBlocking(bool block);
-
- protected:
- /// The address on which the socket is binded.
- InetAddress address;
- /// The low-level socket used by system functions.
- SOCKET listenSocket;
+class UdpSocket
+{
+ public:
+ /** Create a new socket that will not be bound to any port. To be used
+ * for data output.
+ */
+ UdpSocket();
+ /** Create a new socket.
+ * @param port The port number on which the socket will be bound
+ */
+ UdpSocket(int port);
+ /** Create a new socket.
+ * @param port The port number on which the socket will be bound
+ * @param name The IP address on which the socket will be bound.
+ * It is used to bind the socket on a specific interface if
+ * the computer have many NICs.
+ */
+ UdpSocket(int port, const std::string& name);
+ ~UdpSocket();
+ UdpSocket(const UdpSocket& other) = delete;
+ const UdpSocket& operator=(const UdpSocket& other) = delete;
+
+ /** Send an UDP packet.
+ * @param packet The UDP packet to be sent. It includes the data and the
+ * destination address
+ * return 0 if ok, -1 if error
+ */
+ int send(UdpPacket& packet);
+
+ /** Send an UDP packet
+ *
+ * return 0 if ok, -1 if error
+ */
+ int send(const std::vector<uint8_t>& data, InetAddress destination);
+
+ /** Receive an UDP packet.
+ * @param packet The packet that will receive the data. The address will be set
+ * to the source address.
+ * @return 0 if ok, -1 if error
+ */
+ int receive(UdpPacket& packet);
+
+ int joinGroup(char* groupname);
+ int setMulticastSource(const char* source_addr);
+ int setMulticastTTL(int ttl);
+
+ /** Set blocking mode. By default, the socket is blocking.
+ * @return 0 if ok
+ * -1 if error
+ */
+ int setBlocking(bool block);
+
+ protected:
+ int init_sock(int port, const std::string& name);
+
+ /// The address on which the socket is bound.
+ InetAddress address;
+ /// The low-level socket used by system functions.
+ SOCKET listenSocket;
};
-/**
- * This class represents a UDP packet.
+/** This class represents a UDP packet.
*
- * UDP packets are used to implement a connectionless packet delivery service.
- * Each message is routed from one machine to another based solely on
- * information contained within that packet. Multiple packets sent from one
- * machine to another might be routed differently, and might arrive in any order.
- * @author Pascal Charest pascal.charest@crc.ca
+ * A UDP packet contains a payload (sequence of bytes) and an address. For
+ * outgoing packets, the address is the destination address. For incoming
+ * packets, the address tells the user from what source the packet arrived from.
*/
-class UdpPacket {
- public:
- UdpPacket(unsigned int initSize = 1024);
- UdpPacket(const UdpPacket& packet) = delete;
- const UdpPacket& operator=(const UdpPacket&) = delete;
- UdpPacket(const UdpPacket&& packet) = delete;
- const UdpPacket& operator=(const UdpPacket&&) = delete;
- ~UdpPacket();
-
- char *getData();
- void addData(const void *data, unsigned size);
- unsigned long getLength();
- unsigned long getSize();
- unsigned long getOffset();
- void setLength(unsigned long len);
- void setOffset(unsigned long val);
- void setSize(unsigned newSize);
- InetAddress &getAddress();
-
- private:
- char *dataBuf;
- unsigned long length, size, offset;
- InetAddress address;
+class UdpPacket
+{
+ public:
+ /** Construct an empty UDP packet.
+ */
+ UdpPacket();
+ UdpPacket(size_t initSize);
+ UdpPacket(const UdpPacket& packet) = delete;
+ const UdpPacket& operator=(const UdpPacket&) = delete;
+ UdpPacket(const UdpPacket&& packet) = delete;
+ const UdpPacket& operator=(const UdpPacket&&) = delete;
+
+ /** Give the pointer to data.
+ * @return The pointer
+ */
+ uint8_t* getData(void);
+
+ /** Append some data at the end of data buffer and adjust size.
+ * @param data Pointer to the data to add
+ * @param size Size in bytes of new data
+ */
+ void addData(const void *data, size_t size);
+
+ size_t getSize(void);
+
+ /** Changes size of the data buffer size. Keeps data intact unless
+ * truncated.
+ */
+ void setSize(size_t newSize);
+
+ /** Returns the UDP address of the packet.
+ */
+ InetAddress getAddress(void);
+
+ private:
+ std::vector<uint8_t> m_buffer;
+ InetAddress address;
};
#endif // _UDPSOCKET
-
diff --git a/src/dabInputBridgeUdp.cpp b/src/dabInputBridgeUdp.cpp
index 95ba487..fdf3d1f 100644
--- a/src/dabInputBridgeUdp.cpp
+++ b/src/dabInputBridgeUdp.cpp
@@ -76,18 +76,18 @@ int dabInputBridgeUdpRead(dabInputOperations* ops, void* args, void* buffer, int
stats->frameRecords[stats->frameCount].curSize = 0;
stats->frameRecords[stats->frameCount].maxSize = size;
- if (input->udpData->packet->getLength() == 0) {
+ if (input->udpData->packet->getSize() == 0) {
input->udpData->socket->receive(*input->udpData->packet);
}
while ((nbBytes = writePacket(input->udpData->packet->getData(),
- input->udpData->packet->getLength(), buffer, size,
+ input->udpData->packet->getSize(), buffer, size,
input->info))
!= 0) {
stats->frameRecords[stats->frameCount].curSize = nbBytes;
input->udpData->socket->receive(*input->udpData->packet);
}
- if (input->udpData->packet->getLength() != 0) {
+ if (input->udpData->packet->getSize() != 0) {
stats->frameRecords[stats->frameCount].curSize = size;
}
diff --git a/src/dabInputDmbFile.cpp b/src/dabInputDmbFile.cpp
index 9262e6c..423d644 100644
--- a/src/dabInputDmbFile.cpp
+++ b/src/dabInputDmbFile.cpp
@@ -27,6 +27,7 @@
#include <string.h>
#include <stdio.h>
+#ifdef HAVE_FORMAT_DMB
struct dabInputDmbFileData {
FILE* file;
@@ -62,7 +63,6 @@ int dabInputDmbFileInit(void** args)
input->dmb = new Dmb();
*args = input;
- UdpSocket::init();
return 0;
}
@@ -159,3 +159,4 @@ int dabInputDmbFileClean(void** args)
}
+#endif //HAVE_FORMAT_DMB
diff --git a/src/dabInputDmbUdp.cpp b/src/dabInputDmbUdp.cpp
index 3609de2..bd1bde7 100644
--- a/src/dabInputDmbUdp.cpp
+++ b/src/dabInputDmbUdp.cpp
@@ -65,7 +65,6 @@ int dabInputDmbUdpInit(void** args)
input->dmb = new Dmb();
*args = input;
- UdpSocket::init();
return 0;
}
diff --git a/src/dabInputSlip.cpp b/src/dabInputSlip.cpp
deleted file mode 100644
index 0063cca..0000000
--- a/src/dabInputSlip.cpp
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications
- Research Center Canada)
- */
-/*
- 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 "dabInputSlip.h"
-#include "dabInputFifo.h"
-#include "TcpServer.h"
-#include "UdpSocket.h"
-#include "bridge.h"
-
-#include <signal.h>
-#include <string.h>
-#include <limits.h>
-
-#ifdef HAVE_FORMAT_BRIDGE
-# ifdef HAVE_INPUT_SLIP
-
-#ifdef _WIN32
-# include <io.h>
-
-# define sem_t HANDLE
-# define O_NONBLOCK 0
-#else
-# include <semaphore.h>
-# define O_BINARY 0
-#endif
-
-
-struct dabInputSlipData {
- TcpServer* server;
- UdpPacket** packets;
- UdpPacket* buffer;
- bridgeInfo* info;
- dabInputFifoStats stats;
- pthread_t thread;
- sem_t semWrite;
- sem_t semQueue;
- bool reading;
- volatile int nbPackets;
- volatile int queueSize;
- volatile int packetSize;
-};
-
-
-struct dabInputOperations dabInputSlipOperations = {
- dabInputSlipInit,
- dabInputSlipOpen,
- dabInputSlipSetbuf,
- dabInputSlipRead,
- NULL,
- NULL,
- dabInputSlipReadFrame,
- dabInputSetbitrate,
- dabInputSlipClose,
- dabInputSlipClean,
- NULL
-};
-
-
-int dabInputSlipInit(void** args)
-{
- dabInputSlipData* data = new dabInputSlipData;
- memset(&data->stats, 0, sizeof(data->stats));
- data->stats.id = dabInputFifoData::nb++;
- data->server = new TcpServer();
- data->packetSize = 1500;
- data->queueSize = 10;
- data->packets = new UdpPacket*[data->queueSize];
- for (int i = 0; i < data->queueSize; ++i) {
- data->packets[i] = new UdpPacket(data->packetSize);
- }
- data->buffer = new UdpPacket(data->packetSize);
- data->nbPackets = 0;
- data->info = new bridgeInfo;
- data->thread = (pthread_t)NULL;
- bridgeInitInfo(data->info);
-
-#ifdef _WIN32
- char semName[32];
- sprintf(semName, "semWrite%i", data->stats.id);
- data->semWrite = CreateSemaphore(NULL, 1, 1, semName);
- if (data->semWrite == NULL) {
- fprintf(stderr, "Can't init SLIP data write semaphore %s\n", semName);
- return -1;
- }
- sprintf(semName, "semQueue%i", data->stats.id);
- data->semQueue = CreateSemaphore(NULL, 1, 1, semName);
- if (data->semQueue == NULL) {
- fprintf(stderr, "Can't init SLIP data index semaphore %s\n", semName);
- return -1;
- }
-#else
- if (sem_init(&data->semWrite, 0, data->queueSize) == -1) {
- perror("Can't init SLIP data write semaphore");
- return -1;
- }
- if (sem_init(&data->semQueue, 0, 1) == -1) {
- perror("Can't init SLIP data index semaphore");
- return -1;
- }
-#endif
- data->reading = false;
-
- *args = data;
- return 0;
-}
-
-
-void* dabInputSlipThread(void* args)
-{
- dabInputSlipData* data = (dabInputSlipData*)args;
- TcpSocket* client;
-
- while ((client = data->server->accept()) != NULL) {
- int size = 0;
- etiLog.log(info, "SLIP server got a new client.\n");
-
-#ifdef _WIN32
- WaitForSingleObject(data->semWrite, INFINITE);
- WaitForSingleObject(data->semQueue, INFINITE);
-#else
- sem_wait(&data->semWrite);
- sem_wait(&data->semQueue);
-#endif
- UdpPacket* packet = data->packets[data->nbPackets];
-#ifdef _WIN32
- ReleaseSemaphore(data->semQueue, 1, NULL);
-#else
- sem_post(&data->semQueue);
-#endif
-
- while ((size = client->read(packet->getData(), packet->getSize()))
- > 0) {
- packet->setLength(size);
-#ifdef _WIN32
- WaitForSingleObject(data->semQueue, INFINITE);
-#else
- sem_wait(&data->semQueue);
-#endif
- data->nbPackets++;
-#ifdef _WIN32
- ReleaseSemaphore(data->semQueue, 1, NULL);
-#else
- sem_post(&data->semQueue);
-#endif
-
-#ifdef _WIN32
- WaitForSingleObject(data->semWrite, INFINITE);
- WaitForSingleObject(data->semQueue, INFINITE);
-#else
- sem_wait(&data->semWrite);
- sem_wait(&data->semQueue);
-#endif
- packet = data->packets[data->nbPackets];
-#ifdef _WIN32
- ReleaseSemaphore(data->semQueue, 1, NULL);
-#else
- sem_post(&data->semQueue);
-#endif
- }
- etiLog.log(info, "SLIP server client deconnected.\n");
- client->close();
- }
- etiLog.log(error, "SLIP thread can't accept new client (%s)\n",
- inetErrDesc, inetErrMsg);
-
- return NULL;
-}
-
-
-int dabInputSlipOpen(void* args, const char* inputName)
-{
- const char* address;
- long port;
- address = strchr(inputName, ':');
- if (address == NULL) {
- etiLog.log(error, "\"%s\" SLIP address format is invalid: "
- "should be [address]:port - > aborting\n", inputName);
- return -1;
- }
- ++address;
- port = strtol(address, (char **)NULL, 10);
- if ((port == LONG_MIN) || (port == LONG_MAX)) {
- etiLog.log(error, "can't convert port number in SLIP address %s\n",
- address);
- return -1;
- }
- if (port == 0) {
- etiLog.log(error, "can't use port number 0 in SLIP address\n");
- return -1;
- }
- dabInputSlipData* data = (dabInputSlipData*)args;
- if (data->server->create(port) == -1) {
- etiLog.log(error, "can't set port %i on SLIP input (%s: %s)\n",
- port, inetErrDesc, inetErrMsg);
- return -1;
- }
-
- if (data->server->listen() == -1) {
- etiLog.log(error, "can't listen on SLIP socket(%s: %s)\n",
- inetErrDesc, inetErrMsg);
- return -1;
- }
-#ifdef _WIN32
- data->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)dabInputSlipThread, data, 0, NULL);
- if (data->thread == NULL) {
- fprintf(stderr, "Can't create SLIP child");
- return -1;
- }
-#else
- if (pthread_create(&data->thread, NULL, dabInputSlipThread, data)) {
- perror("Can't create SLIP child");
- return -1;
- }
-#endif
-
- etiLog.log(debug, "check return code of create\n");
- return 0;
-}
-
-
-int dabInputSlipSetbuf(void* args, int size)
-{
- dabInputSlipData* data = (dabInputSlipData*)args;
-
- if (size <= 10) {
- return -1;
- }
-
- data->packetSize = size - 10;
- if (data->packets == NULL) {
- data->packets = new UdpPacket*[data->queueSize];
- }
- for (int i = 0; i < data->queueSize; ++i) {
- if (data->packets[i] == NULL) {
- data->packets[i] = new UdpPacket(data->packetSize);
- } else {
- data->packets[i]->setSize(data->packetSize);
- }
- }
- if (data->buffer == NULL) {
- data->buffer = new UdpPacket(data->packetSize);
- } else {
- data->buffer->setSize(data->packetSize);
- }
-
- return 0;
-}
-
-
-int dabInputSlipRead(void* args, void* buffer, int size)
-{
- dabInputSlipData* data = (dabInputSlipData*)args;
-
- if (data->nbPackets > 0) { // data ready
- UdpPacket* temp;
- temp = data->buffer;
- data->buffer = data->packets[0];
-
-#ifdef _WIN32
- WaitForSingleObject(data->semQueue, INFINITE);
-#else
- sem_wait(&data->semQueue);
-#endif
- for (int i = 1; i < data->queueSize; ++i) {
- data->packets[i - 1] = data->packets[i];
- }
- data->packets[data->queueSize - 1] = temp;
- --data->nbPackets;
-#ifdef _WIN32
- ReleaseSemaphore(data->semQueue, 1, NULL);
- ReleaseSemaphore(data->semWrite, 1, NULL);
-#else
- sem_post(&data->semQueue);
- sem_post(&data->semWrite);
-#endif
- } else {
- data->buffer->setLength(0);
- }
-
- return data->buffer->getLength();
-}
-
-
-int dabInputSlipReadFrame(dabInputOperations* ops, void* args, void* buffer, int size)
-{
- int nbBytes = 0;
- dabInputSlipData* data = (dabInputSlipData*)args;
- dabInputFifoStats* stats = (dabInputFifoStats*)&data->stats;
-
-#ifdef _WIN32
- WaitForSingleObject(data->semQueue, INFINITE);
-#else
- sem_wait(&data->semQueue);
-#endif
- stats->bufferRecords[stats->bufferCount].curSize = data->nbPackets;
- stats->bufferRecords[stats->bufferCount].maxSize = data->queueSize;
-#ifdef _WIN32
- ReleaseSemaphore(data->semQueue, 1, NULL);
-#else
- sem_post(&data->semQueue);
-#endif
- if (++stats->bufferCount == NB_RECORDS) {
- etiLog.log(info, "SLIP buffer state: (%i)", stats->id);
- for (int i = 0; i < stats->bufferCount; ++i) {
- etiLog.log(info, " %i/%i",
- stats->bufferRecords[i].curSize,
- stats->bufferRecords[i].maxSize);
- }
- etiLog.log(info, "\n");
-
- stats->bufferCount = 0;
- }
-
- data->stats.frameRecords[data->stats.frameCount].curSize = 0;
- data->stats.frameRecords[data->stats.frameCount].maxSize = size;
-
- if (data->buffer->getLength() == 0) {
- ops->read(args, NULL, 0);
- }
- while ((nbBytes = writePacket(data->buffer->getData(),
- data->buffer->getLength(), buffer, size, data->info))
- != 0) {
- data->stats.frameRecords[data->stats.frameCount].curSize = nbBytes;
- ops->read(args, NULL, 0);
- }
-
- if (data->buffer->getLength() != 0) {
- data->stats.frameRecords[data->stats.frameCount].curSize = size;
- }
-
- if (++stats->frameCount == NB_RECORDS) {
- etiLog.log(info, "Data subchannel usage: (%i)",
- stats->id);
- for (int i = 0; i < stats->frameCount; ++i) {
- etiLog.log(info, " %i/%i",
- stats->frameRecords[i].curSize,
- stats->frameRecords[i].maxSize);
- }
- etiLog.log(info, "\n");
- stats->frameCount = 0;
- }
- return size;
-}
-
-
-int dabInputSlipClose(void* args)
-{
- dabInputSlipData* data = (dabInputSlipData*)args;
- data->server->close();
-#ifdef WIN32
- DWORD status;
- for (int i = 0; i < 5; ++i) {
- if (GetExitCodeThread(data->thread, &status)) {
- break;
- }
- Sleep(100);
- }
- TerminateThread(data->thread, 1);
-#else
- if (data->thread != (pthread_t)NULL) {
- pthread_kill(data->thread, SIGPIPE);
- }
-#endif
- return 0;
-}
-
-
-int dabInputSlipClean(void** args)
-{
- dabInputSlipData* data = (dabInputSlipData*)(*args);
-#ifdef _WIN32
- CloseHandle(data->thread);
- CloseHandle(data->semWrite);
- CloseHandle(data->semQueue);
-#else
- sem_destroy(&data->semWrite);
- sem_destroy(&data->semQueue);
-#endif
- for (int i = 0; i < data->queueSize; ++i) {
- if (data->packets[i] != NULL) {
- delete data->packets[i];
- }
- }
- delete []data->packets;
- delete data->buffer;
- delete data->server;
- delete data->info;
- delete data;
- return 0;
-}
-
-# endif // HAVE_INPUT_SLIP
-#endif // HAVE_FORMAT_BRIDGE
-
diff --git a/src/dabInputSlip.h b/src/dabInputSlip.h
deleted file mode 100644
index 2b8a782..0000000
--- a/src/dabInputSlip.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications
- Research Center Canada)
- */
-/*
- 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/>.
- */
-
-#ifndef DAB_INPUT_SLIP_H
-#define DAB_INPUT_SLIP_H
-
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#include "dabInput.h"
-
-
-#ifdef HAVE_FORMAT_BRIDGE
-# ifdef HAVE_INPUT_SLIP
-
-
-extern struct dabInputOperations dabInputSlipOperations;
-
-int dabInputSlipInit(void** args);
-void* dabInputSlipThread(void* args);
-int dabInputSlipOpen(void* args, const char* inputName);
-int dabInputSlipSetbuf(void* args, int size);
-int dabInputSlipRead(void* args, void* buffer, int size);
-int dabInputSlipReadFrame(dabInputOperations* ops, void* args, void* buffer, int size);
-int dabInputSlipClose(void* args);
-int dabInputSlipClean(void** args);
-
-
-# endif
-#endif
-
-
-#endif // DAB_INPUT_SLIP_H
diff --git a/src/dabOutput/dabOutput.h b/src/dabOutput/dabOutput.h
index 9032297..b911880 100644
--- a/src/dabOutput/dabOutput.h
+++ b/src/dabOutput/dabOutput.h
@@ -2,13 +2,13 @@
Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the Queen in
Right of Canada (Communications Research Center Canada)
- Copyright (C) 2016 Matthias P. Braendli
- http://mpb.li
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
- http://opendigitalradio.org
+ http://www.opendigitalradio.org
An object-oriented version of the output channels.
- */
+*/
/*
This file is part of ODR-DabMux.
@@ -30,7 +30,6 @@
#define __DAB_OUTPUT_H
#include "UdpSocket.h"
-#include "TcpServer.h"
#include "Log.h"
#include "string.h"
#include <stdexcept>
@@ -39,20 +38,11 @@
#include <chrono>
#include <memory>
-#ifdef _WIN32
-# include <io.h>
-# ifdef __MINGW32__
-# define FS_DECLARE_CFG_ARRAYS
-# include <winioctl.h>
-# endif
-# include <sdci.h>
-#else
-# include <unistd.h>
-# include <sys/time.h>
-# ifndef O_BINARY
-# define O_BINARY 0
-# endif // O_BINARY
-#endif
+#include <unistd.h>
+#include <sys/time.h>
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif // O_BINARY
#ifdef HAVE_OUTPUT_ZEROMQ
# include "zmq.hpp"
#endif
@@ -169,21 +159,15 @@ class DabOutputRaw : public DabOutput
public:
DabOutputRaw()
{
-#ifdef _WIN32
- socket_ = INVALID_HANDLE_VALUE;
-#else
socket_ = -1;
isCyclades_ = false;
-#endif
buffer_ = new unsigned char[6144];
}
DabOutputRaw(const DabOutputRaw& other)
{
socket_ = other.socket_;
-#ifndef _WIN32
isCyclades_ = other.isCyclades_;
-#endif
buffer_ = new unsigned char[6144];
memcpy(buffer_, other.buffer_, 6144);
}
@@ -203,12 +187,8 @@ class DabOutputRaw : public DabOutput
}
private:
std::string filename_;
-#ifdef _WIN32
- HANDLE socket_;
-#else
int socket_;
bool isCyclades_;
-#endif
unsigned char* buffer_;
};
@@ -217,17 +197,10 @@ class DabOutputUdp : public DabOutput
{
public:
DabOutputUdp() {
- UdpSocket::init();
packet_ = new UdpPacket(6144);
socket_ = new UdpSocket();
}
- // make sure we don't copy this output around
- // the UdpPacket and UdpSocket do not support
- // copying either
- DabOutputUdp(const DabOutputUdp& other);
- DabOutputUdp operator=(const DabOutputUdp& other);
-
~DabOutputUdp() {
delete socket_;
delete packet_;
@@ -241,34 +214,26 @@ class DabOutputUdp : public DabOutput
return "udp://" + uri_;
}
private:
+ // make sure we don't copy this output around
+ // the UdpPacket and UdpSocket do not support
+ // copying either
+ DabOutputUdp(const DabOutputUdp& other) = delete;
+ DabOutputUdp operator=(const DabOutputUdp& other) = delete;
+
std::string uri_;
UdpSocket* socket_;
UdpPacket* packet_;
};
// -------------- TCP ------------------
+class TCPDataDispatcher;
class DabOutputTcp : public DabOutput
{
public:
- DabOutputTcp()
- {
- TcpSocket::init();
- server = new TcpServer();
- client = NULL;
- }
-
- DabOutputTcp(const DabOutputTcp& other);
- DabOutputTcp operator=(const DabOutputTcp& other);
-
- ~DabOutputTcp() {
-
-#ifdef _WIN32
- CloseHandle(this->thread_);
-#endif
-
- delete this->server;
- delete this->client;
- }
+ DabOutputTcp() {}
+ DabOutputTcp(const DabOutputTcp& other) = delete;
+ const DabOutputTcp& operator=(const DabOutputTcp& other) = delete;
+ ~DabOutputTcp();
int Open(const char* name);
int Write(void* buffer, int size);
@@ -278,11 +243,10 @@ class DabOutputTcp : public DabOutput
return "tcp://" + uri_;
}
- TcpServer* server;
- TcpSocket* client;
private:
std::string uri_;
- pthread_t thread_;
+
+ TCPDataDispatcher* dispatcher_;
};
// -------------- Simul ------------------
@@ -307,11 +271,7 @@ class DabOutputSimul : public DabOutput
}
private:
std::string name_;
-#ifdef _WIN32
- DWORD startTime_;
-#else
std::chrono::steady_clock::time_point startTime_;
-#endif
};
#if defined(HAVE_OUTPUT_ZEROMQ)
diff --git a/src/dabOutput/dabOutputTcp.cpp b/src/dabOutput/dabOutputTcp.cpp
index 5f1943d..9a84937 100644
--- a/src/dabOutput/dabOutputTcp.cpp
+++ b/src/dabOutput/dabOutputTcp.cpp
@@ -2,8 +2,10 @@
Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the Queen in
Right of Canada (Communications Research Center Canada)
- Copyright (C) 2013 Matthias P. Braendli
- http://mpb.li
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
TCP output
*/
@@ -28,48 +30,129 @@
#include <signal.h>
#include <limits.h>
#include "dabOutput.h"
-#include "TcpServer.h"
-
-#ifdef _WIN32
-# include <io.h>
-# ifdef __MINGW32__
-# define FS_DECLARE_CFG_ARRAYS
-# include <winioctl.h>
-# endif
-# include <sdci.h>
-#else
-# include <unistd.h>
-# include <sys/time.h>
-# ifndef O_BINARY
-# define O_BINARY 0
-# endif // O_BINARY
-#endif
-
-void* tcpThread(void* param)
+#include <unistd.h>
+#include <sys/time.h>
+#include <list>
+#include <vector>
+#include <atomic>
+#include <boost/thread.hpp>
+#include "ThreadsafeQueue.h"
+#include "TcpSocket.h"
+
+using namespace std;
+
+using vec_u8 = std::vector<uint8_t>;
+
+// In ETI one element would be an ETI frame of 6144 bytes.
+// 250 frames correspond to 6 seconds. This is mostly here
+// to ensure we do not accumulate data for faulty sockets, delay
+// management has to be done on the receiver end.
+const size_t MAX_QUEUED_ELEMS = 250;
+
+class TCPConnection
{
- TcpSocket* client;
+ public:
+ TCPConnection(TcpSocket&& sock) :
+ queue(),
+ m_running(true),
+ m_sender_thread(),
+ m_sock(move(sock)) {
+ auto addr = m_sock.getRemoteAddress();
+ etiLog.level(debug) << "New TCP Connection from " <<
+ addr.getHostAddress() << ":" << addr.getPort();
+ m_sender_thread = boost::thread(&TCPConnection::process, this, 0);
+ }
+
+ ~TCPConnection() {
+ m_running = false;
+ queue.notify();
+ m_sender_thread.join();
+ }
- DabOutputTcp* tcp = (DabOutputTcp*)param;
+ ThreadsafeQueue<vec_u8> queue;
- while ((client = tcp->server->accept()) != NULL) {
- etiLog.log(info, "TCP server got a new client.\n");
- if (tcp->client != NULL) {
- delete tcp->client;
+ bool is_overloaded(void) const {
+ return queue.size() > MAX_QUEUED_ELEMS;
+ }
+
+ private:
+ TCPConnection(const TCPConnection& other) = delete;
+ TCPConnection& operator=(const TCPConnection& other) = delete;
+
+ atomic<bool> m_running;
+ boost::thread m_sender_thread;
+ TcpSocket m_sock;
+
+ void process(long) {
+ while (m_running) {
+ vec_u8 data;
+ queue.wait_and_pop(data);
+
+ try {
+ m_sock.send(&data[0], data.size());
+ }
+ catch (std::runtime_error& e) {
+ m_running = false;
+ }
+ }
+ }
+};
+
+class TCPDataDispatcher
+{
+ public:
+ ~TCPDataDispatcher() {
+ m_running = false;
+ m_connections.clear();
+ m_listener_socket.close();
+ m_listener_thread.join();
+ }
+
+ void start(int port, const string& address) {
+ TcpSocket sock(port, address);
+ m_listener_socket = move(sock);
+
+ m_running = true;
+ m_listener_thread = boost::thread(&TCPDataDispatcher::process, this, 0);
}
- tcp->client = client;
- }
- etiLog.log(error, "TCP thread can't accept new client (%s)\n",
- inetErrDesc, inetErrMsg);
- return NULL;
+ void Write(const vec_u8& data) {
+ for (auto& connection : m_connections) {
+ connection.queue.push(data);
+ }
+
+ m_connections.remove_if([](TCPConnection& conn){ return conn.is_overloaded(); });
+ }
+
+ private:
+ void process(long) {
+ m_listener_socket.listen();
+
+ while (m_running) {
+ // Add a new TCPConnection to the list, constructing it from the client socket
+ m_connections.emplace(m_connections.begin(), m_listener_socket.accept());
+ }
+ }
+
+ atomic<bool> m_running;
+ boost::thread m_listener_thread;
+ TcpSocket m_listener_socket;
+ std::list<TCPConnection> m_connections;
+};
+
+DabOutputTcp::~DabOutputTcp()
+{
+ if (dispatcher_) {
+ delete dispatcher_;
+ dispatcher_ = nullptr;
+ }
}
-int DabOutputTcp::Open(const char* name)
+static bool parse_uri(const char *uri, long *port, string& addr)
{
- char* hostport = strdup(name); // the name is actually an tuple host:port
+ char* const hostport = strdup(uri); // the uri is actually an tuple host:port
char* address;
- long port;
address = strchr((char*)hostport, ':');
if (address == NULL) {
etiLog.log(error,
@@ -82,98 +165,68 @@ int DabOutputTcp::Open(const char* name)
// terminate string hostport after the host, and advance address to the port number
*(address++) = 0;
- port = strtol(address, (char **)NULL, 10);
- if ((port == LONG_MIN) || (port == LONG_MAX)) {
+ *port = strtol(address, (char **)NULL, 10);
+ if ((*port == LONG_MIN) || (*port == LONG_MAX)) {
etiLog.log(error,
"can't convert port number in tcp address %s\n", address);
goto tcp_open_fail;
}
- if (port == 0) {
+ if (*port == 0) {
etiLog.log(error,
"can't use port number 0 in tcp address\n");
goto tcp_open_fail;
}
- address = hostport;
- if (strlen(address) > 0) {
- if (this->server->create(port, address) == -1) {
- etiLog.log(error, "Can't create Tcp server on %s:%i "
- "(%s: %s) -> aborting\n",
- address, port, inetErrDesc, inetErrMsg);
- goto tcp_open_fail;
- }
- } else {
- if (this->server->create(port) == -1) {
- etiLog.log(error, "Can't create Tcp server on :%i "
- "(%s: %s) -> aborting\n",
- port, inetErrDesc, inetErrMsg);
- goto tcp_open_fail;
- }
- }
+ addr = hostport;
+ free(hostport);
+ return true;
- //sprintf(name, "%s:%i", this->packet_->getAddress().getHostAddress(),
- // this->packet_->getAddress().getPort());
+tcp_open_fail:
+ free(hostport);
+ return false;
+}
- if (this->server->listen() == -1) {
- etiLog.log(error, "Can't listen on Tcp socket (%s: %s)\n",
- inetErrDesc, inetErrMsg);
- goto tcp_open_fail;
- }
-#ifdef _WIN32
- this->thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tcpThread, this, 0, NULL);
- if (this->thread_ == NULL) {
- fprintf(stderr, "Can't create TCP child");
- goto tcp_open_fail;
+int DabOutputTcp::Open(const char* name)
+{
+ long port = 0;
+ string address;
+ bool success = parse_uri(name, &port, address);
+
+ if (success) {
+ dispatcher_ = new TCPDataDispatcher();
+ try {
+ dispatcher_->start(port, address);
+ }
+ catch (std::runtime_error& e) {
+ stringstream ss;
+ ss << "Caught error during socket open of TCP output " << name;
+ throw e;
+ }
}
-#else
- if (pthread_create(&this->thread_, NULL, tcpThread, this)) {
- perror("Can't create TCP child");
- goto tcp_open_fail;
+ else {
+ stringstream ss;
+ ss << "Could not parse TCP output address " << name;
+ throw std::runtime_error(ss.str());
}
-#endif
-
return 0;
-
-tcp_open_fail:
- free(hostport);
- return -1;
}
int DabOutputTcp::Write(void* buffer, int size)
{
+ vec_u8 data(6144);
+ uint8_t* buffer_u8 = (uint8_t*)buffer;
- if (this->client != NULL) {
- if (this->client->write(&size, 2) == 2) {
- if (this->client->write(buffer, size) != size) {
- return size;
- }
- }
- else {
- etiLog.log(info, "TCP server client disconnected.\n");
- delete this->client;
- this->client = NULL;
- }
- }
+ std::copy(buffer_u8, buffer_u8 + size, data.begin());
+
+ // Pad to 6144 bytes
+ std::fill(data.begin() + size, data.end(), 0x55);
+
+ dispatcher_->Write(data);
return size;
}
int DabOutputTcp::Close()
{
- this->server->close();
- if( this->client != NULL )
- this->client->close();
-#ifdef WIN32
- DWORD status;
- for (int i = 0; i < 5; ++i) {
- if (GetExitCodeThread(this->thread_, &status)) {
- break;
- }
- Sleep(100);
- }
- TerminateThread(this->thread_, 1);
-#else
- pthread_kill(this->thread_, SIGPIPE);
-#endif
return 0;
}
diff --git a/src/dabOutput/dabOutputUdp.cpp b/src/dabOutput/dabOutputUdp.cpp
index 433ef8f..9d3ea84 100644
--- a/src/dabOutput/dabOutputUdp.cpp
+++ b/src/dabOutput/dabOutputUdp.cpp
@@ -2,8 +2,10 @@
Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the Queen in
Right of Canada (Communications Research Center Canada)
- Copyright (C) 2013 Matthias P. Braendli
- http://mpb.li
+ Copyright (C) 2016
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ http://www.opendigitalradio.org
UDP output
*/
@@ -79,12 +81,6 @@ int DabOutputUdp::Open(const char* name)
this->packet_->getAddress().setPort(port);
- if (this->socket_->create() == -1) {
- etiLog.level(error) << "can't create UDP socket (" <<
- inetErrDesc << ": " << inetErrMsg << ")";
- return -1;
- }
-
string query_params = what[3];
smatch query_what;
if (regex_match(query_params, query_what, re_query, match_default)) {
@@ -132,7 +128,7 @@ int DabOutputUdp::Open(const char* name)
int DabOutputUdp::Write(void* buffer, int size)
{
- this->packet_->setLength(0);
+ this->packet_->setSize(0);
this->packet_->addData(buffer, size);
return this->socket_->send(*this->packet_);
}
diff --git a/src/fec/LICENSE b/src/fec/LICENSE
new file mode 100644
index 0000000..5a883d3
--- /dev/null
+++ b/src/fec/LICENSE
@@ -0,0 +1,502 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+(This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.)
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ {signature of Ty Coon}, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/src/fec/README.md b/src/fec/README.md
new file mode 100644
index 0000000..a44d28d
--- /dev/null
+++ b/src/fec/README.md
@@ -0,0 +1,12 @@
+FEC routines from KA9Q's libfec
+===============================
+
+This folder contains part of the libfec library by KA9Q. Only the
+char-sized Reed-Solomon encoder and decoder is here.
+
+The files have been copied from the libfec fork at
+https://github.com/Opendigitalradio/ka9q-fec
+
+Original code is at http://www.ka9q.net/code/fec/
+
+All files in this folder are licenced under the LGPL v2.1, please see LICENCE
diff --git a/src/fec/char.h b/src/fec/char.h
new file mode 100644
index 0000000..25efd65
--- /dev/null
+++ b/src/fec/char.h
@@ -0,0 +1,24 @@
+/* Stuff specific to the 8-bit symbol version of the general purpose RS codecs
+ *
+ * Copyright 2003, Phil Karn, KA9Q
+ * May be used under the terms of the GNU Lesser General Public License (LGPL)
+ */
+typedef unsigned char data_t;
+
+#define MODNN(x) modnn(rs,x)
+
+#define MM (rs->mm)
+#define NN (rs->nn)
+#define ALPHA_TO (rs->alpha_to)
+#define INDEX_OF (rs->index_of)
+#define GENPOLY (rs->genpoly)
+#define NROOTS (rs->nroots)
+#define FCR (rs->fcr)
+#define PRIM (rs->prim)
+#define IPRIM (rs->iprim)
+#define PAD (rs->pad)
+#define A0 (NN)
+
+
+
+
diff --git a/src/fec/decode_rs.h b/src/fec/decode_rs.h
new file mode 100644
index 0000000..c165cf3
--- /dev/null
+++ b/src/fec/decode_rs.h
@@ -0,0 +1,298 @@
+/* The guts of the Reed-Solomon decoder, meant to be #included
+ * into a function body with the following typedefs, macros and variables supplied
+ * according to the code parameters:
+
+ * data_t - a typedef for the data symbol
+ * data_t data[] - array of NN data and parity symbols to be corrected in place
+ * retval - an integer lvalue into which the decoder's return code is written
+ * NROOTS - the number of roots in the RS code generator polynomial,
+ * which is the same as the number of parity symbols in a block.
+ Integer variable or literal.
+ * NN - the total number of symbols in a RS block. Integer variable or literal.
+ * PAD - the number of pad symbols in a block. Integer variable or literal.
+ * ALPHA_TO - The address of an array of NN elements to convert Galois field
+ * elements in index (log) form to polynomial form. Read only.
+ * INDEX_OF - The address of an array of NN elements to convert Galois field
+ * elements in polynomial form to index (log) form. Read only.
+ * MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
+ * FCR - An integer literal or variable specifying the first consecutive root of the
+ * Reed-Solomon generator polynomial. Integer variable or literal.
+ * PRIM - The primitive root of the generator poly. Integer variable or literal.
+ * DEBUG - If set to 1 or more, do various internal consistency checking. Leave this
+ * undefined for production code
+
+ * The memset(), memmove(), and memcpy() functions are used. The appropriate header
+ * file declaring these functions (usually <string.h>) must be included by the calling
+ * program.
+ */
+
+
+#if !defined(NROOTS)
+#error "NROOTS not defined"
+#endif
+
+#if !defined(NN)
+#error "NN not defined"
+#endif
+
+#if !defined(PAD)
+#error "PAD not defined"
+#endif
+
+#if !defined(ALPHA_TO)
+#error "ALPHA_TO not defined"
+#endif
+
+#if !defined(INDEX_OF)
+#error "INDEX_OF not defined"
+#endif
+
+#if !defined(MODNN)
+#error "MODNN not defined"
+#endif
+
+#if !defined(FCR)
+#error "FCR not defined"
+#endif
+
+#if !defined(PRIM)
+#error "PRIM not defined"
+#endif
+
+#if !defined(NULL)
+#define NULL ((void *)0)
+#endif
+
+#undef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#undef A0
+#define A0 (NN)
+
+{
+ int deg_lambda, el, deg_omega;
+ int i, j, r,k;
+ data_t u,q,tmp,num1,num2,den,discr_r;
+ data_t lambda[NROOTS+1], s[NROOTS]; /* Err+Eras Locator poly
+ * and syndrome poly */
+ data_t b[NROOTS+1], t[NROOTS+1], omega[NROOTS+1];
+ data_t root[NROOTS], reg[NROOTS+1], loc[NROOTS];
+ int syn_error, count;
+
+ /* form the syndromes; i.e., evaluate data(x) at roots of g(x) */
+ for(i=0;i<NROOTS;i++)
+ s[i] = data[0];
+
+ for(j=1;j<NN-PAD;j++){
+ for(i=0;i<NROOTS;i++){
+ if(s[i] == 0){
+ s[i] = data[j];
+ } else {
+ s[i] = data[j] ^ ALPHA_TO[MODNN(INDEX_OF[s[i]] + (FCR+i)*PRIM)];
+ }
+ }
+ }
+
+ /* Convert syndromes to index form, checking for nonzero condition */
+ syn_error = 0;
+ for(i=0;i<NROOTS;i++){
+ syn_error |= s[i];
+ s[i] = INDEX_OF[s[i]];
+ }
+
+ if (!syn_error) {
+ /* if syndrome is zero, data[] is a codeword and there are no
+ * errors to correct. So return data[] unmodified
+ */
+ count = 0;
+ goto finish;
+ }
+ memset(&lambda[1],0,NROOTS*sizeof(lambda[0]));
+ lambda[0] = 1;
+
+ if (no_eras > 0) {
+ /* Init lambda to be the erasure locator polynomial */
+ lambda[1] = ALPHA_TO[MODNN(PRIM*(NN-1-eras_pos[0]))];
+ for (i = 1; i < no_eras; i++) {
+ u = MODNN(PRIM*(NN-1-eras_pos[i]));
+ for (j = i+1; j > 0; j--) {
+ tmp = INDEX_OF[lambda[j - 1]];
+ if(tmp != A0)
+ lambda[j] ^= ALPHA_TO[MODNN(u + tmp)];
+ }
+ }
+
+#if DEBUG >= 1
+ /* Test code that verifies the erasure locator polynomial just constructed
+ Needed only for decoder debugging. */
+
+ /* find roots of the erasure location polynomial */
+ for(i=1;i<=no_eras;i++)
+ reg[i] = INDEX_OF[lambda[i]];
+
+ count = 0;
+ for (i = 1,k=IPRIM-1; i <= NN; i++,k = MODNN(k+IPRIM)) {
+ q = 1;
+ for (j = 1; j <= no_eras; j++)
+ if (reg[j] != A0) {
+ reg[j] = MODNN(reg[j] + j);
+ q ^= ALPHA_TO[reg[j]];
+ }
+ if (q != 0)
+ continue;
+ /* store root and error location number indices */
+ root[count] = i;
+ loc[count] = k;
+ count++;
+ }
+ if (count != no_eras) {
+ printf("count = %d no_eras = %d\n lambda(x) is WRONG\n",count,no_eras);
+ count = -1;
+ goto finish;
+ }
+#if DEBUG >= 2
+ printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
+ for (i = 0; i < count; i++)
+ printf("%d ", loc[i]);
+ printf("\n");
+#endif
+#endif
+ }
+ for(i=0;i<NROOTS+1;i++)
+ b[i] = INDEX_OF[lambda[i]];
+
+ /*
+ * Begin Berlekamp-Massey algorithm to determine error+erasure
+ * locator polynomial
+ */
+ r = no_eras;
+ el = no_eras;
+ while (++r <= NROOTS) { /* r is the step number */
+ /* Compute discrepancy at the r-th step in poly-form */
+ discr_r = 0;
+ for (i = 0; i < r; i++){
+ if ((lambda[i] != 0) && (s[r-i-1] != A0)) {
+ discr_r ^= ALPHA_TO[MODNN(INDEX_OF[lambda[i]] + s[r-i-1])];
+ }
+ }
+ discr_r = INDEX_OF[discr_r]; /* Index form */
+ if (discr_r == A0) {
+ /* 2 lines below: B(x) <-- x*B(x) */
+ memmove(&b[1],b,NROOTS*sizeof(b[0]));
+ b[0] = A0;
+ } else {
+ /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
+ t[0] = lambda[0];
+ for (i = 0 ; i < NROOTS; i++) {
+ if(b[i] != A0)
+ t[i+1] = lambda[i+1] ^ ALPHA_TO[MODNN(discr_r + b[i])];
+ else
+ t[i+1] = lambda[i+1];
+ }
+ if (2 * el <= r + no_eras - 1) {
+ el = r + no_eras - el;
+ /*
+ * 2 lines below: B(x) <-- inv(discr_r) *
+ * lambda(x)
+ */
+ for (i = 0; i <= NROOTS; i++)
+ b[i] = (lambda[i] == 0) ? A0 : MODNN(INDEX_OF[lambda[i]] - discr_r + NN);
+ } else {
+ /* 2 lines below: B(x) <-- x*B(x) */
+ memmove(&b[1],b,NROOTS*sizeof(b[0]));
+ b[0] = A0;
+ }
+ memcpy(lambda,t,(NROOTS+1)*sizeof(t[0]));
+ }
+ }
+
+ /* Convert lambda to index form and compute deg(lambda(x)) */
+ deg_lambda = 0;
+ for(i=0;i<NROOTS+1;i++){
+ lambda[i] = INDEX_OF[lambda[i]];
+ if(lambda[i] != A0)
+ deg_lambda = i;
+ }
+ /* Find roots of the error+erasure locator polynomial by Chien search */
+ memcpy(&reg[1],&lambda[1],NROOTS*sizeof(reg[0]));
+ count = 0; /* Number of roots of lambda(x) */
+ for (i = 1,k=IPRIM-1; i <= NN; i++,k = MODNN(k+IPRIM)) {
+ q = 1; /* lambda[0] is always 0 */
+ for (j = deg_lambda; j > 0; j--){
+ if (reg[j] != A0) {
+ reg[j] = MODNN(reg[j] + j);
+ q ^= ALPHA_TO[reg[j]];
+ }
+ }
+ if (q != 0)
+ continue; /* Not a root */
+ /* store root (index-form) and error location number */
+#if DEBUG>=2
+ printf("count %d root %d loc %d\n",count,i,k);
+#endif
+ root[count] = i;
+ loc[count] = k;
+ /* If we've already found max possible roots,
+ * abort the search to save time
+ */
+ if(++count == deg_lambda)
+ break;
+ }
+ if (deg_lambda != count) {
+ /*
+ * deg(lambda) unequal to number of roots => uncorrectable
+ * error detected
+ */
+ count = -1;
+ goto finish;
+ }
+ /*
+ * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
+ * x**NROOTS). in index form. Also find deg(omega).
+ */
+ deg_omega = deg_lambda-1;
+ for (i = 0; i <= deg_omega;i++){
+ tmp = 0;
+ for(j=i;j >= 0; j--){
+ if ((s[i - j] != A0) && (lambda[j] != A0))
+ tmp ^= ALPHA_TO[MODNN(s[i - j] + lambda[j])];
+ }
+ omega[i] = INDEX_OF[tmp];
+ }
+
+ /*
+ * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
+ * inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form
+ */
+ for (j = count-1; j >=0; j--) {
+ num1 = 0;
+ for (i = deg_omega; i >= 0; i--) {
+ if (omega[i] != A0)
+ num1 ^= ALPHA_TO[MODNN(omega[i] + i * root[j])];
+ }
+ num2 = ALPHA_TO[MODNN(root[j] * (FCR - 1) + NN)];
+ den = 0;
+
+ /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
+ for (i = MIN(deg_lambda,NROOTS-1) & ~1; i >= 0; i -=2) {
+ if(lambda[i+1] != A0)
+ den ^= ALPHA_TO[MODNN(lambda[i+1] + i * root[j])];
+ }
+#if DEBUG >= 1
+ if (den == 0) {
+ printf("\n ERROR: denominator = 0\n");
+ count = -1;
+ goto finish;
+ }
+#endif
+ /* Apply error to data */
+ if (num1 != 0 && loc[j] >= PAD) {
+ data[loc[j]-PAD] ^= ALPHA_TO[MODNN(INDEX_OF[num1] + INDEX_OF[num2] + NN - INDEX_OF[den])];
+ }
+ }
+ finish:
+ if(eras_pos != NULL){
+ for(i=0;i<count;i++)
+ eras_pos[i] = loc[i];
+ }
+ retval = count;
+}
diff --git a/src/fec/decode_rs_char.c b/src/fec/decode_rs_char.c
new file mode 100644
index 0000000..7105233
--- /dev/null
+++ b/src/fec/decode_rs_char.c
@@ -0,0 +1,22 @@
+/* General purpose Reed-Solomon decoder for 8-bit symbols or less
+ * Copyright 2003 Phil Karn, KA9Q
+ * May be used under the terms of the GNU Lesser General Public License (LGPL)
+ */
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#include <string.h>
+
+#include "char.h"
+#include "rs-common.h"
+
+int decode_rs_char(void *p, data_t *data, int *eras_pos, int no_eras){
+ int retval;
+ struct rs *rs = (struct rs *)p;
+
+#include "decode_rs.h"
+
+ return retval;
+}
diff --git a/src/fec/encode_rs.h b/src/fec/encode_rs.h
new file mode 100644
index 0000000..2c157f9
--- /dev/null
+++ b/src/fec/encode_rs.h
@@ -0,0 +1,58 @@
+/* The guts of the Reed-Solomon encoder, meant to be #included
+ * into a function body with the following typedefs, macros and variables supplied
+ * according to the code parameters:
+
+ * data_t - a typedef for the data symbol
+ * data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
+ * data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
+ * NROOTS - the number of roots in the RS code generator polynomial,
+ * which is the same as the number of parity symbols in a block.
+ Integer variable or literal.
+ *
+ * NN - the total number of symbols in a RS block. Integer variable or literal.
+ * PAD - the number of pad symbols in a block. Integer variable or literal.
+ * ALPHA_TO - The address of an array of NN elements to convert Galois field
+ * elements in index (log) form to polynomial form. Read only.
+ * INDEX_OF - The address of an array of NN elements to convert Galois field
+ * elements in polynomial form to index (log) form. Read only.
+ * MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
+ * GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
+
+ * The memset() and memmove() functions are used. The appropriate header
+ * file declaring these functions (usually <string.h>) must be included by the calling
+ * program.
+
+ * Copyright 2004, Phil Karn, KA9Q
+ * May be used under the terms of the GNU Lesser General Public License (LGPL)
+ */
+
+
+#undef A0
+#define A0 (NN) /* Special reserved value encoding zero in index form */
+
+{
+ int i, j;
+ data_t feedback;
+
+ memset(parity,0,NROOTS*sizeof(data_t));
+
+ for(i=0;i<NN-NROOTS-PAD;i++){
+ feedback = INDEX_OF[data[i] ^ parity[0]];
+ if(feedback != A0){ /* feedback term is non-zero */
+#ifdef UNNORMALIZED
+ /* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
+ * always be for the polynomials constructed by init_rs()
+ */
+ feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
+#endif
+ for(j=1;j<NROOTS;j++)
+ parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
+ }
+ /* Shift */
+ memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
+ if(feedback != A0)
+ parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
+ else
+ parity[NROOTS-1] = 0;
+ }
+}
diff --git a/src/fec/encode_rs_char.c b/src/fec/encode_rs_char.c
new file mode 100644
index 0000000..a9bf2b8
--- /dev/null
+++ b/src/fec/encode_rs_char.c
@@ -0,0 +1,15 @@
+/* Reed-Solomon encoder
+ * Copyright 2002, Phil Karn, KA9Q
+ * May be used under the terms of the GNU Lesser General Public License (LGPL)
+ */
+#include <string.h>
+
+#include "char.h"
+#include "rs-common.h"
+
+void encode_rs_char(void *p,data_t *data, data_t *parity){
+ struct rs *rs = (struct rs *)p;
+
+#include "encode_rs.h"
+
+}
diff --git a/src/fec/fec.h b/src/fec/fec.h
new file mode 100644
index 0000000..0d1bae1
--- /dev/null
+++ b/src/fec/fec.h
@@ -0,0 +1,30 @@
+/* Main header for reduced libfec.
+ *
+ * The FEC code in this folder is
+ * Copyright 2003 Phil Karn, KA9Q
+ * May be used under the terms of the GNU Lesser General Public License (LGPL)
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+#include "char.h"
+#include "rs-common.h"
+
+/* Initialize a Reed-Solomon codec
+ * symsize = symbol size, bits
+ * gfpoly = Field generator polynomial coefficients
+ * fcr = first root of RS code generator polynomial, index form
+ * prim = primitive element to generate polynomial roots
+ * nroots = RS code generator polynomial degree (number of roots)
+ * pad = padding bytes at front of shortened block
+ */
+void *init_rs_char(int symsize,int gfpoly,int fcr,int prim,int nroots,int pad);
+
+int decode_rs_char(void *p, data_t *data, int *eras_pos, int no_eras);
+
+void encode_rs_char(void *p,data_t *data, data_t *parity);
+
+void free_rs_char(void *p);
+
diff --git a/src/fec/init_rs.h b/src/fec/init_rs.h
new file mode 100644
index 0000000..2b2ae98
--- /dev/null
+++ b/src/fec/init_rs.h
@@ -0,0 +1,106 @@
+/* Common code for intializing a Reed-Solomon control block (char or int symbols)
+ * Copyright 2004 Phil Karn, KA9Q
+ * May be used under the terms of the GNU Lesser General Public License (LGPL)
+ */
+#undef NULL
+#define NULL ((void *)0)
+
+{
+ int i, j, sr,root,iprim;
+
+ rs = NULL;
+ /* Check parameter ranges */
+ if(symsize < 0 || symsize > 8*sizeof(data_t)){
+ goto done;
+ }
+
+ if(fcr < 0 || fcr >= (1<<symsize))
+ goto done;
+ if(prim <= 0 || prim >= (1<<symsize))
+ goto done;
+ if(nroots < 0 || nroots >= (1<<symsize))
+ goto done; /* Can't have more roots than symbol values! */
+ if(pad < 0 || pad >= ((1<<symsize) -1 - nroots))
+ goto done; /* Too much padding */
+
+ rs = (struct rs *)calloc(1,sizeof(struct rs));
+ if(rs == NULL)
+ goto done;
+
+ rs->mm = symsize;
+ rs->nn = (1<<symsize)-1;
+ rs->pad = pad;
+
+ rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
+ if(rs->alpha_to == NULL){
+ free(rs);
+ rs = NULL;
+ goto done;
+ }
+ rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
+ if(rs->index_of == NULL){
+ free(rs->alpha_to);
+ free(rs);
+ rs = NULL;
+ goto done;
+ }
+
+ /* Generate Galois field lookup tables */
+ rs->index_of[0] = A0; /* log(zero) = -inf */
+ rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */
+ sr = 1;
+ for(i=0;i<rs->nn;i++){
+ rs->index_of[sr] = i;
+ rs->alpha_to[i] = sr;
+ sr <<= 1;
+ if(sr & (1<<symsize))
+ sr ^= gfpoly;
+ sr &= rs->nn;
+ }
+ if(sr != 1){
+ /* field generator polynomial is not primitive! */
+ free(rs->alpha_to);
+ free(rs->index_of);
+ free(rs);
+ rs = NULL;
+ goto done;
+ }
+
+ /* Form RS code generator polynomial from its roots */
+ rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1));
+ if(rs->genpoly == NULL){
+ free(rs->alpha_to);
+ free(rs->index_of);
+ free(rs);
+ rs = NULL;
+ goto done;
+ }
+ rs->fcr = fcr;
+ rs->prim = prim;
+ rs->nroots = nroots;
+
+ /* Find prim-th root of 1, used in decoding */
+ for(iprim=1;(iprim % prim) != 0;iprim += rs->nn)
+ ;
+ rs->iprim = iprim / prim;
+
+ rs->genpoly[0] = 1;
+ for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) {
+ rs->genpoly[i+1] = 1;
+
+ /* Multiply rs->genpoly[] by @**(root + x) */
+ for (j = i; j > 0; j--){
+ if (rs->genpoly[j] != 0)
+ rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)];
+ else
+ rs->genpoly[j] = rs->genpoly[j-1];
+ }
+ /* rs->genpoly[0] can never be zero */
+ rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)];
+ }
+ /* convert rs->genpoly[] to index form for quicker encoding */
+ for (i = 0; i <= nroots; i++)
+ rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
+ done:;
+
+}
diff --git a/src/fec/init_rs_char.c b/src/fec/init_rs_char.c
new file mode 100644
index 0000000..a51099a
--- /dev/null
+++ b/src/fec/init_rs_char.c
@@ -0,0 +1,35 @@
+/* Initialize a RS codec
+ *
+ * Copyright 2002 Phil Karn, KA9Q
+ * May be used under the terms of the GNU Lesser General Public License (LGPL)
+ */
+#include <stdlib.h>
+
+#include "char.h"
+#include "rs-common.h"
+
+void free_rs_char(void *p){
+ struct rs *rs = (struct rs *)p;
+
+ free(rs->alpha_to);
+ free(rs->index_of);
+ free(rs->genpoly);
+ free(rs);
+}
+
+/* Initialize a Reed-Solomon codec
+ * symsize = symbol size, bits
+ * gfpoly = Field generator polynomial coefficients
+ * fcr = first root of RS code generator polynomial, index form
+ * prim = primitive element to generate polynomial roots
+ * nroots = RS code generator polynomial degree (number of roots)
+ * pad = padding bytes at front of shortened block
+ */
+void *init_rs_char(int symsize,int gfpoly,int fcr,int prim,
+ int nroots,int pad){
+ struct rs *rs;
+
+#include "init_rs.h"
+
+ return rs;
+}
diff --git a/src/fec/rs-common.h b/src/fec/rs-common.h
new file mode 100644
index 0000000..e64eb39
--- /dev/null
+++ b/src/fec/rs-common.h
@@ -0,0 +1,26 @@
+/* Stuff common to all the general-purpose Reed-Solomon codecs
+ * Copyright 2004 Phil Karn, KA9Q
+ * May be used under the terms of the GNU Lesser General Public License (LGPL)
+ */
+
+/* Reed-Solomon codec control block */
+struct rs {
+ int mm; /* Bits per symbol */
+ int nn; /* Symbols per block (= (1<<mm)-1) */
+ data_t *alpha_to; /* log lookup table */
+ data_t *index_of; /* Antilog lookup table */
+ data_t *genpoly; /* Generator polynomial */
+ int nroots; /* Number of generator roots = number of parity symbols */
+ int fcr; /* First consecutive root, index form */
+ int prim; /* Primitive element, index form */
+ int iprim; /* prim-th root of 1, index form */
+ int pad; /* Padding bytes in shortened block */
+};
+
+static inline int modnn(struct rs *rs,int x){
+ while (x >= rs->nn) {
+ x -= rs->nn;
+ x = (x >> rs->mm) + (x & rs->nn);
+ }
+ return x;
+}
diff --git a/src/fig/FIG0.cpp b/src/fig/FIG0.cpp
index 8393c72..4ac4e5e 100644
--- a/src/fig/FIG0.cpp
+++ b/src/fig/FIG0.cpp
@@ -950,7 +950,7 @@ FillStatus FIG0_13::fill(uint8_t *buf, size_t max_size)
(*subchannel)->type == subchannel_type_t::Packet &&
(*componentFIG0_13)->packet.appType != 0xffff) {
- const int required_size = 5+2;
+ const int required_size = 5+2+2;
if (fig0 == NULL) {
if (remaining < 2 + required_size) {
@@ -980,10 +980,19 @@ FillStatus FIG0_13::fill(uint8_t *buf, size_t max_size)
FIG0_13_app* app = (FIG0_13_app*)buf;
app->setType((*componentFIG0_13)->packet.appType);
- app->length = 0;
- buf += 2;
- remaining -= 2;
- fig0->Length += 2;
+ 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;
+ }
+ buf += 2 + app->length;
+ remaining -= 2 + app->length;
+ fig0->Length += 2 + app->length;
}
}
diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h
index 59ed1af..3a0113b 100644
--- a/src/fig/FIG0.h
+++ b/src/fig/FIG0.h
@@ -196,7 +196,7 @@ class FIG0_17 : public IFIG
public:
FIG0_17(FIGRuntimeInformation* rti);
virtual FillStatus fill(uint8_t *buf, size_t max_size);
- virtual FIG_rate repetition_rate(void) { return FIG_rate::A_B; }
+ 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 17; }
diff --git a/src/utils.cpp b/src/utils.cpp
index 7a20c43..98ec22d 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -96,7 +96,7 @@ void header_message()
fprintf(stderr,
"(Communications Research Centre Canada) All rights reserved.\n\n");
fprintf(stderr,
- "Copyright (C) 2013, 2014, 2015 Matthias P. Braendli\n");
+ "Copyright (C) 2016 Matthias P. Braendli\n");
fprintf(stderr,
"http://opendigitalradio.org\n\n");
@@ -107,9 +107,6 @@ void header_message()
#if defined(HAVE_INPUT_TEST)
" test" <<
#endif
-#if defined(HAVE_INPUT_SLIP)
- " slip" <<
-#endif
#if defined(HAVE_INPUT_UDP)
" udp" <<
#endif