summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
38 files changed, 1979 insertions, 2019 deletions
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