mirror of
https://github.com/JvanKatwijk/dab-cmdline
synced 2025-10-05 23:52:50 +02:00
added second impl of RS error handling in data packets
This commit is contained in:
@@ -57,6 +57,7 @@ motdata is passed on as uint8_t array, the size of the data is passed,
|
||||
the name of the slides - as derived from the DAB data - is passed on.
|
||||
See the dab-api for details
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
Disclaimer
|
||||
-----------------------------------------------------------------------
|
||||
@@ -153,6 +154,8 @@ example 2 is the basic one, others are derived.
|
||||
It contains a simple "keyboard listener", that will react
|
||||
on entering a stroke on the return key. It will cause the
|
||||
"next" (audio) service to be selected.
|
||||
In example 5 there is support for tdc packet handling,
|
||||
and - thanks to stefan Juhl - support for FEC protected packets.
|
||||
|
||||
- example 6 is an experimental version where stdin is
|
||||
used as input device (and the command line parameters are
|
||||
@@ -162,6 +165,10 @@ example 2 is the basic one, others are derived.
|
||||
python3.9 (it should work with other versions as well, adapt the
|
||||
CMakeLists.tct file)
|
||||
|
||||
- the scanner example is what the name suggests, it scans the band
|
||||
and shows the content of the channels that carry (detectable)
|
||||
DAB data
|
||||
|
||||
For all examples it holds that NO garantee is
|
||||
given on their functioning, feel free to improve.
|
||||
|
||||
@@ -175,7 +182,7 @@ and collects and emits data about the ensembles and services encountered.
|
||||
Output can be sent to a file - ASCII - that can be interpreted
|
||||
by Libre Office Calc or similar programs.
|
||||
|
||||
The dab-scanner supports rtlsdr, sdrplay, airspy, hackrf, and lime sdr.
|
||||
The dab-scanner supports rtlsdr, sdrplay (with support for the 2.13 lib and the 3.XX libraries), airspy, hackrf, and lime sdr.
|
||||
|
||||

|
||||
|
||||
|
@@ -53,14 +53,14 @@
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
void printOptions (void); // forward declaration
|
||||
void printOptions (); // forward declaration
|
||||
// we deal with some callbacks, so we have some data that needs
|
||||
// to be accessed from global contexts
|
||||
static
|
||||
std::atomic<bool> run;
|
||||
|
||||
static
|
||||
void *theRadio = NULL;
|
||||
void *theRadio = nullptr;
|
||||
|
||||
static
|
||||
std::atomic<bool>timeSynced;
|
||||
@@ -140,7 +140,7 @@ static
|
||||
void bytesOut_Handler (uint8_t *data, int16_t amount,
|
||||
uint8_t type, void *ctx) {
|
||||
(void)data;
|
||||
(void)amount;
|
||||
(void)amount; (void)type;
|
||||
(void)ctx;
|
||||
}
|
||||
//
|
||||
@@ -235,7 +235,7 @@ bool firstEnsemble = true;
|
||||
switch (opt) {
|
||||
case 'F':
|
||||
outFile = fopen (optarg, "w");
|
||||
if (outFile == NULL)
|
||||
if (outFile == nullptr)
|
||||
outFile = stderr;
|
||||
break;
|
||||
|
||||
@@ -437,7 +437,7 @@ bool firstEnsemble = true;
|
||||
nullptr, // no constellations
|
||||
nullptr
|
||||
);
|
||||
if (theRadio == NULL) {
|
||||
if (theRadio == nullptr) {
|
||||
fprintf (stderr, "sorry, no radio available, fatal\n");
|
||||
exit (4);
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ class OpeningFileFailed : public std::exception {
|
||||
char message[MAX_MESSAGE_SIZE];
|
||||
|
||||
public:
|
||||
OpeningFileFailed(char * file ,char * error) {
|
||||
OpeningFileFailed (const char * file, char * error) {
|
||||
snprintf(this->message, MAX_MESSAGE_SIZE, "Unable to open %s because: %s", file, error);
|
||||
};
|
||||
|
||||
|
@@ -35,7 +35,7 @@
|
||||
#include "device-exceptions.h"
|
||||
|
||||
static inline
|
||||
int64_t getMyTime (void) {
|
||||
int64_t getMyTime () {
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday (&tv, NULL);
|
||||
@@ -53,7 +53,7 @@ struct timeval tv;
|
||||
filePointer = fopen (f. c_str (), "rb");
|
||||
if (filePointer == NULL) {
|
||||
delete _I_Buffer;
|
||||
throw OpeningFileFailed(f.c_str(),strerror(errno));
|
||||
throw OpeningFileFailed (f.c_str(),strerror(errno));
|
||||
}
|
||||
|
||||
this -> eofHandler = nullptr;
|
||||
@@ -80,11 +80,12 @@ struct timeval tv;
|
||||
running. store (false);
|
||||
}
|
||||
|
||||
rawFiles::~rawFiles (void) {
|
||||
if (running. load ())
|
||||
rawFiles::~rawFiles () {
|
||||
if (running. load ()) {
|
||||
running. store (false);
|
||||
workerHandle. join ();
|
||||
running. store (false);
|
||||
fclose (filePointer);
|
||||
fclose (filePointer);
|
||||
}
|
||||
delete _I_Buffer;
|
||||
}
|
||||
|
||||
@@ -95,10 +96,11 @@ bool rawFiles::restartReader (int32_t frequency) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void rawFiles::stopReader (void) {
|
||||
if (running. load ())
|
||||
void rawFiles::stopReader () {
|
||||
if (running. load ()) {
|
||||
running. store (false);
|
||||
workerHandle. join ();
|
||||
running. store (false);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t rawFiles::getSamples (std::complex<float> *V, int32_t size) {
|
||||
@@ -120,7 +122,7 @@ int32_t rawFiles::Samples (void) {
|
||||
//
|
||||
// The actual interface to the filereader is in a separate thread
|
||||
//
|
||||
void rawFiles::run (void) {
|
||||
void rawFiles::run () {
|
||||
int32_t t, i;
|
||||
std::complex<float> *bi;
|
||||
int32_t bufferSize = 32768;
|
||||
|
@@ -365,6 +365,6 @@ closeAPI:
|
||||
void sdrplayHandler_v3::setup_xmlDump () {
|
||||
}
|
||||
|
||||
void sdrplayHandler_v3::close_mlDump () {
|
||||
void sdrplayHandler_v3::close_xmlDump () {
|
||||
}
|
||||
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <time.h>
|
||||
#include <cstring>
|
||||
#include "wavfiles.h"
|
||||
#include "device-exceptions.h"
|
||||
|
||||
static inline
|
||||
int64_t getMyTime (void) {
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <ctime>
|
||||
|
||||
#include "device-exceptions.h"
|
||||
#include "xml-descriptor.h"
|
||||
#include "xml-reader.h"
|
||||
|
||||
|
@@ -171,7 +171,7 @@ void motdata_Handler (uint8_t *data, int size,
|
||||
}
|
||||
|
||||
void tii_data_Handler (int s, void *p) {
|
||||
fprintf (stderr, "mainId %d, subId %d\n", s >> 8, s & 0xFF);
|
||||
// fprintf (stderr, "mainId %d, subId %d\n", s >> 8, s & 0xFF);
|
||||
(void)p;
|
||||
}
|
||||
|
||||
|
@@ -45,6 +45,11 @@ if(DEFINED SDRPLAY)
|
||||
set(objectName dab-sdrplay-5)
|
||||
endif ()
|
||||
|
||||
if(DEFINED SDRPLAY_V3)
|
||||
set(SDRPLAY_V3 true)
|
||||
set(objectName dab-sdrplay-5)
|
||||
endif ()
|
||||
|
||||
if(DEFINED RTLSDR)
|
||||
set(RTLSDR true)
|
||||
set(objectName dab-rtlsdr-5)
|
||||
@@ -65,6 +70,11 @@ if(DEFINED RTL_TCP)
|
||||
set(objectName dab-rtl_tcp-5)
|
||||
endif ()
|
||||
|
||||
if(XMLFILES)
|
||||
set(XMLFILES true)
|
||||
set (objectName dab-xml-5)
|
||||
endif ()
|
||||
|
||||
if (DEFINED SERVER)
|
||||
add_definitions(-DHAVE_SERVER)
|
||||
endif ()
|
||||
@@ -172,6 +182,38 @@ endif ()
|
||||
add_definitions (-DHAVE_SDRPLAY)
|
||||
endif (SDRPLAY)
|
||||
|
||||
if (SDRPLAY_V3)
|
||||
find_path (SDRPLAYLIB_INCLUDE_DIR
|
||||
NAMES sdrplay_api.h
|
||||
PATHS
|
||||
/usr/local/include/
|
||||
)
|
||||
include_directories (${SDRPLAYLIB_INCLUDE_DIR})
|
||||
|
||||
find_library (SDRPLAYLIB sdrplay_api)
|
||||
if(NOT(SDRPLAYLIB))
|
||||
message(FATAL_ERROR "please install -lsdrplay_api")
|
||||
else(NOT(SDRPLAYLIB))
|
||||
list (APPEND extraLibs ${SDRPLAYLIB})
|
||||
endif(NOT(SDRPLAYLIB))
|
||||
|
||||
include_directories (
|
||||
../devices/sdrplay-handler-v3
|
||||
)
|
||||
|
||||
set ($(objectName)_HDRS
|
||||
${${objectName}_HDRS}
|
||||
../devices/sdrplay-handler-v3/sdrplay-handler-v3.h
|
||||
)
|
||||
|
||||
set (${objectName}_SRCS
|
||||
${${objectName}_SRCS}
|
||||
../devices/sdrplay-handler-v3/sdrplay-handler-v3.cpp
|
||||
)
|
||||
|
||||
add_definitions (-DHAVE_SDRPLAY_V3)
|
||||
endif (SDRPLAY_V3)
|
||||
|
||||
if (AIRSPY)
|
||||
find_package(LibAIRSPY)
|
||||
if (NOT LIBAIRSPY_FOUND)
|
||||
@@ -262,6 +304,29 @@ endif ()
|
||||
add_definitions (-DHAVE_RAWFILES)
|
||||
endif()
|
||||
|
||||
if (XMLFILES)
|
||||
include_directories (
|
||||
../devices/xml-filereader
|
||||
)
|
||||
|
||||
set ($(objectName)_HDRS
|
||||
${${objectName}_HDRS}
|
||||
../devices/xml-filereader/rapidxml.hpp
|
||||
../devices/xml-filereader/xml-filereader.h
|
||||
../devices/xml-filereader/xmlreader.h
|
||||
../devices/xml-filereader/element-reader.h
|
||||
../devices/xml-filereader/xml-descriptor.h
|
||||
)
|
||||
|
||||
set (${objectName}_SRCS
|
||||
${${objectName}_SRCS}
|
||||
../devices/xml-filereader/xml-filereader.cpp
|
||||
../devices/xml-filereader/xml-reader.cpp
|
||||
../devices/xml-filereader/xml-descriptor.cpp
|
||||
)
|
||||
|
||||
add_definitions (-DHAVE_XMLFILES)
|
||||
endif (XMLFILES)
|
||||
#######################################################################
|
||||
#
|
||||
# Here we really start
|
||||
@@ -322,6 +387,7 @@ endif ()
|
||||
../library/includes/backend/audio/mp2processor.h
|
||||
../library/includes/backend/data/virtual-datahandler.h
|
||||
../library/includes/backend/data/tdc-datahandler.h
|
||||
# ../library/includes/backend/data/adv-datahandler.h
|
||||
../library/includes/backend/data/pad-handler.h
|
||||
../library/includes/backend/data/data-processor.h
|
||||
../library/includes/backend/data/mot/mot-handler.h
|
||||
@@ -372,6 +438,7 @@ endif ()
|
||||
../library/src/backend/audio/mp2processor.cpp
|
||||
../library/src/backend/data/virtual-datahandler.cpp
|
||||
../library/src/backend/data/tdc-datahandler.cpp
|
||||
# ../library/src/backend/data/adv-datahandler.cpp
|
||||
../library/src/backend/data/pad-handler.cpp
|
||||
../library/src/backend/data/data-processor.cpp
|
||||
../library/src/backend/data/mot/mot-handler.cpp
|
||||
|
@@ -6,12 +6,11 @@ input device is fixed, depending on a setting in the CMakeLists.txt
|
||||
file. The output is either to the soundcard (default) or to a file
|
||||
(-O option).
|
||||
|
||||
It is an experimental extension of example. In example 5 a
|
||||
It is an experimental extension of example 2. In example 5 a
|
||||
small keyboard listener is added such that touching the "return" key will select
|
||||
the "next" program (it will skip data services). All other keys are ignored.
|
||||
|
||||
NOTE To use a data service, the name of the data program has to be passed by the
|
||||
-P parameter (e.g. -P TPEG). The receipt data will be output via the TDC port (8888).
|
||||
the "next" program. All other keys are ignored.
|
||||
With 'a' key visibility of the DLS is controlled, with the 'b' key
|
||||
the visibility of the quality indicator.
|
||||
|
||||
Other than example 1, it binds directly to the functionality implementing
|
||||
the DAB decoding.
|
||||
@@ -24,7 +23,7 @@ FEEL FREE TO IMPROVE THE PROGRAM
|
||||
|
||||
Points to note:
|
||||
|
||||
The "-W" parameter (to specify Waiting time) is changed into tw
|
||||
The "-W" parameter (to specify Waiting time) is changed into two
|
||||
parameters
|
||||
"-d" to indicate the time within which timesynchronization must be
|
||||
accomplished;
|
||||
@@ -49,3 +48,6 @@ header into account
|
||||
|
||||
The bytesOut function puts the data into a simple TCP server that can be
|
||||
read from port 8888 (depending on the configuration).
|
||||
|
||||
Added is the possibility to use xml files as input device.
|
||||
|
||||
|
@@ -27,9 +27,7 @@
|
||||
*/
|
||||
audioSink::audioSink (int16_t latency,
|
||||
std::string soundChannel,
|
||||
bool *err):
|
||||
audioBase () {
|
||||
int32_t i;
|
||||
bool *err): audioBase () {
|
||||
this -> latency = latency;
|
||||
this -> CardRate = 48000;
|
||||
_O_Buffer = new RingBuffer<float>(2 * 32768);
|
||||
@@ -43,19 +41,19 @@ int32_t i;
|
||||
portAudio = true;
|
||||
fprintf (stderr, "Hostapis: %d\n", Pa_GetHostApiCount ());
|
||||
|
||||
for (i = 0; i < Pa_GetHostApiCount (); i ++)
|
||||
for (int i = 0; i < Pa_GetHostApiCount (); i ++)
|
||||
fprintf (stderr, "Api %d is %s\n", i, Pa_GetHostApiInfo (i) -> name);
|
||||
|
||||
numofDevices = Pa_GetDeviceCount ();
|
||||
outTable = new int16_t [numofDevices + 1];
|
||||
for (i = 0; i < numofDevices; i ++)
|
||||
for (int i = 0; i < numofDevices; i ++)
|
||||
outTable [i] = -1;
|
||||
ostream = NULL;
|
||||
*err = !selectDevice (soundChannel);
|
||||
}
|
||||
|
||||
audioSink::~audioSink (void) {
|
||||
if ((ostream != NULL) && !Pa_IsStreamStopped (ostream)) {
|
||||
audioSink::~audioSink () {
|
||||
if ((ostream != nullptr) && !Pa_IsStreamStopped (ostream)) {
|
||||
paCallbackReturn = paAbort;
|
||||
(void) Pa_AbortStream (ostream);
|
||||
while (!Pa_IsStreamStopped (ostream))
|
||||
@@ -63,7 +61,7 @@ int32_t i;
|
||||
writerRunning = false;
|
||||
}
|
||||
|
||||
if (ostream != NULL)
|
||||
if (ostream != nullptr)
|
||||
Pa_CloseStream (ostream);
|
||||
|
||||
if (portAudio)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#
|
||||
/*
|
||||
* Copyright (C) 2015, 2016, 2017
|
||||
* Copyright (C) 2015, 2016, 2017, 2024
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Computing
|
||||
*
|
||||
@@ -37,6 +37,8 @@
|
||||
#include "includes/support/band-handler.h"
|
||||
#ifdef HAVE_SDRPLAY
|
||||
#include "sdrplay-handler.h"
|
||||
#elif HAVE_SDRPLAY_V3
|
||||
#include "sdrplay-handler-v3.h"
|
||||
#elif HAVE_AIRSPY
|
||||
#include "airspy-handler.h"
|
||||
#elif HAVE_RTLSDR
|
||||
@@ -45,6 +47,8 @@
|
||||
#include "wavfiles.h"
|
||||
#elif HAVE_RAWFILES
|
||||
#include "rawfiles.h"
|
||||
#elif HAVE_XMLFILES
|
||||
#include "xml-filereader.h"
|
||||
#elif HAVE_RTL_TCP
|
||||
#include "rtl_tcp-client.h"
|
||||
#endif
|
||||
@@ -61,15 +65,16 @@ using std::endl;
|
||||
//
|
||||
// messages
|
||||
typedef struct {
|
||||
int key;
|
||||
std::string string;
|
||||
int key;
|
||||
std::string string;
|
||||
} message;
|
||||
|
||||
static
|
||||
lockingQueue<message> messageQueue;
|
||||
|
||||
std::string theChannel = "11C";
|
||||
deviceHandler *theDevice = nullptr;
|
||||
std::string theChannel = "11C";
|
||||
std::string programName = "";
|
||||
|
||||
#define S_QUIT 0100
|
||||
#define S_NEXT 0101
|
||||
@@ -79,13 +84,15 @@ deviceHandler *theDevice = nullptr;
|
||||
void printOptions (); // forward declaration
|
||||
void listener ();
|
||||
void selectNextService ();
|
||||
void startAudio (const std::string &, audiodata *);
|
||||
void startData (const std::string &, packetdata *);
|
||||
// we deal with some callbacks, so we have some data that needs
|
||||
// to be accessed from global contexts
|
||||
static
|
||||
std::atomic<bool> run;
|
||||
|
||||
static
|
||||
void *theRadio = NULL;
|
||||
void *theRadio = nullptr;
|
||||
|
||||
static
|
||||
std::atomic<bool>timeSynced;
|
||||
@@ -97,8 +104,12 @@ static
|
||||
std::atomic<bool>ensembleRecognized;
|
||||
|
||||
static
|
||||
audioBase *soundOut = NULL;
|
||||
audioBase *soundOut = nullptr;
|
||||
|
||||
static
|
||||
bool show_dynLab = false;
|
||||
static
|
||||
bool show_mscQuality = false;
|
||||
#ifdef DATA_STREAMER
|
||||
tcpServer tdcServer (8888);
|
||||
#endif
|
||||
@@ -106,11 +117,9 @@ tcpServer tdcServer (8888);
|
||||
// Relaxed matching
|
||||
bool matches(const std::string& s1, const std::string& s2) {
|
||||
// Trim s1 to the length of s2 and compare
|
||||
return s1.substr(0, s2.size()) == s2;
|
||||
return s1. substr (0, s2.size()) == s2;
|
||||
}
|
||||
|
||||
std::string programName = "Sky Radio";
|
||||
//int32_t serviceIdentifier = -1;
|
||||
|
||||
static void sighandler (int signum) {
|
||||
fprintf (stderr, "Signal caught, terminating!\n");
|
||||
@@ -166,7 +175,8 @@ void programdata_Handler (audiodata *d, void *ctx) {
|
||||
static
|
||||
void dataOut_Handler (const char * dynamicLabel, void *ctx) {
|
||||
(void)ctx;
|
||||
fprintf (stderr, "%s\n", dynamicLabel);
|
||||
if (show_dynLab)
|
||||
fprintf (stderr, "%s\n", dynamicLabel);
|
||||
}
|
||||
//
|
||||
// Note: the function is called from the tdcHandler with a
|
||||
@@ -196,7 +206,7 @@ int16_t i;
|
||||
localBuf [7] = type == 0 ? 0 : 0xFF;
|
||||
for (i = 0; i < amount; i ++)
|
||||
localBuf [8 + i] = data[i];
|
||||
fprintf(stderr,"tdcServer::out[%d]\n",amount+8);
|
||||
fprintf (stderr, "tdcServer::out [%d]\n",amount+8);
|
||||
tdcServer. sendData (localBuf, amount + 8);
|
||||
#else
|
||||
(void)data;
|
||||
@@ -239,41 +249,46 @@ void fibQuality (int16_t q, void *ctx) {
|
||||
|
||||
static
|
||||
void mscQuality (int16_t fe, int16_t rsE, int16_t aacE, void *ctx) {
|
||||
fprintf (stderr, "msc quality = %d %d %d\n", fe, rsE, aacE);
|
||||
if (show_mscQuality)
|
||||
fprintf (stderr, "msc quality = %d %d %d\n", fe, rsE, aacE);
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
// Default values
|
||||
std::string commandParams = "";
|
||||
uint8_t theMode = 1;
|
||||
uint8_t theBand = BAND_III;
|
||||
int16_t ppmCorrection = 0;
|
||||
#ifdef HAVE_SDRPLAY
|
||||
#if defined (HAVE_SDRPLAY) || defined (HAVE_SDRPLAY_V3)
|
||||
int16_t GRdB = 30;
|
||||
int16_t lnaState = 2;
|
||||
#else
|
||||
int theGain = 35; // scale = 0 .. 100
|
||||
int16_t lnaState = 3;
|
||||
#endif
|
||||
int theGain = 35; // scale = 0 .. 100
|
||||
std::string soundChannel = "default";
|
||||
int16_t latency = 10;
|
||||
int16_t timeSyncTime = 5;
|
||||
int16_t freqSyncTime = 10;
|
||||
int16_t progSyncTime = 5;
|
||||
int16_t progSyncTime = 10;
|
||||
bool autogain = false;
|
||||
int opt;
|
||||
struct sigaction sigact;
|
||||
bandHandler dabBand;
|
||||
#ifdef HAVE_WAVFILES
|
||||
std::string fileName;
|
||||
#elif HAVE_RAWFILES
|
||||
#if defined (HAVE_WAVFILES) || defined (HAVE_RAWFILES) || defined (HAVE_XMLFILES)
|
||||
commandParams = "D:d:M:B:P:A:L:S:F:O:";
|
||||
std::string fileName;
|
||||
bool repeater = true;
|
||||
#elif HAVE_RTL_TCP
|
||||
std::string hostname = "127.0.0.1"; // default
|
||||
int32_t basePort = 1234; // default
|
||||
commandParams ="D:d:M:B:P:A:L:S:F:OR:";
|
||||
|
||||
#endif
|
||||
bool err;
|
||||
|
||||
fprintf (stderr, "dab_cmdline V 1.0alfa example 5,\n \
|
||||
fprintf (stderr, "dab_cmdline V 1.0 example 5,\n \
|
||||
Copyright 2017 J van Katwijk, Lazy Chair Computing\n");
|
||||
if (commandParams == "")
|
||||
commandParams = "D:d:M:B:C:P:G:A:L:S:H:I:QO:";
|
||||
timeSynced. store (false);
|
||||
timesyncSet. store (false);
|
||||
run. store (false);
|
||||
@@ -285,13 +300,7 @@ bool err;
|
||||
|
||||
// For file input we do not need options like Q, G and C,
|
||||
// We do need an option to specify the filename
|
||||
#if (defined (HAVE_WAVFILES) && defined (HAVE_RAWFILES))
|
||||
while ((opt = getopt (argc, argv, "D:d:M:B:P:A:L:S:F:O:")) != -1) {
|
||||
#elif HAVE_RTL_TCP
|
||||
while ((opt = getopt (argc, argv, "D:d:M:B:C:P:G:A:L:S:H:QO:")) != -1) { // Arg H has to be included
|
||||
#else
|
||||
while ((opt = getopt (argc, argv, "D:d:M:B:C:P:G:A:L:S:H:I:QO:")) != -1) {
|
||||
#endif
|
||||
while ((opt = getopt (argc, argv, commandParams. c_str ())) != -1) {
|
||||
switch (opt) {
|
||||
case 'D':
|
||||
freqSyncTime = atoi (optarg);
|
||||
@@ -319,16 +328,20 @@ bool err;
|
||||
case 'p':
|
||||
ppmCorrection = atoi (optarg);
|
||||
break;
|
||||
#if defined (HAVE_WAVFILES) || defined (HAVE_RAWFILES)
|
||||
|
||||
#if (defined (HAVE_WAVFILES) || defined (HAVE_RAWFILES) || defined (HAVE_XMLFILES))
|
||||
case 'F':
|
||||
fileName = std::string (optarg);
|
||||
break;
|
||||
case 'R':
|
||||
repeater = false;
|
||||
break;
|
||||
#else
|
||||
case 'C':
|
||||
theChannel = std::string (optarg);
|
||||
break;
|
||||
|
||||
#ifdef HAVE_SDRPLAY
|
||||
#endif
|
||||
#if defined (HAVE_SDRPLAY) || defined (HAVE_SDRPLAY_V3)
|
||||
case 'G':
|
||||
GRdB = atoi (optarg);
|
||||
break;
|
||||
@@ -360,8 +373,6 @@ bool err;
|
||||
basePort = atoi (optarg);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
case 'O':
|
||||
soundOut = new fileSink (std::string (optarg), &err);
|
||||
if (!err) {
|
||||
@@ -394,6 +405,14 @@ bool err;
|
||||
autogain,
|
||||
0,
|
||||
0);
|
||||
#elif HAVE_SDRPLAY_V3
|
||||
theDevice = new sdrplayHandler_v3 (frequency,
|
||||
ppmCorrection,
|
||||
GRdB,
|
||||
lnaState,
|
||||
autogain,
|
||||
0,
|
||||
0);
|
||||
#elif HAVE_AIRSPY
|
||||
theDevice = new airspyHandler (frequency,
|
||||
ppmCorrection,
|
||||
@@ -406,7 +425,9 @@ bool err;
|
||||
#elif HAVE_WAVFILES
|
||||
theDevice = new wavFiles (fileName);
|
||||
#elif HAVE_RAWFILES
|
||||
theDevice = new wavFiles (fileName);
|
||||
theDevice = new rawFiles (fileName);
|
||||
#elif HAVE_XMLFILES
|
||||
theDevice = new xml_fileReader (fileName, repeater);
|
||||
#elif HAVE_RTL_TCP
|
||||
theDevice = new rtl_tcp_client (hostname,
|
||||
basePort,
|
||||
@@ -425,14 +446,14 @@ bool err;
|
||||
if (soundOut == nullptr) { // not bound to a file?
|
||||
soundOut = new audioSink (latency, soundChannel, &err);
|
||||
if (err) {
|
||||
fprintf (stderr, "DBG: no valid sound channel\n"); // , fatal\n");
|
||||
//exit (33); // Be more tolerant to the guys, running this piece of software on a head- and phoneless server
|
||||
fprintf (stderr, "no valid sound channel\n");
|
||||
exit (33);
|
||||
}
|
||||
}
|
||||
//
|
||||
// and with a sound device we can create a "backend"
|
||||
API_struct interface;
|
||||
interface. dabMode = theMode;
|
||||
interface. dabMode = theMode;
|
||||
interface. syncsignal_Handler = syncsignal_Handler;
|
||||
interface. systemdata_Handler = systemData;
|
||||
interface. ensemblename_Handler = ensemblename_Handler;
|
||||
@@ -449,16 +470,16 @@ bool err;
|
||||
|
||||
theRadio = dabInit (theDevice,
|
||||
&interface,
|
||||
NULL, // no spectrum shown
|
||||
NULL, // no constellations
|
||||
NULL
|
||||
nullptr, // no spectrum shown
|
||||
nullptr, // no constellations
|
||||
nullptr
|
||||
);
|
||||
if (theRadio == NULL) {
|
||||
if (theRadio == nullptr) {
|
||||
fprintf (stderr, "sorry, no radio available, fatal\n");
|
||||
exit (4);
|
||||
}
|
||||
|
||||
// theDevice -> setGain (theGain);
|
||||
theDevice -> setGain (theGain);
|
||||
if (autogain)
|
||||
theDevice -> set_autogain (autogain);
|
||||
theDevice -> restartReader (frequency);
|
||||
@@ -501,48 +522,52 @@ bool err;
|
||||
exit (22);
|
||||
}
|
||||
|
||||
if (programNames. size () == 0) { // ensemble with no names
|
||||
fprintf (stderr, "No services found, fatal\n");
|
||||
theDevice -> stopReader ();
|
||||
sleep (1);
|
||||
dabStop (theRadio);
|
||||
dabExit (theRadio);
|
||||
delete theDevice;
|
||||
exit (23);
|
||||
}
|
||||
|
||||
run. store (true);
|
||||
std::thread keyboard_listener = std::thread (&listener);
|
||||
/* std::cerr << "we try to start program " <<
|
||||
programName << "\n"; */
|
||||
|
||||
// Search Ensemble for selected programName
|
||||
// Search Ensemble for selected programName
|
||||
audiodata ad;
|
||||
packetdata pd;
|
||||
ad.defined = pd.defined = false; // seems to be necessary
|
||||
while ((ad. defined == pd .defined) && (--progSyncTime >= 0)) {
|
||||
if (programName == "")
|
||||
programName = programNames [0];
|
||||
bool nameFound = false;
|
||||
while ((!ad. defined && !pd. defined) && (--progSyncTime >= 0)) {
|
||||
for (uint8_t i = 0; i < programNames. size (); i ++) {
|
||||
if (matches (programNames [i], programName)) {
|
||||
programName = programNames [i];
|
||||
fprintf (stderr, "we now try to start program %s \n", programName. c_str ());
|
||||
if (is_audioService (theRadio, programName. c_str ())) {
|
||||
dataforAudioService (theRadio, programName. c_str (), &ad, 0);
|
||||
dabReset_msc (theRadio);
|
||||
set_audioChannel (theRadio, &ad);
|
||||
fprintf(stderr,"(audio) \n");
|
||||
} else if (is_dataService (theRadio, programName. c_str ())) {
|
||||
dataforDataService (theRadio, programName. c_str (), &pd, 0);
|
||||
dabReset_msc (theRadio);
|
||||
set_dataChannel (theRadio, &pd);
|
||||
fprintf(stderr,"DBG: bitRate=%d length=%d prot=%d\n",pd.bitRate,pd.length,pd.protLevel); // (data) is already part of the program name
|
||||
} else {
|
||||
fprintf(stderr,"Should not happen");
|
||||
}
|
||||
if (ad. defined == pd. defined) {
|
||||
programName = programNames [i];
|
||||
nameFound = true;
|
||||
fprintf (stderr,
|
||||
"we now try to start program %s \n",
|
||||
programName. c_str ());
|
||||
if (is_audioService (theRadio, programName. c_str ())) {
|
||||
startAudio (programName, &ad);
|
||||
}
|
||||
else
|
||||
if (is_dataService (theRadio, programName. c_str ())) {
|
||||
startData (programName, &pd);
|
||||
}
|
||||
else {
|
||||
fprintf (stderr,"Should not happen");
|
||||
}
|
||||
if (!ad. defined && !pd. defined) {
|
||||
std::cerr << "sorry we cannot handle service " << programName << "\n";
|
||||
sighandler (9);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ad. defined == pd. defined) {
|
||||
fprintf (stderr, ">>> %d\r\r\r\r\r", progSyncTime);
|
||||
if (progSyncTime == 0) {
|
||||
programName = programNames [0]; // Fallback to first program of ensemble
|
||||
progSyncTime = 2;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
if (!nameFound)
|
||||
programName = programNames [0];
|
||||
}
|
||||
|
||||
while (run. load ()) {
|
||||
@@ -552,6 +577,9 @@ bool err;
|
||||
case S_NEXT:
|
||||
selectNextService ();
|
||||
break;
|
||||
case S_QUIT:
|
||||
run. store (false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -562,9 +590,10 @@ bool err;
|
||||
dabExit (theRadio);
|
||||
delete theDevice;
|
||||
delete soundOut;
|
||||
throw (1);
|
||||
}
|
||||
|
||||
void printOptions (void) {
|
||||
void printOptions () {
|
||||
fprintf (stderr,
|
||||
" dab-cmdline options are\n\
|
||||
-W number amount of time to look for an ensemble\n\
|
||||
@@ -572,10 +601,10 @@ void printOptions (void) {
|
||||
-B Band Band is either L_BAND or BAND_III (default)\n\
|
||||
-P name program to be selected in the ensemble\n\
|
||||
-C channel channel to be used\n\
|
||||
-G Gainreduction for SDRPLAY (range 20 .. 59)\n\
|
||||
-L lnaState for SDRplay\n\
|
||||
-G Gain gain for device (range 1 .. 100)\n\
|
||||
-L number latency for audiobuffer\n\
|
||||
-G gainreduction for SDRplay\n\
|
||||
-L lnaState for SDRplay\n\
|
||||
-Q if set, set autogain for device true\n\
|
||||
-F filename in case the input is from file\n\
|
||||
-A name select the audio channel (portaudio)\n\
|
||||
@@ -591,10 +620,9 @@ void printOptions (void) {
|
||||
}
|
||||
|
||||
void selectNextService () {
|
||||
int16_t i;
|
||||
int16_t foundIndex = -1;
|
||||
|
||||
for (i = 0; i < programNames. size (); i ++) {
|
||||
for (int i = 0; i < programNames. size (); i ++) {
|
||||
if (matches (programNames [i], programName)) {
|
||||
if (i == programNames. size () - 1)
|
||||
foundIndex = 0;
|
||||
@@ -610,9 +638,11 @@ int16_t foundIndex = -1;
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// skip the data services. Slightly dangerous here, may be
|
||||
// add a guard for "only data services" ensembles
|
||||
audiodata ad;
|
||||
packetdata pd;
|
||||
while (!is_audioService (theRadio,
|
||||
programNames [foundIndex]. c_str ()) &&
|
||||
!is_dataService (theRadio,
|
||||
programNames [foundIndex]. c_str ()))
|
||||
foundIndex = (foundIndex + 1) % programNames. size ();
|
||||
|
||||
@@ -620,27 +650,24 @@ int16_t foundIndex = -1;
|
||||
fprintf (stderr, "we now try to start program %s\n",
|
||||
programName. c_str ());
|
||||
|
||||
audiodata ad;
|
||||
dataforAudioService (theRadio, programName. c_str (), &ad, 0);
|
||||
if (!ad. defined) {
|
||||
std::cerr << "sorry we cannot handle service " <<
|
||||
programName << "\n";
|
||||
sighandler (9);
|
||||
if (is_audioService (theRadio, programName. c_str ())) {
|
||||
startAudio (programName, &ad);
|
||||
}
|
||||
else
|
||||
if (is_dataService (theRadio, programName. c_str ())) {
|
||||
startData (programName, &pd);
|
||||
}
|
||||
dabReset_msc (theRadio);
|
||||
set_audioChannel (theRadio, &ad);
|
||||
}
|
||||
|
||||
#define MAX_STRING_SIZE 100
|
||||
void listener () {
|
||||
char input [MAX_STRING_SIZE] = {0};
|
||||
|
||||
fprintf (stderr, "listener is running\n");
|
||||
while (run. load ()) {
|
||||
message m;
|
||||
fgets (input, sizeof (input), stdin);
|
||||
int inputLen = strlev (input):
|
||||
for (int i = 0; i < inputLen; i ++)
|
||||
int inputLen = strlen (input);
|
||||
for (int i = 0; i < inputLen; i ++) {
|
||||
switch (input [i]) {
|
||||
case '\n':
|
||||
m.key = S_NEXT;
|
||||
@@ -648,9 +675,37 @@ char input [MAX_STRING_SIZE] = {0};
|
||||
messageQueue. push (m);
|
||||
i = inputLen; // leave the loop
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
case 'q':
|
||||
m. key = S_QUIT;
|
||||
m. string = "";
|
||||
messageQueue. push (m);
|
||||
return;
|
||||
case 'a':
|
||||
show_dynLab = !show_dynLab;
|
||||
i = inputLen; // leave the loop
|
||||
break;
|
||||
case 's':
|
||||
show_mscQuality = !show_mscQuality;
|
||||
i = inputLen;
|
||||
break;
|
||||
default:;
|
||||
i = inputLen;
|
||||
// fprintf (stderr, "unidentified %d (%c)\n", t, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void startAudio (const std::string &serviceName, audiodata *ad) {
|
||||
dataforAudioService (theRadio, serviceName. c_str (), ad, 0);
|
||||
dabReset_msc (theRadio);
|
||||
set_audioChannel (theRadio, ad);
|
||||
}
|
||||
|
||||
void startData (const std::string &serviceName, packetdata *pd) {
|
||||
dataforDataService (theRadio, serviceName. c_str (), pd, 0);
|
||||
dabReset_msc (theRadio);
|
||||
set_dataChannel (theRadio, pd);
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "dab-semaphore.h"
|
||||
#include "dab-api.h"
|
||||
#include "virtual-backend.h"
|
||||
#include "data-processor.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
class backendBase;
|
||||
@@ -63,6 +64,6 @@ void run (void);
|
||||
int16_t nextOut;
|
||||
|
||||
protection *protectionHandler;
|
||||
backendBase *our_backendBase;
|
||||
dataProcessor our_backendBase;
|
||||
};
|
||||
|
||||
|
37
library/includes/backend/data/adv-datahandler.h
Normal file
37
library/includes/backend/data/adv-datahandler.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#
|
||||
/*
|
||||
* Copyright (C) 2015 .. 2024
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Computing
|
||||
*
|
||||
* This file is part of the DAB library
|
||||
*
|
||||
* DAB library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DAB 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DAB library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "dab-constants.h"
|
||||
#include "virtual-datahandler.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
class adv_dataHandler:public virtual_dataHandler {
|
||||
public:
|
||||
adv_dataHandler ();
|
||||
~adv_dataHandler ();
|
||||
void add_mscDatagroup (std::vector<uint8_t>);
|
||||
};
|
||||
|
@@ -4,7 +4,8 @@
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Computing
|
||||
*
|
||||
* This file is part of the DAB library of the SDR-J software
|
||||
* This file is part of the DAB library
|
||||
*
|
||||
* DAB library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
@@ -20,15 +21,15 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#
|
||||
#ifndef __DATA_PROCESSOR__
|
||||
#define __DATA_PROCESSOR__
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include "dab-api.h"
|
||||
#include "backend-base.h"
|
||||
#include "reed-solomon.h"
|
||||
#include "reed-solomon.h"
|
||||
|
||||
|
||||
class virtual_dataHandler;
|
||||
@@ -39,7 +40,7 @@ public:
|
||||
packetdata *pd,
|
||||
API_struct *p,
|
||||
void *ctx);
|
||||
~dataProcessor (void);
|
||||
~dataProcessor ();
|
||||
void addtoFrame (uint8_t *);
|
||||
private:
|
||||
int16_t bitRate;
|
||||
@@ -48,43 +49,35 @@ private:
|
||||
int16_t packetAddress;
|
||||
uint8_t DGflag;
|
||||
int16_t FEC_scheme;
|
||||
int16_t expectedIndex;
|
||||
std::vector<uint8_t> series;
|
||||
int16_t fillPointer;
|
||||
bool assembling;
|
||||
std::vector<uint8_t> AppVector;
|
||||
std::vector<uint8_t> FECVector;
|
||||
bool FEC_table [9];
|
||||
reedSolomon my_rsDecoder;
|
||||
bytesOut_t bytesOut;
|
||||
programQuality_t mscQuality; // taken from mp4processor.h
|
||||
void *ctx;
|
||||
int16_t crcErrors;
|
||||
int16_t frameCount;
|
||||
int16_t frameErrors;
|
||||
int16_t rsErrors;
|
||||
int16_t frame_quality;
|
||||
int16_t rs_quality;
|
||||
uint8_t pdlen; // to check that was passed from the API
|
||||
|
||||
std::vector<uint8_t> series;
|
||||
uint8_t packetState;
|
||||
std::vector<uint8_t> ByteBuf;
|
||||
|
||||
int16_t blockFillIndex;
|
||||
int16_t blocksInBuffer;
|
||||
uint8_t curMSC;
|
||||
uint8_t curPI;
|
||||
std::vector<uint8_t> frameBytes;
|
||||
std::vector<uint8_t> outVector; // added for RS decoding
|
||||
uint8_t RSDims;
|
||||
reedSolomon my_rsDecoder;
|
||||
|
||||
int32_t streamAddress; // int since we init with -1
|
||||
std::atomic<bool> running;
|
||||
//
|
||||
// result handlers
|
||||
void handleTDCAsyncstream (uint8_t *, int16_t);
|
||||
void handlePackets (uint8_t *, int16_t);
|
||||
void handleRSdata (uint8_t *); // handle RS data bytes
|
||||
uint8_t processRSData (uint8_t *, uint16_t); // process RS data bytes
|
||||
void handleAppData (uint8_t *, int16_t); // handle application data
|
||||
void applyFEC (void);
|
||||
void verifyRSdecoder (uint8_t* , uint8_t* , uint8_t*); // verification of RS decoder
|
||||
void processOutVector (const std::vector<uint8_t>&);
|
||||
void processPacketStream (void);
|
||||
void handleTDCAsyncstream (uint8_t *, int32_t);
|
||||
void handlePackets (uint8_t *, int16_t);
|
||||
void handlePacket (uint8_t *vec);
|
||||
|
||||
void handleRSPacket (uint8_t *);
|
||||
void registerFEC (uint8_t *, int);
|
||||
void clear_FECtable ();
|
||||
bool FEC_complete ();
|
||||
void handle_RSpackets (std::vector<uint8_t> &);
|
||||
void handle_RSpacket (uint8_t *, int16_t);
|
||||
int addPacket (uint8_t *,
|
||||
std::vector<uint8_t> &, int);
|
||||
|
||||
void processRS (std::vector<uint8_t> &appdata,
|
||||
const std::vector<uint8_t> &RSdata);
|
||||
virtual_dataHandler *my_dataHandler;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
83
library/includes/backend/data/data-processor.h-jan
Normal file
83
library/includes/backend/data/data-processor.h-jan
Normal file
@@ -0,0 +1,83 @@
|
||||
#
|
||||
/*
|
||||
* Copyright (C) 2015
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Computing
|
||||
*
|
||||
* This file is part of the DAB library
|
||||
*
|
||||
* DAB library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DAB 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DAB library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include "dab-api.h"
|
||||
#include "backend-base.h"
|
||||
#include "reed-solomon.h"
|
||||
|
||||
|
||||
class virtual_dataHandler;
|
||||
|
||||
class dataProcessor:public backendBase {
|
||||
public:
|
||||
dataProcessor (int16_t bitRate,
|
||||
packetdata *pd,
|
||||
API_struct *p,
|
||||
void *ctx);
|
||||
~dataProcessor ();
|
||||
void addtoFrame (uint8_t *);
|
||||
private:
|
||||
int16_t bitRate;
|
||||
uint8_t DSCTy;
|
||||
int16_t appType;
|
||||
int16_t packetAddress;
|
||||
uint8_t DGflag;
|
||||
int16_t FEC_scheme;
|
||||
int16_t expectedIndex;
|
||||
std::vector<uint8_t> series;
|
||||
int16_t fillPointer;
|
||||
bool assembling;
|
||||
std::vector<uint8_t> AppVector;
|
||||
std::vector<uint8_t> FECVector;
|
||||
bool FEC_table [9];
|
||||
reedSolomon my_rsDecoder;
|
||||
bytesOut_t bytesOut;
|
||||
int32_t streamAddress; // int since we init with -1
|
||||
std::atomic<bool> running;
|
||||
//
|
||||
// result handlers
|
||||
void handleTDCAsyncstream (uint8_t *, int32_t);
|
||||
void handlePackets (uint8_t *, int16_t);
|
||||
void handlePacket (uint8_t *vec);
|
||||
|
||||
void handleRSPacket (uint8_t *);
|
||||
void registerFEC (uint8_t *, int);
|
||||
void clear_FECtable ();
|
||||
bool FEC_complete ();
|
||||
void handle_RSpackets (std::vector<uint8_t> &);
|
||||
void handle_RSpacket (uint8_t *, int16_t);
|
||||
int addPacket (uint8_t *,
|
||||
std::vector<uint8_t> &, int);
|
||||
|
||||
void processRS (std::vector<uint8_t> &appdata,
|
||||
const std::vector<uint8_t> &RSdata);
|
||||
virtual_dataHandler *my_dataHandler;
|
||||
};
|
||||
|
||||
|
90
library/includes/backend/data/data-processor.h-juhl
Normal file
90
library/includes/backend/data/data-processor.h-juhl
Normal file
@@ -0,0 +1,90 @@
|
||||
#
|
||||
/*
|
||||
* Copyright (C) 2015
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Computing
|
||||
*
|
||||
* This file is part of the DAB library of the SDR-J software
|
||||
* DAB library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DAB 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DAB library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#
|
||||
#ifndef __DATA_PROCESSOR__
|
||||
#define __DATA_PROCESSOR__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include "dab-api.h"
|
||||
#include "backend-base.h"
|
||||
#include "reed-solomon.h"
|
||||
|
||||
|
||||
class virtual_dataHandler;
|
||||
|
||||
class dataProcessor:public backendBase {
|
||||
public:
|
||||
dataProcessor (int16_t bitRate,
|
||||
packetdata *pd,
|
||||
API_struct *p,
|
||||
void *ctx);
|
||||
~dataProcessor (void);
|
||||
void addtoFrame (uint8_t *);
|
||||
private:
|
||||
int16_t bitRate;
|
||||
uint8_t DSCTy;
|
||||
int16_t appType;
|
||||
int16_t packetAddress;
|
||||
uint8_t DGflag;
|
||||
int16_t FEC_scheme;
|
||||
bytesOut_t bytesOut;
|
||||
programQuality_t mscQuality; // taken from mp4processor.h
|
||||
void *ctx;
|
||||
int16_t crcErrors;
|
||||
int16_t frameCount;
|
||||
int16_t frameErrors;
|
||||
int16_t rsErrors;
|
||||
int16_t frame_quality;
|
||||
int16_t rs_quality;
|
||||
uint8_t pdlen; // to check that was passed from the API
|
||||
|
||||
std::vector<uint8_t> series;
|
||||
uint8_t packetState;
|
||||
std::vector<uint8_t> ByteBuf;
|
||||
|
||||
int16_t blockFillIndex;
|
||||
int16_t blocksInBuffer;
|
||||
uint8_t curMSC;
|
||||
uint8_t curPI;
|
||||
std::vector<uint8_t> frameBytes;
|
||||
std::vector<uint8_t> outVector; // added for RS decoding
|
||||
uint8_t RSDims;
|
||||
reedSolomon my_rsDecoder;
|
||||
|
||||
//
|
||||
// result handlers
|
||||
void handleTDCAsyncstream (uint8_t *, int16_t);
|
||||
void handlePackets (uint8_t *, int16_t);
|
||||
void handleRSdata (uint8_t *); // handle RS data bytes
|
||||
uint8_t processRSData (uint8_t *, uint16_t); // process RS data bytes
|
||||
void handleAppData (uint8_t *, int16_t); // handle application data
|
||||
void applyFEC (void);
|
||||
void verifyRSdecoder (uint8_t* , uint8_t* , uint8_t*); // verification of RS decoder
|
||||
void processOutVector (const std::vector<uint8_t>&);
|
||||
void processPacketStream (void);
|
||||
virtual_dataHandler *my_dataHandler;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -68,8 +68,8 @@ int32_t i, j;
|
||||
protectionHandler = new eep_protection (bitRate,
|
||||
protLevel);
|
||||
|
||||
fprintf (stderr, "protection handler is %s\n",
|
||||
shortForm ? "uep_protection" : "eep_protection");
|
||||
// fprintf (stderr, "protection handler is %s\n",
|
||||
// shortForm ? "uep_protection" : "eep_protection");
|
||||
if (dabModus == DAB)
|
||||
our_backendBase = new mp2Processor (bitRate, p, ctx);
|
||||
else
|
||||
@@ -78,7 +78,7 @@ int32_t i, j;
|
||||
else // cannot happen
|
||||
our_backendBase = new backendBase ();
|
||||
|
||||
fprintf (stderr, "we have now %s\n", dabModus == DAB_PLUS ? "DAB+" : "DAB");
|
||||
// fprintf (stderr, "we have now %s\n", dabModus == DAB_PLUS ? "DAB+" : "DAB");
|
||||
tempX . resize (fragmentSize);
|
||||
nextIn = 0;
|
||||
nextOut = 0;
|
||||
@@ -99,14 +99,14 @@ int32_t i, j;
|
||||
start ();
|
||||
}
|
||||
|
||||
audioBackend::~audioBackend (void) {
|
||||
audioBackend::~audioBackend () {
|
||||
int16_t i;
|
||||
if (running. load ()) {
|
||||
running. store (false);
|
||||
threadHandle. join ();
|
||||
}
|
||||
// delete our_backendBase;
|
||||
delete protectionHandler;
|
||||
delete our_backendBase;
|
||||
for (i = 0; i < 16; i ++)
|
||||
delete[] interleaveData [i];
|
||||
delete [] interleaveData;
|
||||
@@ -122,6 +122,8 @@ void audioBackend::start (void) {
|
||||
}
|
||||
|
||||
int32_t audioBackend::process (int16_t *v, int16_t cnt) {
|
||||
if (!running. load ())
|
||||
return 0;
|
||||
while (!freeSlots. tryAcquire (200))
|
||||
if (!running. load ())
|
||||
return 0;
|
||||
|
@@ -23,10 +23,8 @@
|
||||
#
|
||||
#include "dab-constants.h"
|
||||
#include "data-backend.h"
|
||||
#include "backend-base.h"
|
||||
#include "eep-protection.h"
|
||||
#include "uep-protection.h"
|
||||
#include "data-processor.h"
|
||||
#include <chrono>
|
||||
|
||||
//
|
||||
@@ -37,24 +35,23 @@
|
||||
virtualBackend (d -> startAddr,
|
||||
d -> length),
|
||||
outV (24 * d -> bitRate),
|
||||
freeSlots (20) {
|
||||
int32_t i, j;
|
||||
freeSlots (20),
|
||||
our_backendBase (d -> bitRate,
|
||||
d,
|
||||
p,
|
||||
ctx) {
|
||||
this -> fragmentSize = d -> length * CUSize;
|
||||
this -> bitRate = d -> bitRate;
|
||||
this -> shortForm = d -> shortForm;
|
||||
this -> protLevel = d -> protLevel;
|
||||
our_backendBase = new dataProcessor (bitRate,
|
||||
d,
|
||||
p,
|
||||
ctx);
|
||||
nextIn = 0;
|
||||
nextOut = 0;
|
||||
for (i = 0; i < 20; i ++)
|
||||
for (int i = 0; i < 20; i ++)
|
||||
theData [i] = new int16_t [fragmentSize];
|
||||
|
||||
tempX. resize (fragmentSize);
|
||||
interleaveData = new int16_t *[16]; // the size
|
||||
for (i = 0; i < 16; i ++) {
|
||||
for (int i = 0; i < 16; i ++) {
|
||||
interleaveData [i] = new int16_t [fragmentSize];
|
||||
memset (interleaveData [i], 0, fragmentSize * sizeof (int16_t));
|
||||
}
|
||||
@@ -74,9 +71,9 @@ int32_t i, j;
|
||||
uint8_t shiftRegister [9];
|
||||
disperseVector. resize (24 * bitRate);
|
||||
memset (shiftRegister, 1, 9);
|
||||
for (i = 0; i < bitRate * 24; i ++) {
|
||||
for (int i = 0; i < bitRate * 24; i ++) {
|
||||
uint8_t b = shiftRegister [8] ^ shiftRegister [4];
|
||||
for (j = 8; j > 0; j--)
|
||||
for (int j = 8; j > 0; j--)
|
||||
shiftRegister [j] = shiftRegister [j - 1];
|
||||
shiftRegister [0] = b;
|
||||
disperseVector [i] = b;
|
||||
@@ -85,23 +82,22 @@ int32_t i, j;
|
||||
start ();
|
||||
}
|
||||
|
||||
dataBackend::~dataBackend (void) {
|
||||
int16_t i;
|
||||
dataBackend::~dataBackend () {
|
||||
if (running. load ()) {
|
||||
threadHandle. join ();
|
||||
running. store (false);
|
||||
threadHandle. join ();
|
||||
}
|
||||
|
||||
delete protectionHandler;
|
||||
for (i = 0; i < 16; i ++)
|
||||
for (int i = 0; i < 16; i ++)
|
||||
delete[] interleaveData [i];
|
||||
delete[] interleaveData;
|
||||
for (i = 0; i < 20; i ++)
|
||||
for (int i = 0; i < 20; i ++)
|
||||
delete [] theData [i];
|
||||
delete our_backendBase;
|
||||
}
|
||||
|
||||
void dataBackend::start (void) {
|
||||
void dataBackend::start () {
|
||||
running. store (true);
|
||||
threadHandle = std::thread (&dataBackend::run, this);
|
||||
}
|
||||
|
||||
@@ -118,7 +114,7 @@ int32_t dataBackend::process (int16_t *v, int16_t cnt) {
|
||||
|
||||
const int16_t interleaveMap[] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
|
||||
|
||||
void dataBackend::run (void) {
|
||||
void dataBackend::run () {
|
||||
int16_t i;
|
||||
int16_t countforInterleaver = 0;
|
||||
int16_t interleaverIndex = 0;
|
||||
@@ -126,7 +122,7 @@ int16_t interleaverIndex = 0;
|
||||
running. store (true);
|
||||
while (running. load ()) {
|
||||
while (!usedSlots. tryAcquire (200))
|
||||
if (!running)
|
||||
if (!running. load ())
|
||||
return;
|
||||
|
||||
for (i = 0; i < fragmentSize; i ++) {
|
||||
@@ -154,12 +150,12 @@ int16_t interleaverIndex = 0;
|
||||
// What we get here is a long sequence (24 * bitrate) of bits, not packed
|
||||
// but forming a DAB packet
|
||||
// we hand it over to make an MSC data group
|
||||
our_backendBase -> addtoFrame (outV. data ());
|
||||
our_backendBase. addtoFrame (outV. data ());
|
||||
}
|
||||
}
|
||||
|
||||
// It might take a msec for the task to stop
|
||||
void dataBackend::stopRunning (void) {
|
||||
void dataBackend::stopRunning () {
|
||||
running. store (false);
|
||||
threadHandle. join ();
|
||||
}
|
||||
|
92
library/src/backend/data/adv-datahandler.cpp
Normal file
92
library/src/backend/data/adv-datahandler.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#
|
||||
/*
|
||||
* Copyright (C) 2015 .. 2024
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Computing
|
||||
*
|
||||
* This file is part of the DAB library
|
||||
*
|
||||
* DAB library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DAB 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DAB library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "adv-datahandler.h"
|
||||
|
||||
//
|
||||
// The adv data handler is meant to be for processing PPP type data,
|
||||
// the format is unknown to me, so I leave it here to
|
||||
// assembling the MSC datagroup
|
||||
adv_dataHandler::adv_dataHandler () {
|
||||
}
|
||||
|
||||
adv_dataHandler::~adv_dataHandler () {
|
||||
}
|
||||
|
||||
const uint8_t syncWord [] = {0x01, 0x41, 0x0f, 0xf7, 0xcf, 0x78, 0x9c};
|
||||
|
||||
void adv_dataHandler::add_mscDatagroup (std::vector<uint8_t> msc) {
|
||||
uint8_t *data = (uint8_t *)(msc. data());
|
||||
bool extensionFlag = getBits_1 (data, 0) != 0;
|
||||
bool crcFlag = getBits_1 (data, 1) != 0;
|
||||
bool segmentFlag = getBits_1 (data, 2) != 0;
|
||||
bool userAccessFlag = getBits_1 (data, 3) != 0;
|
||||
uint8_t dataGroupType = getBits_1 (data, 4);
|
||||
uint8_t cntIdx = getBits_4 (data, 8);
|
||||
uint8_t repInd = getBits_4 (data, 12);
|
||||
uint16_t extField =
|
||||
extensionFlag ? getBits (data, 16, 16) : 0;
|
||||
int next = extensionFlag ? 32 : 16;
|
||||
|
||||
bool LastSegment = 0;
|
||||
uint16_t segmentNumber = 0;
|
||||
|
||||
if (segmentFlag) {
|
||||
LastSegment = getBits (data, next, 15);
|
||||
segmentNumber = getBits_1 (data, next ++);
|
||||
fprintf (stderr, "segment %d\n", segmentNumber);
|
||||
next = next + 15;
|
||||
}
|
||||
|
||||
if (userAccessFlag) {
|
||||
int lengthInd = getBits (data, next + 4, 4);
|
||||
uint8_t transportFlag = getBits (data, next + 3, 1);
|
||||
if (transportFlag)
|
||||
fprintf (stderr, "transportId %d\n",
|
||||
getBits (data, next + 8, 16));
|
||||
next = 16 + (extensionFlag ? 1 : 0) * 16 + (lengthInd + 1) * 8;
|
||||
}
|
||||
if (crcFlag && !check_CRC_bits (data, msc.size())) {
|
||||
fprintf (stderr, "cntIdx %d fails\n", cntIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
int dataLength = msc. size () / 8 - 2 - next / 8;
|
||||
// fprintf (stderr, "%d datalength %d\n",
|
||||
// cntIdx, msc. size () / 8 - 2 - next / 8);
|
||||
uint8_t x0, x1, x2;
|
||||
x0 = getBits (data, next + 8 * 0, 8);
|
||||
x1 = getBits (data, next + 8 * 1, 8);
|
||||
x2 = getBits (data, next + 8 * 2, 8);
|
||||
if (x0 == 0xd3 && x1 == 0) {
|
||||
for (int i = 0; i < 25; i ++) {
|
||||
uint8_t xx = getBits (data, next + 8 * i, 8);
|
||||
fprintf (stderr, "%x%x ", xx >> 4, xx & 0xF);
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
//
|
||||
// the data can be found at next / 8
|
||||
// and is still one bit per byte, ;ength
|
||||
// dataLength
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
379
library/src/backend/data/data-processor.cpp-jan
Normal file
379
library/src/backend/data/data-processor.cpp-jan
Normal file
@@ -0,0 +1,379 @@
|
||||
#
|
||||
/*
|
||||
* Copyright (C) 2024
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Computing
|
||||
*
|
||||
* Replaced Reed-Solomon-Decoder for Packet Data in 2024
|
||||
* from Stefan Juhl (stefanjuhl75@gmail.com) by my own version
|
||||
*
|
||||
* This file is part of DAB library
|
||||
* DAB library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DAB 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DAB library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "dab-constants.h"
|
||||
#include "data-processor.h"
|
||||
#include "virtual-datahandler.h"
|
||||
#include "mot-handler.h"
|
||||
#include "tdc-datahandler.h"
|
||||
#include "mot-handler.h"
|
||||
//#include "adv-datahandler.h"
|
||||
// \class dataProcessor
|
||||
// The main function of this class is to ASSEMBLE the
|
||||
// MSCdatagroups from the incoming packets
|
||||
// and dispatch to the appropriate handler
|
||||
#define RSDIMS 12
|
||||
#define FRAMESIZE 188
|
||||
// fragmentsize == Length * CUSize
|
||||
dataProcessor::dataProcessor (int16_t bitRate,
|
||||
packetdata *pd,
|
||||
API_struct *p,
|
||||
void *ctx):
|
||||
my_rsDecoder (8, 0435, 0, 1, 16) {
|
||||
this -> bitRate = pd -> bitRate;
|
||||
this -> DSCTy = pd -> DSCTy;
|
||||
this -> appType = pd -> appType;
|
||||
this -> packetAddress = pd -> packetAddress;
|
||||
this -> DGflag = pd -> DGflag;
|
||||
this -> FEC_scheme = pd -> FEC_scheme;
|
||||
this -> bytesOut = p -> bytesOut_Handler;
|
||||
|
||||
AppVector. resize (RSDIMS * FRAMESIZE + 48);
|
||||
FECVector. resize (9 * 22);
|
||||
for (int i = 0; i < 9; i ++)
|
||||
FEC_table [i] = false;
|
||||
|
||||
fillPointer = 0;
|
||||
fprintf (stderr, "** DBG: dataProcessor: appType=%d FEC=%d DSCTy=%d (", pd -> appType, FEC_scheme, pd -> DSCTy);
|
||||
switch (DSCTy) {
|
||||
default:
|
||||
fprintf (stderr, "DSCTy %d not supported\n", DSCTy);
|
||||
my_dataHandler = new virtual_dataHandler();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// if (appType == 1500)
|
||||
// my_dataHandler = new adv_dataHandler (); // a dummy one
|
||||
// else
|
||||
if (appType == 4)
|
||||
my_dataHandler = new tdc_dataHandler (appType,
|
||||
bytesOut, ctx);
|
||||
else {
|
||||
fprintf (stderr, "DSCTy 5 with appType %d not supported\n",
|
||||
appType);
|
||||
my_dataHandler = new virtual_dataHandler();
|
||||
}
|
||||
break;
|
||||
|
||||
case 60:
|
||||
my_dataHandler = new motHandler (p -> motdata_Handler, ctx);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
running. store (true);
|
||||
assembling = false;
|
||||
}
|
||||
dataProcessor::~dataProcessor() {
|
||||
running. store (false);
|
||||
delete my_dataHandler;
|
||||
}
|
||||
|
||||
void dataProcessor::addtoFrame (uint8_t *outV) {
|
||||
// There is - obviously - some exception, that is
|
||||
// when the DG flag is on and there are no datagroups for DSCTy5
|
||||
if (!running. load ()) {
|
||||
return;
|
||||
}
|
||||
if ((this -> DSCTy == 5) &&
|
||||
(this -> DGflag)) // no datagroups
|
||||
handleTDCAsyncstream (outV, 24 * bitRate);
|
||||
else
|
||||
handlePackets (outV, 24 * bitRate);
|
||||
}
|
||||
//
|
||||
void dataProcessor::handlePackets (uint8_t *dataL, int16_t length) {
|
||||
uint8_t *data = dataL;
|
||||
while (running. load ()) {
|
||||
// pLength is in bits
|
||||
int32_t pLength = (getBits_2 (data, 0) + 1) * 24 * 8;
|
||||
if (length < pLength) // be on the safe side
|
||||
return;
|
||||
|
||||
if (!FEC_scheme)
|
||||
handlePacket (data);
|
||||
else
|
||||
handleRSPacket (data);
|
||||
//
|
||||
// prepare for the next round
|
||||
length -= pLength;
|
||||
if (length < 24) {
|
||||
return;
|
||||
}
|
||||
data = &(data [pLength]);
|
||||
}
|
||||
}
|
||||
|
||||
void dataProcessor::handlePacket (uint8_t *vec) {
|
||||
static int expected_cntidx = 0;
|
||||
uint8_t Length = (getBits (vec, 0, 2) + 1) * 24;
|
||||
if (!check_CRC_bits (vec, Length * 8)) {
|
||||
// fprintf (stderr, "crc fails %d\n", Length);
|
||||
return;
|
||||
}
|
||||
// fprintf (stderr, "packet crc OK %d\n", Length);
|
||||
|
||||
// Continuity index:
|
||||
uint8_t cntIdx = getBits (vec, 2, 2);
|
||||
// First/Last flag:
|
||||
uint8_t flflg = getBits (vec, 4, 2);
|
||||
// Packet address
|
||||
uint16_t paddr = getBits (vec, 6, 10);
|
||||
// Useful data length
|
||||
uint8_t udlen = getBits (vec, 17,7);
|
||||
if (udlen == 0)
|
||||
return;
|
||||
|
||||
if (paddr != packetAddress)
|
||||
return;
|
||||
if (cntIdx != expected_cntidx) {
|
||||
// fprintf (stderr, "packet cntIdx %d expected %d address %d\n",
|
||||
// cntIdx, expected_cntidx, paddr);
|
||||
expected_cntidx = 0;
|
||||
return;
|
||||
}
|
||||
expected_cntidx = (cntIdx + 1) % 4;
|
||||
|
||||
switch (flflg) {
|
||||
case 2: // First data group packet
|
||||
series. resize (udlen * 8);
|
||||
for (uint16_t i = 0; i < udlen * 8; i ++)
|
||||
series [i] = vec [3 * 8 + i];
|
||||
assembling = true;
|
||||
return;
|
||||
|
||||
case 0: // Intermediate data group packet
|
||||
if (assembling) {
|
||||
int currentLength = series. size ();
|
||||
if (currentLength + udlen * 8 > 4 * 8192) {
|
||||
assembling = false;
|
||||
// fprintf (stderr, "too large???\n");
|
||||
return;
|
||||
}
|
||||
series. resize (currentLength + udlen * 8);
|
||||
for (int i = 0; i < udlen * 8; i ++)
|
||||
series [currentLength + i] = vec [3 * 8 + i];
|
||||
}
|
||||
return;
|
||||
|
||||
case 1: // Last data group packet
|
||||
if (assembling) {
|
||||
int currentLength = series. size ();
|
||||
if (currentLength + udlen * 8 > 4 * 8192) {
|
||||
assembling = false;
|
||||
// fprintf (stderr, "too large???\n");
|
||||
return;
|
||||
}
|
||||
series. resize (currentLength + udlen * 8);
|
||||
for (int i = 0; i < udlen * 8; i ++)
|
||||
series [currentLength + i] = vec [3 * 8 + i];
|
||||
assembling = false;
|
||||
//
|
||||
// Note, we are sending the UNPROCESSED mscdatagroup to the
|
||||
// appropriate handler
|
||||
my_dataHandler -> add_mscDatagroup (series);
|
||||
series. resize (0);
|
||||
}
|
||||
return;
|
||||
|
||||
case 3: { // Single packet, mostly padding
|
||||
series. resize (udlen * 8);
|
||||
for (uint8_t i = 0; i < udlen * 8; i ++)
|
||||
series [i] = vec [3 * 8 + i];
|
||||
my_dataHandler -> add_mscDatagroup (series);
|
||||
series. resize (0);
|
||||
}
|
||||
return;
|
||||
|
||||
default: // cannot happen
|
||||
return;
|
||||
}
|
||||
}
|
||||
//
|
||||
// we try to ensure that when the RS packages are read in, we
|
||||
// have exactly RSDIMS * FRAMESIZE uint's read
|
||||
void dataProcessor::handleRSPacket (uint8_t *vec) {
|
||||
int32_t pLength = (getBits_2 (vec, 0) + 1) * 24;
|
||||
uint16_t address = getBits (vec, 6, 10);
|
||||
|
||||
if (!running)
|
||||
return;
|
||||
// we differentiate between the "data" packets and the "RS" packets
|
||||
// The "order" is first RSDIMS * FRAMESIZE packet elements
|
||||
// with data, next 9 * 22 bytes RS data
|
||||
if ((pLength == 24) && (address == 1022)) { // RS packet
|
||||
uint8_t counter = getBits (vec, 2, 4);
|
||||
registerFEC (vec, counter);
|
||||
if ((counter == 8) && FEC_complete ()) {
|
||||
processRS (AppVector, FECVector);
|
||||
handle_RSpackets (AppVector);
|
||||
clear_FECtable ();
|
||||
fillPointer = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// addPacket checks the size and sets fillPointer to 0 if erroneous
|
||||
fillPointer = addPacket (vec, AppVector, fillPointer);
|
||||
}
|
||||
}
|
||||
|
||||
void dataProcessor::clear_FECtable () {
|
||||
for (int i = 0; i < 9; i ++)
|
||||
FEC_table [i] = false;
|
||||
}
|
||||
//
|
||||
// addPacket basically packs the sequence of bits into a sequence
|
||||
// of bytes, for processing by the RS decoder
|
||||
// Of course, we check for overflow
|
||||
int dataProcessor::addPacket (uint8_t *vec,
|
||||
std::vector<uint8_t> &theBuffer,
|
||||
int fillPointer) {
|
||||
int16_t packetLength = (getBits_2 (vec, 0) + 1) * 24;
|
||||
// Assert theBuffer. size () == RDIMS * FRAMESIZE + 48
|
||||
if (fillPointer + packetLength > theBuffer. size ()) {
|
||||
clear_FECtable ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < packetLength; i ++) {
|
||||
uint8_t temp = 0;
|
||||
for (int j = 0; j < 8; j ++)
|
||||
temp = (temp << 1) | (vec [i * 8 + j] == 0 ? 0 : 1);
|
||||
if (fillPointer + i >= theBuffer. size ())
|
||||
fprintf (stderr, "%d is too large\n", fillPointer + i);
|
||||
else
|
||||
theBuffer [fillPointer + i] = temp;
|
||||
}
|
||||
return fillPointer + packetLength;
|
||||
}
|
||||
//
|
||||
// The output of the RS decoding is a vector with a sequence
|
||||
// of packets, first dispatch and separate the packet sequence
|
||||
// into its elements
|
||||
//
|
||||
// the package size of the last package may be oversized
|
||||
// and dilled with 0, so, if needed, adjust
|
||||
void dataProcessor::handle_RSpackets (std::vector<uint8_t> &vec) {
|
||||
for (int baseP = 0; baseP < RSDIMS * FRAMESIZE; ) {
|
||||
int16_t packetLength = (((vec [baseP] & 0xc0) >> 6) + 1) * 24;
|
||||
handle_RSpacket (&(vec. data ()) [baseP], packetLength);
|
||||
baseP += packetLength;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
uint8_t bitList [] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
|
||||
//
|
||||
// The RS data is with packed bytes, while the basis infrastructure
|
||||
// is with bit sequences, so to keep things simple, we just
|
||||
// transform the byte sequence into a bit sequence
|
||||
void dataProcessor::handle_RSpacket (uint8_t *packet, int16_t packetLength) {
|
||||
std::vector<uint8_t> bitData (packetLength * 8);
|
||||
for (int i = 0; i < packetLength; i ++) {
|
||||
uint8_t temp = packet [i];
|
||||
for (int j = 0; j < 8; j ++) {
|
||||
uint8_t theBit = (temp & bitList [j]) == 0 ? 0 : 1;
|
||||
bitData [8 * i + j] = theBit;
|
||||
}
|
||||
}
|
||||
handlePacket (bitData. data ());
|
||||
}
|
||||
//
|
||||
// as it tuns out, the FEC data packages are arriving in order,
|
||||
// so it would have been sufficient just to wait until the
|
||||
// package with counter '8' was seen
|
||||
void dataProcessor::registerFEC (uint8_t *vec, int cnt) {
|
||||
if (cnt < 0 || cnt > 8)
|
||||
return; // garbage data
|
||||
for (int i = 0; i < 22; i ++) {
|
||||
uint8_t temp = 0;
|
||||
for (int j = 0; j < 8; j ++)
|
||||
temp = (temp << 1) | vec [16 + 8 * i + j];
|
||||
FECVector [cnt * 22 + i] = temp;
|
||||
}
|
||||
FEC_table [cnt] = true;
|
||||
}
|
||||
|
||||
bool dataProcessor::FEC_complete () {
|
||||
for (int i = 0; i < 9; i ++)
|
||||
if (!FEC_table [i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
//
|
||||
//
|
||||
// Really no idea what to do here
|
||||
void dataProcessor::handleTDCAsyncstream (uint8_t *data,
|
||||
int32_t length) {
|
||||
int16_t packetLength = (getBits_2 (data, 0) + 1) * 24;
|
||||
int16_t continuityIndex = getBits_2 (data, 2);
|
||||
int16_t firstLast = getBits_2 (data, 4);
|
||||
int16_t address = getBits (data, 6, 10);
|
||||
uint16_t command = getBits_1 (data, 16);
|
||||
int16_t usefulLength = getBits_7 (data, 17);
|
||||
|
||||
(void) length;
|
||||
(void) packetLength;
|
||||
(void) continuityIndex;
|
||||
(void) firstLast;
|
||||
(void) address;
|
||||
(void) command;
|
||||
(void) usefulLength;
|
||||
if (!check_CRC_bits (data, packetLength * 8))
|
||||
return;
|
||||
}
|
||||
//
|
||||
// To keep things simple, we abstract from the rs decoding
|
||||
// by providing - as separate vectors - the RSDIMS * FRAMESIZE
|
||||
// app data values and the 9 * 22 RS data values
|
||||
// The appData vector is overwritten with the corrected data
|
||||
//
|
||||
void dataProcessor::processRS (std::vector<uint8_t> &appData,
|
||||
const std::vector<uint8_t> &RSdata) {
|
||||
static
|
||||
uint8_t table [RSDIMS][FRAMESIZE + 16];
|
||||
uint8_t rsOut [FRAMESIZE];
|
||||
if (!running. load ())
|
||||
return;
|
||||
// Assert appdata . size () == RSDIMS * FRAMESIZE + 48
|
||||
// Assert RSdata. size () == 9 * 22;
|
||||
for (int i = 0; i < RSDIMS * FRAMESIZE; i ++)
|
||||
table [i % RSDIMS][i / RSDIMS] = appData [i];
|
||||
for (int i = 0; i < (int)(RSdata. size ()); i ++)
|
||||
table [i % RSDIMS] [FRAMESIZE + i / RSDIMS] = RSdata [i];
|
||||
|
||||
for (int i = 0; i < RSDIMS; i ++) {
|
||||
int xx = my_rsDecoder. dec (table [i], rsOut, 51);
|
||||
// fprintf (stderr, "rs decoder says %d\n", xx);
|
||||
for (int j = 0; j < FRAMESIZE; j ++)
|
||||
table [i][j] = rsOut [j];
|
||||
}
|
||||
//
|
||||
// copy the table back to the vector
|
||||
for (int i = 0; i < RSDIMS * FRAMESIZE; i ++)
|
||||
appData [i] = table [i % RSDIMS][i / RSDIMS];
|
||||
}
|
||||
|
||||
|
597
library/src/backend/data/data-processor.cpp-juhl
Normal file
597
library/src/backend/data/data-processor.cpp-juhl
Normal file
@@ -0,0 +1,597 @@
|
||||
#
|
||||
/*
|
||||
* Copyright (C) 2015
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Programming
|
||||
*
|
||||
* Added Reed-Solomon-Decoder for Packet Data in 2024
|
||||
* by Stefan Juhl (stefanjuhl75@gmail.com)
|
||||
*
|
||||
* This file is part of DAB library
|
||||
* DAB library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DAB 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DAB library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "dab-constants.h"
|
||||
#include "data-processor.h"
|
||||
#include "virtual-datahandler.h"
|
||||
#include "mot-handler.h"
|
||||
#include "tdc-datahandler.h"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cctype> // Für isprint
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <algorithm> // Für std::reverse
|
||||
|
||||
|
||||
// Arrays for Reed-Solomon Data Field and Packet CRC Flag
|
||||
uint8_t rsf[12][16]; // Data Field
|
||||
//uint8_t pcf[94]; // Packet CRC Flag
|
||||
bool chkRS = false; // Check Reed-Solomon Decoder
|
||||
uint8_t maxCorruptBytes=8; // Packet CRC Flag
|
||||
uint64_t packets=0; // Global Packet Counter
|
||||
uint64_t skipped=0; // Global Packet Counter
|
||||
uint8_t cntPkt=0; // Packet Counter
|
||||
uint64_t PktSum=0; // Sum of Packet Lengths
|
||||
|
||||
// CRC calculation
|
||||
uint16_t calc_crc_bits(uint8_t *data, uint32_t size) {
|
||||
uint16_t crc = 0xFFFF; // Initial value
|
||||
const ushort generator = 0x1021; /* divisor is 16bit */
|
||||
|
||||
for (size_t byteIndex = 0; byteIndex < size; byteIndex++) {
|
||||
crc ^= (ushort(data[byteIndex]<<15));
|
||||
if ( crc & 0x8000 ) { crc = (ushort((crc<<1) ^ generator));
|
||||
} else { crc <<= 1; }
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
/* The packet CRC shall be a 16-bit CRC word calculated on
|
||||
* the packet header and the packet data field. It shall be
|
||||
* generated according to the procedure defined in annex E.
|
||||
* The generation shall be based on the polynomial
|
||||
* Recommendation ITU-T X.25 / Annex E of
|
||||
* ETSI EN 300 401 V2.1.1 (2017-01)
|
||||
*/
|
||||
uint16_t calc_crc_bytes (const std::vector<uint8_t>& outVector,
|
||||
size_t start, int16_t len) {
|
||||
uint16_t accumulator = 0xFFFF;
|
||||
uint16_t genpoly = 0x1021;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
int16_t data = outVector[start + i] << 8;
|
||||
for (int j = 8; j > 0; j--) {
|
||||
if ((data ^ accumulator) & 0x8000)
|
||||
accumulator = ((accumulator << 1) ^ genpoly) & 0xFFFF;
|
||||
else
|
||||
accumulator = (accumulator << 1) & 0xFFFF;
|
||||
data = (data << 1) & 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
// CRC-Wert berechnen
|
||||
uint16_t crc = ~accumulator & 0xFFFF;
|
||||
return crc;
|
||||
}
|
||||
|
||||
void printArrayAsHex (uint8_t* array, size_t size) {
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
// Print each byte as a two-digit hexadecimal value
|
||||
fprintf (stderr, "%02x", array[i]);
|
||||
// Optionally: print a new line after every 16 values for readability
|
||||
if ((i + 1) % 24 == 0) {
|
||||
fprintf (stderr,"\n ");
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
|
||||
// Hilfsfunktion zum Ausgeben eines Arrays als ASCII-Zeichen
|
||||
void printArrayAsASCII (const uint8_t* data, size_t length) {
|
||||
for (size_t i = 3; i < (length-5); i++) {
|
||||
if (isprint (data [i])) {
|
||||
fprintf (stderr, "%c", data [i]);
|
||||
} else {
|
||||
fprintf (stderr, "_");
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
// class dataProcessor
|
||||
// The main function of this class is to assemble the
|
||||
// MSCdatagroups and dispatch to the appropriate handler
|
||||
//
|
||||
// fragmentsize == Length * CUSize
|
||||
dataProcessor::dataProcessor (int16_t bitRate,
|
||||
packetdata *pd,
|
||||
API_struct *p,
|
||||
void *ctx):
|
||||
my_rsDecoder (8, 0435, 0, 1, 16) {
|
||||
this -> bitRate = pd -> bitRate;
|
||||
this -> DSCTy = pd -> DSCTy;
|
||||
this -> appType = pd -> appType;
|
||||
this -> packetAddress = pd -> packetAddress;
|
||||
this -> DGflag = pd -> DGflag;
|
||||
this -> FEC_scheme = pd -> FEC_scheme;
|
||||
this -> pdlen = pd -> length * 4;
|
||||
this -> bytesOut = p -> bytesOut_Handler;
|
||||
// added S. Juhl
|
||||
this -> mscQuality = p -> program_quality_Handler;
|
||||
this -> ctx = ctx;
|
||||
RSDims = 12; // mp4: 8 -> 12 rows
|
||||
|
||||
frameBytes. resize (0);
|
||||
outVector. resize (RSDims * 188);
|
||||
|
||||
blockFillIndex = 0;
|
||||
blocksInBuffer = 0;
|
||||
curMSC = 16;
|
||||
curPI = 255;
|
||||
|
||||
frameCount = 0;
|
||||
frameErrors = 0;
|
||||
rsErrors = 0;
|
||||
crcErrors = 0;
|
||||
|
||||
frame_quality = 0;
|
||||
rs_quality = 0;
|
||||
|
||||
fprintf (stderr, "** DBG: dataProcessor: appType=%d FEC=%d DSCTy=%d len=%d(", pd -> appType, FEC_scheme, pd -> DSCTy, pd->length);
|
||||
switch (DSCTy) {
|
||||
default:
|
||||
my_dataHandler = new virtual_dataHandler ();
|
||||
break;
|
||||
|
||||
case 5: // do know yet
|
||||
if (appType == 4)
|
||||
my_dataHandler = new tdc_dataHandler (appType, bytesOut, ctx);
|
||||
break;
|
||||
|
||||
case 60:
|
||||
my_dataHandler = new motHandler (p -> motdata_Handler, ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dataProcessor::~dataProcessor () {
|
||||
delete my_dataHandler;
|
||||
}
|
||||
|
||||
void dataProcessor::addtoFrame (uint8_t *outV) {
|
||||
// There is - obviously - some exception, that is
|
||||
// when the DG flag is on and there are no datagroups for DSCTy5
|
||||
|
||||
if (++frameCount >= 100) {
|
||||
frameCount = 0;
|
||||
frame_quality = 1 * (100 - frameErrors);
|
||||
rs_quality = 100 * (1 - rsErrors/12);
|
||||
if (mscQuality != nullptr)
|
||||
mscQuality (frame_quality, rs_quality, 1*(100-crcErrors), ctx);
|
||||
frameErrors = crcErrors = rsErrors = 0;
|
||||
}
|
||||
|
||||
if ((this -> DSCTy == 5) &&
|
||||
(this -> DGflag)) { // no datagroups
|
||||
handleTDCAsyncstream (outV, 24 * bitRate);
|
||||
}
|
||||
else
|
||||
if (outV == nullptr) {
|
||||
fprintf (stderr, "AddressSanitizer: Null pointer in addtoFrame\n");
|
||||
exit(22);
|
||||
}
|
||||
handlePackets (outV, 24 * bitRate); // Data group packets
|
||||
}
|
||||
|
||||
// Helper function to process RS data (8 Bit to 1 Byte)
|
||||
uint8_t dataProcessor::processRSData (uint8_t *data, uint16_t ind) {
|
||||
uint8_t temp = 0;
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
temp = (temp << 1) | (data [(ind + 2) * 8 + j] & 0x01);
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
uint8_t AppDTCnt = 0;
|
||||
//
|
||||
// While for a full mix data and audio there will be a single packet in a
|
||||
// data compartment, for an empty mix, there may be many more
|
||||
/* VERIFIED! TPEG appType=4 FEC=1 344 / ETSI TS 103 551
|
||||
TBD TPEG_MM appType=4 FEC=0 152
|
||||
VERIFIED! PPP-RTK-AdV appType=1500 FEC=1 192
|
||||
*/
|
||||
void dataProcessor::handlePackets (uint8_t *data, int16_t length) {
|
||||
// Calculate packetLength and address
|
||||
uint8_t packetLength = (getBits_2(data, 0) + 1) * 24; // 24/48/72/96 bytes
|
||||
uint16_t address = getBits(data, 6, 10); // 0 -> only for padding
|
||||
|
||||
if (packetLength > dataProcessor::pdlen)
|
||||
packetLength = dataProcessor::pdlen;
|
||||
|
||||
// Check packet for FEC address
|
||||
if (address == 1022) {
|
||||
handleRSdata (data); // Reed-Solomon protected packet
|
||||
if (dataProcessor::pdlen == packetLength)
|
||||
return;
|
||||
|
||||
// If present, process further FEC packets
|
||||
for (uint8_t cp = 1;
|
||||
cp < (dataProcessor::pdlen/packetLength); cp++) {
|
||||
// define a new pointer to the position of 2nd FEC packet
|
||||
uint8_t *dataz = data + cp * packetLength + 8;
|
||||
// Get address of the 2nd part
|
||||
address = getBits (dataz, 6, 10);
|
||||
if (address == 1022) { // another one?
|
||||
handleRSdata (dataz);
|
||||
return;
|
||||
}
|
||||
// skip the FEC packet
|
||||
data += (address != 1022)?24*8:0;
|
||||
} // end for loop
|
||||
} // end address = 1022
|
||||
else
|
||||
if (packetLength != dataProcessor::pdlen) {
|
||||
// define a new pointer for 2nd part of the packet
|
||||
uint8_t *dataz = data + 1 * packetLength + 8;
|
||||
// Get address of the 2nd part
|
||||
address = getBits (dataz, 6, 10);
|
||||
// Has to be done, BEFORE processing RS data bytes!
|
||||
handleAppData (data, packetLength - 5);
|
||||
if (address == 1022) {
|
||||
handleRSdata (dataz);
|
||||
return;
|
||||
}
|
||||
// skip the first part of the packet
|
||||
data += (address != 1022) ? 24 * 8 : 0;
|
||||
}
|
||||
|
||||
|
||||
handleAppData (data, packetLength-5);
|
||||
}
|
||||
|
||||
// Handle sequence of FEC packets and RS decoding
|
||||
void dataProcessor::handleRSdata (uint8_t *data) {
|
||||
const uint16_t RS_PACKET_SIZE = 22; // Number of bytes in each RS packet
|
||||
const uint16_t MAX_ROWS = 12; // Number of rows in RS matrix
|
||||
const bool debug = false;
|
||||
|
||||
uint16_t rs_dt_pos = 0;
|
||||
uint8_t col, row, temp = 0;
|
||||
uint8_t Counter; // = getBits_4(data, 2);
|
||||
|
||||
// Process Reed-Solomon data
|
||||
for (uint8_t j = 0; j < 1; j++ ) { // j < (DFcnt); j++) {
|
||||
Counter = getBits_4 (data, j * 24 * 8 + 2); // Counter 0..7
|
||||
if (true) {
|
||||
if (debug)
|
||||
fprintf (stderr,
|
||||
"DBG::handleRSdata:j=%d:Counter#%2d ", j, Counter);
|
||||
for (int i = 0; i < RS_PACKET_SIZE; i++) {
|
||||
temp = processRSData (data, i + 24 * j);
|
||||
// Assign to rsf matrix
|
||||
rs_dt_pos = RS_PACKET_SIZE * Counter + i;
|
||||
row = rs_dt_pos / MAX_ROWS;
|
||||
col = rs_dt_pos % MAX_ROWS;
|
||||
if (rs_dt_pos < 192) {
|
||||
rsf [col][row] = temp;
|
||||
if (debug) {
|
||||
fprintf (stderr, "%02x", temp);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (debug) {
|
||||
fprintf (stderr,
|
||||
"%s%02x", (rs_dt_pos == 192)?"|":"",temp);
|
||||
}
|
||||
if (debug && Counter < 0xff) fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply FEC when all parts are received
|
||||
if (!frameBytes. empty() && Counter >= 8)
|
||||
applyFEC ();
|
||||
}
|
||||
|
||||
// Simulate data corruption by randomly changing bytes in an array
|
||||
void corruptData(uint8_t* array, size_t arraySize, size_t numCorruptBytes) {
|
||||
if (numCorruptBytes > arraySize) {
|
||||
std::cerr << "Fehler: Anzahl der zu korrumpierenden Bytes übersteigt die Arraygröße." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Initalize random number generator
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd()); // Mersenne-Twister-Engine für Zufallszahlen
|
||||
std::uniform_int_distribution<> byteIndexDistr(0, arraySize - 1); // Zufällige Position im Array
|
||||
std::uniform_int_distribution<> byteValueDistr(0, 255); // Zufälliger Bytewert
|
||||
|
||||
// Randomly corrupt bytes
|
||||
for (size_t i = 0; i < numCorruptBytes; ++i) {
|
||||
size_t index = byteIndexDistr(gen); // Wähle zufälligen Index im Array
|
||||
//uint8_t oldValue = array[index]; // Alter Wert für Debugging
|
||||
array[index] = byteValueDistr(gen); // Setze zufälligen neuen Wert
|
||||
|
||||
/* Debug-Ausgabe, falls gewünscht
|
||||
std::cout << "Byte an Position " << index << " geändert von "
|
||||
<< static_cast<int>(oldValue) << " zu "
|
||||
<< static_cast<int>(array[index]) << std::endl;*/
|
||||
}
|
||||
}
|
||||
|
||||
#include <cstring> // Für memcmp
|
||||
#include <cstdio> // Für fprintf
|
||||
// Reed-Solomon decoder verification
|
||||
/* NOTE see -> https://scholarworks.calstate.edu/downloads/vh53wz89h
|
||||
(204, 188) REED-SOLOMON CODE ENCODER/DECODER DESIGN, SYNTHESIS, AND SIMULATION ...
|
||||
A graduate project submitted in partial fulfillment of the requirements
|
||||
For the degree of Master of Science in Electrical Engineering
|
||||
By Haoyi Zhang
|
||||
sample data table for dt[0]=1, dt[1]=2 .. dt[187]=188
|
||||
k=51 -> 0xc3e75ac28e7055ab3ff2fb9a015221de */
|
||||
void dataProcessor::verifyRSdecoder(uint8_t* rsIn, uint8_t* rseIn, uint8_t* rsOut) {
|
||||
if (memcmp(rseIn, rsOut, 188) == 0 && !chkRS) {
|
||||
fprintf(stderr,"applyFEC::verifyRSdecoder\n");
|
||||
/* fprintf(stderr,"FEC: ");
|
||||
printArrayAsHex(rsIn, 204); */
|
||||
corruptData(rsIn, 188, maxCorruptBytes); // Simulate data corruption
|
||||
/* fprintf(stderr,"COR: ");
|
||||
printArrayAsHex(rsIn, 188); */
|
||||
my_rsDecoder.dec(rsIn, rsOut, 51);
|
||||
uint8_t rsc = memcmp(rseIn, rsOut, 188);
|
||||
if (rsc == 0) {
|
||||
/* fprintf(stderr,"REC: ");
|
||||
printArrayAsHex(rsOut, 188); */
|
||||
fprintf(stderr,"-> SUCESSFULLY recovered %d corrupted bytes.\n", maxCorruptBytes);
|
||||
chkRS = true;
|
||||
} else {
|
||||
fprintf(stderr,"failed recovering %d corrupted bytes - retrying.\n", maxCorruptBytes);
|
||||
maxCorruptBytes--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply reed solomon forward error correction
|
||||
// (204, 188) Reed-Solomon code is a truncated version
|
||||
// of RS(255, 239) code. It deletes the
|
||||
// first 51 symbols of RS(255, 239) code since they are all zeros.
|
||||
void dataProcessor::applyFEC () {
|
||||
// Use dynamic memory allocation to avoid frequent stack allocation
|
||||
// Fill up Application Data Table up to 2256 bytes, tbd if necessary
|
||||
const bool debug = false;
|
||||
uint8_t* rsIn = new uint8_t[204];
|
||||
uint8_t* rsOut = new uint8_t[188];
|
||||
uint8_t* rseIn = new uint8_t[188];
|
||||
//uint8_t pcf[94]; // Packet CRC Flag
|
||||
uint8_t rsc[RSDims]; // RS Result Checker
|
||||
|
||||
uint16_t k;
|
||||
uint8_t rsek = 0, base = 0; // rse
|
||||
|
||||
if (debug)
|
||||
fprintf (stderr,
|
||||
"DBG: applyFEC on frameBytes[%ld]\n", frameBytes. size ());
|
||||
|
||||
// Helper function to fill rsIn and rseIn
|
||||
auto fillRsIn = [&](uint16_t j, uint16_t k) {
|
||||
uint32_t index = (base + j + k * RSDims) % (RSDims * 204);
|
||||
if (index < frameBytes. size ()) {
|
||||
rsIn [k] = frameBytes [index];
|
||||
if (k < 188) {
|
||||
rseIn [k] = rsIn [k];
|
||||
}
|
||||
}
|
||||
else {
|
||||
rsIn [k] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Apply forward error correction
|
||||
for (uint16_t j = 0; j < RSDims; j++) {
|
||||
if (debug)
|
||||
fprintf (stderr, "Row#%02d: ", j + 1);
|
||||
for (k = 0; k < 204; k++) {
|
||||
fillRsIn (j, k);
|
||||
if (k > 187) {
|
||||
rsIn [k] = rsf [j][k - 188]; // Copy RS Data Field
|
||||
if (debug)
|
||||
fprintf (stderr, "%02x", rsf[j][k - 188]);
|
||||
}
|
||||
}
|
||||
// braces or rsek += rse; necessary
|
||||
rsc [j] = ((my_rsDecoder.dec (rsIn, rsOut, 51) < 0) ? 1 : 0);
|
||||
if (debug)
|
||||
fprintf (stderr, " %s\n",rsc[j]?"X":"✓");
|
||||
rsek += rsc[j];
|
||||
// Copy the corrected data to outVector
|
||||
for (k = 0; k < 188; k++) {
|
||||
outVector [j + k * RSDims] = rsOut[k]; // Bypass Out[k];
|
||||
}
|
||||
}
|
||||
fprintf (stderr,
|
||||
"FEC::RSQ[<%d Err.Bytes] %.1f%% crc=%d\n",
|
||||
maxCorruptBytes, 100.0 - 100 * rsek / 12, crcErrors);
|
||||
|
||||
// Post-FEC check for efficiency of RS Decoder
|
||||
verifyRSdecoder (rsIn, rseIn, rsOut);
|
||||
|
||||
// Process packet stream
|
||||
processOutVector (outVector);
|
||||
|
||||
// Clean up and update error count
|
||||
frameBytes. resize (0);
|
||||
rsErrors += rsek;
|
||||
|
||||
// Free dynamically allocated memory
|
||||
delete[] rsIn;
|
||||
delete[] rsOut;
|
||||
delete[] rseIn;
|
||||
}
|
||||
|
||||
// convert Bytes back to bit vector
|
||||
std::vector<uint8_t> bytes_to_bits (const std::vector<uint8_t>& bytes) {
|
||||
std::vector<uint8_t> bits;
|
||||
for (uint8_t byte : bytes) {
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
bits.push_back((byte >> i) & 0x01);
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
// Function to parse FEC processed packet,
|
||||
// see section 5.3.2 of ETSI EN 300 401 V2.1.1 (2017-01)
|
||||
void dataProcessor::processOutVector (const std::vector<uint8_t>& outVector){
|
||||
const bool debug = false;
|
||||
uint16_t boi=0;
|
||||
uint8_t packet_length;
|
||||
uint8_t continuity_index;
|
||||
uint8_t packet_number = 1;
|
||||
uint8_t firstlast_flags;
|
||||
uint16_t packet_adrress;
|
||||
uint8_t command_flag;
|
||||
uint8_t useable_length;
|
||||
uint16_t packet_crc;
|
||||
uint16_t check_crc;
|
||||
uint8_t crc_chk_len;
|
||||
|
||||
while (boi < outVector. size ()) {
|
||||
// 2 bits
|
||||
packet_length = ((outVector[boi] >> 6) & 0x03) * 24 + 24;
|
||||
// 2 bits
|
||||
continuity_index = (outVector[boi] & 0x30) >> 4;
|
||||
// 2 bits
|
||||
firstlast_flags = (outVector[boi] & 0x0C) >> 2;
|
||||
// 10 bits
|
||||
packet_adrress =
|
||||
(outVector[boi] & 0x03) << 8 | outVector[boi + 1];
|
||||
// 1 bit
|
||||
command_flag = (outVector[boi + 2] & 0x80) >> 7;
|
||||
// 7 bits, <= 91
|
||||
useable_length = (outVector[boi + 2] & 0x7F);
|
||||
|
||||
// Data validity checks: packet length, useable data length,
|
||||
// continuity index
|
||||
if (packet_length > dataProcessor::pdlen)
|
||||
packet_length = dataProcessor::pdlen;
|
||||
if (useable_length == 0 || useable_length > 91 ||
|
||||
(useable_length > dataProcessor::pdlen))
|
||||
useable_length = packet_length - 5;
|
||||
|
||||
// Calculate CRC check length and perform CRC extraktion and calculation
|
||||
crc_chk_len = packet_length <= dataProcessor::pdlen ?
|
||||
packet_length - 2 : 22;
|
||||
packet_crc = ((outVector [boi + crc_chk_len ]) << 8) |
|
||||
outVector [boi + crc_chk_len + 1];
|
||||
check_crc = calc_crc_bytes (outVector, boi, crc_chk_len);
|
||||
|
||||
if (check_crc != 0x604b) { // don't care about zeroed packets ...
|
||||
// Check Sequence Continuity
|
||||
if (curPI > 4 || continuity_index == ((curPI + 1) % 4)) {
|
||||
curPI = continuity_index;
|
||||
}
|
||||
else {
|
||||
fprintf (stderr,
|
||||
"DBG::Continuity Error: %d -> %d\n",
|
||||
curPI, continuity_index);
|
||||
series. resize (0);
|
||||
curPI=0xff;
|
||||
return;
|
||||
}
|
||||
|
||||
// CRC Check
|
||||
std::vector<uint8_t> relevant_bytes (outVector.begin() + boi + 3,
|
||||
outVector.begin() + boi + 3 + useable_length);
|
||||
// Konvertieren Sie die Bytes in Bits
|
||||
std::vector<uint8_t> bits = bytes_to_bits (relevant_bytes);
|
||||
// Fügen Sie die Bits zum series-Array hinzu
|
||||
series. insert (series. end (), bits. begin (), bits. end ());
|
||||
|
||||
switch (firstlast_flags) {
|
||||
case 0: // Middle packet
|
||||
break;
|
||||
case 2: // First packet
|
||||
if (series. size () > 0) {
|
||||
// TBD, what if series is not empty?
|
||||
}
|
||||
break;
|
||||
case 1: // Last packet
|
||||
case 3: // Single packet
|
||||
my_dataHandler -> add_mscDatagroup (series);
|
||||
series. resize (0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
boi += crc_chk_len + 2;
|
||||
packet_number++;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Copy data buffer to series array and check crc of packet
|
||||
void dataProcessor::handleAppData (uint8_t *data, int16_t length) {
|
||||
uint16_t currentLength = frameBytes.size();
|
||||
uint16_t ccrc = calc_crc_bits(data, (length +5 - 2) * 8);
|
||||
uint16_t pcrc = getBits(data, (length +5 - 2) * 8, 16);
|
||||
uint8_t temp;
|
||||
const bool debug = false;
|
||||
|
||||
crcErrors += (ccrc != pcrc)?1:0;
|
||||
if (debug)
|
||||
fprintf (stderr, "DBG::handleAppData: length=%d -> |",length);
|
||||
|
||||
frameBytes. resize (currentLength + (length + 5));
|
||||
for (uint8_t i = 0; i < (length + 5); i++ ) {
|
||||
temp = 0;
|
||||
for (uint8_t bit = 0; bit < 8; ++bit) {
|
||||
temp |= (data[i * 8 + bit] & 1) << (7 - bit);
|
||||
}
|
||||
|
||||
frameBytes [currentLength + i] = temp;
|
||||
if (debug) {
|
||||
fprintf (stderr, "%02x", temp);
|
||||
if (i == length + 2) {
|
||||
fprintf (stderr, "|");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (debug)
|
||||
fprintf (stderr, "|%s\n", (ccrc!=pcrc)?"X":"");
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Really no idea what to do here
|
||||
void dataProcessor::handleTDCAsyncstream (uint8_t *data, int16_t length) {
|
||||
int16_t packetLength = (getBits_2 (data, 0) + 1) * 24;
|
||||
int16_t continuityIndex = getBits_2 (data, 2);
|
||||
int16_t firstLast = getBits_2 (data, 4);
|
||||
int16_t address = getBits (data, 6, 10);
|
||||
uint16_t command = getBits_1 (data, 16);
|
||||
int16_t usefulLength = getBits_7 (data, 17);
|
||||
fprintf (stderr, "DBG: dataProcessor::handleTDCAsyncstream\n");
|
||||
(void) length;
|
||||
(void) packetLength;
|
||||
(void) continuityIndex;
|
||||
(void) firstLast;
|
||||
(void) address;
|
||||
(void) command;
|
||||
(void) usefulLength;
|
||||
if (!check_CRC_bits (data, packetLength * 8))
|
||||
return;
|
||||
}
|
@@ -29,174 +29,79 @@
|
||||
this -> ctx = ctx;
|
||||
}
|
||||
|
||||
tdc_dataHandler::~tdc_dataHandler (void) {
|
||||
tdc_dataHandler::~tdc_dataHandler () {
|
||||
}
|
||||
|
||||
// CRC calculation
|
||||
uint16_t calc_crc_bitts(uint8_t *data, uint32_t size) {
|
||||
uint16_t crc = 0xFFFF; // Initial value
|
||||
const ushort generator = 0x1021; /* divisor is 16bit */
|
||||
|
||||
for (size_t byteIndex = 0; byteIndex < size; byteIndex++) {
|
||||
crc ^= (ushort(data[byteIndex]<<15));
|
||||
if ( crc & 0x8000 ) { crc = (ushort((crc<<1) ^ generator));
|
||||
} else { crc <<= 1; }
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
/* General information regarding the data format can be found
|
||||
a) EN 300 401 - V2.1.1 - Radio Broadcasting Systems; Digital Audio Broadcasting (DAB) to mobile, portable and fixed receivers
|
||||
b) https://www.imperial.ac.uk/media/imperial-college/research-centres-and-groups/centre-for-transport-studies/seminars/2009/TPEG---an-introduction-to-the-growing-family-of-standards-and-specifications-for-Traffic-and-Travel-Information-services.pdf
|
||||
c) DIN CEN ISO/TS 18234-2:2014-07 Intelligent transport systems -- Traffic and travel information (TTI) via transport protocol experts group, generation 2 (TPEG2) -- Part 2: UML model for TPEG2 UML
|
||||
*/
|
||||
/* ETSI TS 101 756 table 16 -> reference 6.3.6 of ETSI EN 300 401
|
||||
User App Type = 4 -> ETSI TS 103 551 [8] -> TPEG
|
||||
https://www.etsi.org/deliver/etsi_ts/103500_103599/103551/01.01.01_60/ts_103551v010101p.pdf
|
||||
The MSC data groups shall be transported using the TDC in a packet mode service component with
|
||||
data groups, as specified in ETSI TS 101 759 [3], clause 4.1.2.
|
||||
|
||||
*/
|
||||
/* TPEG end sequenz ff0f|0006|579e|00|01 41 0f |f7 |a8d0 <-- indicator for TPEG?
|
||||
SYNC|LEN |CRC |FT|SID-A/B/C|ENC|????
|
||||
Sync Word: dient dem Decoder zur Erkennung einer neuen Nachricht.
|
||||
Dieses Sync Word hat immer den Wert FF0F hex.
|
||||
Field Length: Gesamtlänge des Services Frames in Bytes. Ein Service Frame kann somit
|
||||
nicht größer als 65535 Bytes sein.
|
||||
Frame Type: sorgt für die weiter oben besprochene Unterscheidung zwischen
|
||||
„Stream directory information“ und „Conventional service frame data“.
|
||||
Header CRC: Prüfsumme um die Korrektheit der Headerdaten sicherzustellen. Diese Summe wird anhand
|
||||
der Felder Sync Word, Field Length, Frame Type und der ersten 11 Byte des Service Frames berechnet.
|
||||
Nähere Informationen zu dieser Berechnung finden sich in [TPEG2].
|
||||
Service Frame: enthält die Nutzdaten (evtl. in verschlüsselter Form) sowie die Service Identifier.
|
||||
Über die Service Identifier (SID) kann ein Contentprovider eindeutig identifiziert werden.
|
||||
Das Service Frame wird wiederum in folgende Bestandteile unterteilt:
|
||||
SID-A bis C: ergeben zusammen eine eindeutige Identifikationsnummer eines Service Providers,
|
||||
vergleichbar mit einer IP-Adresse, z. B. 133.168.123.
|
||||
Encryption-
|
||||
Indikator: spezifiziert anhand eines Wertes zwischen 0 und 255 eine Verschlüsselungsmethode.
|
||||
Die Werte 0 bis 127 sind dabei für standardisierte Methoden vorbehalten.
|
||||
128–255 sind für die freie Verwendung durch einen Service Provider vorgesehen.
|
||||
Die Verschlüsselung kann z. B. genutzt werden, um kostenpflichtige Dienste zu entwickeln.
|
||||
Auch wäre die Verwendung verschlüsselter Nachrichten evtl. bei sicherheitskritischen Anwendungen,
|
||||
wie z. B. Polizei- oder Militärfunk nutzbar.
|
||||
Component
|
||||
Multiplex: Dieser evtl. verschlüsselte Datenbereich enthält dann die eigentlichen TPEG-Nachrichten,
|
||||
Solange die Maximalgröße von 65531 Bytes nicht überschritten wird, kann dieser Bereich
|
||||
mehrere Nachrichten aufnehmen. Die Kodierung dieser Daten ist der Spezifikation zu entnehmen.
|
||||
*/
|
||||
void tdc_dataHandler::add_mscDatagroup (std::vector<uint8_t> m) {
|
||||
int32_t offset = 0;
|
||||
uint8_t *data = (uint8_t *)(m. data ());
|
||||
int32_t size = m. size ();
|
||||
int16_t i;
|
||||
|
||||
// Get a brief overview of the input data
|
||||
fprintf(stderr,"DBG::tdc_dataHandler[%d]:", size/8);
|
||||
uint8_t crcflg = getBits (data, 1, 1); // CRC presence flag
|
||||
|
||||
// Add MSC Data Group header processing
|
||||
uint8_t extflg = getBits (data, 0, 1); // Extension Field Present Flag (see ETSI EN 300 401 - MSC data group header)
|
||||
uint8_t crcflg = getBits (data, 1, 1); // CRC presence flag
|
||||
uint8_t segfld = getBits (data, 2, 1); // Segment field presence flag
|
||||
uint8_t uacfld = getBits (data, 3, 1); // User Access field precence
|
||||
uint8_t dgtype = getBits (data, 4, 4); // Data group type
|
||||
uint8_t cntidx = getBits (data, 8, 4); // Continuity index
|
||||
uint8_t repidx = getBits (data, 12, 4); // Repetition index
|
||||
uint16_t extfld = (extflg==1)?getBits (data, 16, 16):0; // Extension field
|
||||
uint8_t lastflg = (segfld==1)?getBits (data, 16+extfld*16, 1):0; // Last segment flag
|
||||
uint8_t segidx = (segfld==1)?getBits (data, 17+extfld*16, 7):0; // Segment number
|
||||
uint8_t rfa = (segfld==1)?getBits (data, 24+extfld*16, 3):0; // Reserved for future use
|
||||
uint8_t tidflg = (segfld==1)?getBits (data, 27+extfld*16, 1):0; // Transport Identifier flag
|
||||
uint8_t tidlen = (segfld==1)?getBits (data, 28+extfld*16, 4):0; // Field length of Transport Identifier and End User Address
|
||||
uint16_t tid = (segfld==1)?getBits (data, 32+extfld*16, 16):0; // Transport Identifier
|
||||
uint8_t dfoff = 2 * 8 * (extflg + segfld); // Data field offset
|
||||
dfoff += uacfld * (tidlen+1) *8 ; // inlcude User Access Field, if present
|
||||
fprintf(stderr, "MSCDG: ext=%d crc=%d seg=%d uac=%d dgt=%d cnt=%d rep=%d last=%d seg=%d dfoff=%d crc=", extfld, crcflg, segfld, uacfld, dgtype, cntidx, repidx,lastflg,segidx,dfoff);
|
||||
|
||||
// Check CRC of data group
|
||||
uint16_t dgcrc=0, chkcrc=1;
|
||||
if (crcflg==1) {
|
||||
dgcrc = getBits (data, size-16, 16);
|
||||
chkcrc = calc_crc_bitts(data, size-16);
|
||||
fprintf(stderr,"%s ",dgcrc==chkcrc?"✓":"X");
|
||||
} else { fprintf(stderr,"- "); }
|
||||
//
|
||||
// if the crc flag is ON, check the CRC of the datagroup
|
||||
if (crcflg != 0) {
|
||||
if (check_CRC_bits (data, size - 16))
|
||||
fprintf (stderr, "msc crc is OK\n");
|
||||
}
|
||||
|
||||
// we maintain offsets in bits, the "m" array has one bit per byte
|
||||
while (offset < size) {
|
||||
while (offset + 16 < size) {
|
||||
if (getBits (data, offset, 16) == 0xFF0F) {
|
||||
break;
|
||||
}
|
||||
else
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
// we maintain offsets in bits, the "m" array has one bit per byte
|
||||
while (offset < size) {
|
||||
while (offset + 16 < size) {
|
||||
if (getBits (data, offset, 16) == 0xFF0F) {
|
||||
break;
|
||||
}
|
||||
else
|
||||
offset += 8;
|
||||
}
|
||||
if (offset + 16 >= size) {
|
||||
// NO syncword found! -> Output anyway, if CRC of data group is OK
|
||||
if (crcflg==1 && dgcrc==chkcrc) {
|
||||
uint16_t length = size/8-((crcflg==1)?4:2)-dfoff/8;
|
||||
fprintf(stderr,"RAW off=%d len=%d\n",dfoff,length); // (size-(crcflg==1)?32:16));
|
||||
#ifdef _MSC_VER
|
||||
uint8_t *buffer = (uint8_t *)_alloca(length);
|
||||
#else
|
||||
uint8_t buffer[length];
|
||||
#endif
|
||||
if (bytesOut != nullptr)
|
||||
bytesOut (buffer, length, 2, ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (offset + 16 >= size)
|
||||
return;
|
||||
|
||||
// we have a syncword
|
||||
// uint16_t syncword = getBits (data, offset, 16);
|
||||
uint16_t length = getBits (data, offset + 16, 16);
|
||||
// uint16_t crc = getBits (data, offset + 32, 16);
|
||||
// we have a syncword
|
||||
// uint16_t syncword = getBits (data, offset, 16);
|
||||
int16_t length = getBits (data, offset + 16, 16);
|
||||
// uint16_t crc = getBits (data, offset + 32, 16);
|
||||
|
||||
uint8_t frametypeIndicator = getBits (data, offset + 6 * 8, 8);
|
||||
if ((length < 0) || (length >= (size - offset) / 8))
|
||||
return; // garbage
|
||||
uint8_t frametypeIndicator =
|
||||
getBits (data, offset + 48, 8);
|
||||
if ((length < 0) || (length >= (size - offset) / 8))
|
||||
return; // garbage
|
||||
//
|
||||
// OK, prepare to check the crc
|
||||
uint8_t checkVector [18];
|
||||
|
||||
// first the syncword and the length
|
||||
for (uint16_t i = 0; i < 4; i ++)
|
||||
checkVector [i] = getBits (data, offset + i * 8, 8);
|
||||
//
|
||||
// we skip the crc in the incoming data and take the frametype
|
||||
checkVector [4] = getBits (data, offset + 6 * 8, 8);
|
||||
|
||||
uint8_t sida = getBits (data, offset + 7 * 8, 8);
|
||||
uint8_t sidb = getBits (data, offset + 8 * 8, 8);
|
||||
uint8_t sidc = getBits (data, offset + 9 * 8, 8);
|
||||
fprintf(stderr,"TPEG Frame%d SID=%d.%d.%d ",frametypeIndicator,sida,sidb,sidc);
|
||||
|
||||
if (length >= 11) {
|
||||
// OK, prepare to check the crc
|
||||
uint8_t checkVector [18];
|
||||
|
||||
// first the syncword and the length
|
||||
for (i = 0; i < 4; i ++) {
|
||||
checkVector [i] = getBits (data, offset + i * 8, 8);
|
||||
}
|
||||
int fsize = length < 11 ? length : 11;
|
||||
for (uint16_t i = 0; i < fsize; i ++)
|
||||
checkVector [5 + i] = getBits (data, offset + 7 * 8 + i * 8, 8);
|
||||
checkVector [5 + fsize] = getBits (data, offset + 4 * 8, 8);
|
||||
checkVector [5 + fsize + 1] =
|
||||
getBits (data, offset + 5 * 8, 8);
|
||||
|
||||
// we skip the crc in the incoming data and take the frametype
|
||||
checkVector [4] = getBits (data, offset + 6 * 8, 8);
|
||||
|
||||
int size = length < 11 ? length : 11;
|
||||
for (i = 0; i < size; i ++) {
|
||||
checkVector [5 + i] = getBits (data, offset + 7 * 8 + i * 8, 8);
|
||||
}
|
||||
checkVector [5 + size] = getBits (data, offset + 4 * 8, 8);
|
||||
checkVector [5 + size + 1] = getBits (data, offset + 5 * 8, 8);
|
||||
|
||||
if (check_crc_bytes (checkVector, 5 + size ) == 0) {
|
||||
fprintf (stderr, "crc=X");
|
||||
return;
|
||||
} else { fprintf (stderr, "crc=✓"); }
|
||||
} fprintf(stderr,"\n");
|
||||
if (!check_crc_bytes (checkVector, 5 + fsize)){
|
||||
fprintf (stderr, "crc failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr,"DBG::tdc_dataHandler::FrameType=%d off=%d len=%d\n",frametypeIndicator,offset+7*8,length);
|
||||
if (frametypeIndicator == 0)
|
||||
offset = handleFrame_type_0 (data, offset + 7 * 8, length);
|
||||
else
|
||||
if (frametypeIndicator == 1)
|
||||
offset = handleFrame_type_1 (data, offset + 7 * 8, length);
|
||||
else
|
||||
return; // failure
|
||||
// fprintf (stderr, "offset is now %d\n", offset);
|
||||
if (offset < 0)
|
||||
return;
|
||||
}
|
||||
// fprintf (stderr, "frametype %d\n", frametypeIndicator);
|
||||
if (frametypeIndicator == 0)
|
||||
offset = handleFrame_type_0 (data, offset + 7 * 8, length);
|
||||
else
|
||||
if (frametypeIndicator == 1)
|
||||
offset = handleFrame_type_1 (data, offset + 7 * 8, length);
|
||||
else
|
||||
return; // failure
|
||||
// fprintf (stderr, "offset is now %d\n", offset);
|
||||
if (offset < 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t tdc_dataHandler::handleFrame_type_0 (uint8_t *data,
|
||||
@@ -210,9 +115,9 @@ uint8_t buffer [length];
|
||||
#endif
|
||||
|
||||
(void)noS;
|
||||
for (i = 0; i < length; i ++)
|
||||
for (i = 0; i < length; i ++)
|
||||
buffer [i] = getBits (data, offset + i * 8, 8);
|
||||
|
||||
|
||||
if (bytesOut != nullptr)
|
||||
bytesOut (buffer, length, 0, ctx);
|
||||
return offset + length * 8;
|
||||
@@ -220,18 +125,37 @@ uint8_t buffer [length];
|
||||
|
||||
int32_t tdc_dataHandler::handleFrame_type_1 (uint8_t *data,
|
||||
int32_t offset, int32_t length) {
|
||||
int16_t i;
|
||||
#ifdef _MSC_VER
|
||||
uint8_t *buffer = (uint8_t *)_alloca(length);
|
||||
#else
|
||||
uint8_t buffer [length];
|
||||
#endif
|
||||
int lOffset;
|
||||
int llengths = length - 4;
|
||||
|
||||
for (i = 0; i < length; i ++)
|
||||
for (int i = 0; i < length; i ++)
|
||||
buffer [i] = getBits (data, offset + i * 8, 8);
|
||||
|
||||
if (bytesOut != nullptr)
|
||||
bytesOut (buffer, length, 1, ctx);
|
||||
|
||||
if (getBits (data, offset + 24, 8) == 0) { // no encryption
|
||||
lOffset = offset + 4 * 8;
|
||||
do {
|
||||
int compInd = getBits (data, lOffset, 8);
|
||||
int flength = getBits (data, lOffset + 8, 16);
|
||||
// int crc = getBits (data, lOffset + 3 * 8, 8);
|
||||
//#if 0
|
||||
fprintf (stderr, "segment %d, length %d\n",
|
||||
compInd, flength);
|
||||
for (int i = 5; i < flength; i ++)
|
||||
fprintf (stderr, "%c", buffer [i]);
|
||||
fprintf (stderr, "\n");
|
||||
//#endif
|
||||
lOffset += (flength + 5) * 8;
|
||||
llengths -= flength + 5;
|
||||
} while (llengths > 10);
|
||||
}
|
||||
return offset + length * 8;
|
||||
}
|
||||
|
||||
|
281
library/src/backend/data/tdc-datahandler.cpp-juhl
Normal file
281
library/src/backend/data/tdc-datahandler.cpp-juhl
Normal file
@@ -0,0 +1,281 @@
|
||||
#
|
||||
/*
|
||||
* Copyright (C) 2015 .. 2017
|
||||
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
||||
* Lazy Chair Computing
|
||||
*
|
||||
* This file is part of the DAB-library
|
||||
* DAB-library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* DAB-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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with DAB-library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "tdc-datahandler.h"
|
||||
|
||||
tdc_dataHandler::tdc_dataHandler (int16_t appType,
|
||||
bytesOut_t bytesOut,
|
||||
void *ctx) {
|
||||
this -> bytesOut = bytesOut;
|
||||
this -> ctx = ctx;
|
||||
}
|
||||
|
||||
tdc_dataHandler::~tdc_dataHandler () {
|
||||
}
|
||||
|
||||
// CRC calculation
|
||||
static
|
||||
uint16_t calc_crc_bitts (uint8_t *data, uint32_t size) {
|
||||
uint16_t crc = 0xFFFF; // Initial value
|
||||
const ushort generator = 0x1021; /* divisor is 16bit */
|
||||
|
||||
for (size_t byteIndex = 0; byteIndex < size; byteIndex++) {
|
||||
crc ^= (ushort (data [byteIndex] << 15));
|
||||
if (crc & 0x8000 ) {
|
||||
crc = (ushort((crc<<1) ^ generator));
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
/* General information regarding the data format can be found
|
||||
* a) EN 300 401 - V2.1.1 - Radio Broadcasting Systems;
|
||||
* Digital Audio Broadcasting (DAB) to
|
||||
* mobile, portable and fixed receivers
|
||||
* b) https://www.imperial.ac.uk/media/imperial-college/research-centres-and-groups/centre-for-transport-studies/seminars/2009/TPEG---an-introduction-to-the-growing-family-of-standards-and-specifications-for-Traffic-and-Travel-Information-services.pdf
|
||||
* c) DIN CEN ISO/TS 18234-2:2014-07 Intelligent transport systems --
|
||||
* Traffic and travel information (TTI) via transport protocol
|
||||
* experts group, generation 2 (TPEG2) --
|
||||
* Part 2: UML model for TPEG2 UML
|
||||
*/
|
||||
void tdc_dataHandler::add_mscDatagroup (std::vector<uint8_t> m) {
|
||||
int32_t offset = 0;
|
||||
uint8_t *data = (uint8_t *)(m. data ());
|
||||
int32_t size = m. size ();
|
||||
int16_t i;
|
||||
|
||||
// Get a brief overview of the input data
|
||||
// fprintf(stderr,"DBG::tdc_dataHandler[%d]:", size/8);
|
||||
|
||||
// Add MSC Data Group header processing
|
||||
// Extension Field Present Flag
|
||||
uint8_t extflg = getBits (data, 0, 1);
|
||||
uint8_t crcflg = getBits (data, 1, 1); // CRC presence flag
|
||||
uint8_t segfld = getBits (data, 2, 1); // Segment field presence flag
|
||||
uint8_t uacfld = getBits (data, 3, 1); // User Access field precence
|
||||
uint8_t dgtype = getBits (data, 4, 4); // Data group type
|
||||
uint8_t cntidx = getBits (data, 8, 4); // Continuity index
|
||||
uint8_t repidx = getBits (data, 12, 4); // Repetition index
|
||||
// Extension field:
|
||||
uint16_t extfld = (extflg == 1) ? getBits (data, 16, 16): 0;
|
||||
// Last segment flag
|
||||
uint8_t lastflg = (segfld == 1) ? getBits (data, 16 + extfld * 16, 1) : 0;
|
||||
// Segment number :
|
||||
uint8_t segidx = (segfld == 1) ? getBits (data, 17 + extfld * 16, 7) : 0;
|
||||
// Reserved for future use:
|
||||
uint8_t rfa = (segfld == 1) ? getBits (data, 24 + extfld * 16, 3) : 0;
|
||||
// Transport Identifier flag:
|
||||
uint8_t tidflg = (segfld == 1) ? getBits (data, 27 + extfld * 16, 1) : 0;
|
||||
// Field length of Transport Identifier and End User Address:
|
||||
uint8_t tidlen = (segfld == 1) ? getBits (data, 28 + extfld * 16, 4) : 0;
|
||||
// Transport Identifier:
|
||||
uint16_t tid = (segfld == 1) ? getBits (data, 32 + extfld * 16, 16) : 0;
|
||||
// Data field offset
|
||||
uint8_t dfoff = 2 * 8 * (extflg + segfld);
|
||||
// inlcude User Access Field, if present
|
||||
dfoff += uacfld * (tidlen+1) *8 ;
|
||||
|
||||
fprintf (stderr, "MSCDG: ext=%d crc=%d seg=%d uac=%d dgt=%d cnt=%d rep=%d last=%d seg=%d dfoff=%d crc=",
|
||||
extfld, crcflg, segfld, uacfld,
|
||||
dgtype, cntidx, repidx,lastflg,segidx,dfoff);
|
||||
|
||||
// Check CRC of data group
|
||||
uint16_t dgcrc=0, chkcrc=1;
|
||||
if (crcflg==1) {
|
||||
dgcrc = getBits (data, size - 16, 16);
|
||||
chkcrc = calc_crc_bitts (data, size - 16);
|
||||
fprintf (stderr, "%s ", dgcrc == chkcrc?"✓":"X");
|
||||
}
|
||||
else {
|
||||
fprintf (stderr,"- ");
|
||||
}
|
||||
|
||||
// we maintain offsets in bits, the "m" array has one bit per byte
|
||||
while (offset < size) {
|
||||
while (offset + 16 < size) {
|
||||
if (getBits (data, offset, 16) == 0xFF0F) {
|
||||
break;
|
||||
}
|
||||
else
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
if (offset + 16 >= size) {
|
||||
// NO syncword found! -> Output anyway, if CRC of data group is OK
|
||||
if (crcflg == 1 && dgcrc == chkcrc) {
|
||||
uint16_t length = size/8-((crcflg==1)?4:2)-dfoff/8;
|
||||
// (size-(crcflg==1)?32:16));
|
||||
fprintf (stderr, "RAW off=%d len=%d\n", dfoff, length);
|
||||
#ifdef _MSC_VER
|
||||
uint8_t *buffer = (uint8_t *)_alloca(length);
|
||||
#else
|
||||
uint8_t buffer[length];
|
||||
#endif
|
||||
if (bytesOut != nullptr)
|
||||
bytesOut (buffer, length, 2, ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf (stderr, "syncword found\n");
|
||||
// we have a syncword
|
||||
// uint16_t syncword = getBits (data, offset, 16);
|
||||
uint16_t length = getBits (data, offset + 16, 16);
|
||||
// uint16_t crc = getBits (data, offset + 32, 16);
|
||||
|
||||
uint8_t frametypeIndicator = getBits (data, offset + 6 * 8, 8);
|
||||
fprintf (stderr, "frametype %d\n", frametypeIndicator);
|
||||
if ((length < 0) || (length >= (size - offset) / 8))
|
||||
return; // garbage
|
||||
|
||||
uint8_t sida = getBits (data, offset + 7 * 8, 8);
|
||||
uint8_t sidb = getBits (data, offset + 8 * 8, 8);
|
||||
uint8_t sidc = getBits (data, offset + 9 * 8, 8);
|
||||
fprintf (stderr, "TPEG Frame%d SID=%d.%d.%d ",
|
||||
frametypeIndicator, sida ,sidb, sidc);
|
||||
|
||||
if (length >= 11) {
|
||||
// OK, prepare to check the crc
|
||||
uint8_t checkVector [18];
|
||||
|
||||
// first the syncword and the length
|
||||
for (i = 0; i < 4; i ++) {
|
||||
checkVector [i] = getBits (data, offset + i * 8, 8);
|
||||
}
|
||||
|
||||
// we skip the crc in the incoming data and take the frametype
|
||||
checkVector [4] = getBits (data, offset + 6 * 8, 8);
|
||||
|
||||
int size = length < 11 ? length : 11;
|
||||
for (i = 0; i < size; i ++)
|
||||
checkVector [5 + i] =
|
||||
getBits (data, offset + 7 * 8 + i * 8, 8);
|
||||
|
||||
checkVector [5 + size] = getBits (data, offset + 4 * 8, 8);
|
||||
checkVector [5 + size + 1] = getBits (data, offset + 5 * 8, 8);
|
||||
|
||||
if (!check_crc_bytes (checkVector, 5 + size )) {
|
||||
fprintf (stderr, "crc failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf (stderr,
|
||||
"DBG::tdc_dataHandler::FrameType=%d off=%d len=%d\n",
|
||||
frametypeIndicator, offset + 7 * 8, length);
|
||||
if (frametypeIndicator == 0)
|
||||
offset = handleFrame_type_0 (data, offset + 7 * 8, length);
|
||||
else
|
||||
if (frametypeIndicator == 1)
|
||||
offset = handleFrame_type_1 (data, offset + 7 * 8, length);
|
||||
else
|
||||
return; // failure
|
||||
if (offset < 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t tdc_dataHandler::handleFrame_type_0 (uint8_t *data,
|
||||
int32_t offset, int32_t length) {
|
||||
//int16_t noS = getBits (data, offset, 8);
|
||||
#ifdef _MSC_VER
|
||||
uint8_t *buffer = (uint8_t *)_alloca (length);
|
||||
#else
|
||||
uint8_t buffer [length];
|
||||
#endif
|
||||
|
||||
for (uint16_t i = 0; i < length; i ++)
|
||||
buffer [i] = getBits (data, offset + i * 8, 8);
|
||||
|
||||
if (bytesOut != nullptr)
|
||||
bytesOut (buffer, length, 0, ctx);
|
||||
return offset + length * 8;
|
||||
}
|
||||
|
||||
int32_t tdc_dataHandler::handleFrame_type_1 (uint8_t *data,
|
||||
int32_t offset, int32_t length) {
|
||||
#ifdef _MSC_VER
|
||||
uint8_t *buffer = (uint8_t *)_alloca (length);
|
||||
#else
|
||||
uint8_t buffer [length];
|
||||
#endif
|
||||
int lOffset;
|
||||
int llengths = length - 4;
|
||||
|
||||
for (uint16_t i = 0; i < length; i ++)
|
||||
buffer [i] = getBits (data, offset + i * 8, 8);
|
||||
|
||||
if (bytesOut != nullptr)
|
||||
bytesOut (buffer, length, 1, ctx);
|
||||
|
||||
if (getBits (data, offset + 24, 8) == 0) { // no encryption
|
||||
lOffset = offset + 4 * 8;
|
||||
do {
|
||||
int compInd = getBits (data, lOffset, 8);
|
||||
int flength = getBits (data, lOffset + 8, 16);
|
||||
// int crc = getBits (data, lOffset + 3 * 8, 8);
|
||||
//#if 0
|
||||
fprintf (stderr, "segment %d, length %d\n",
|
||||
compInd, flength);
|
||||
for (int i = 5; i < flength; i ++)
|
||||
fprintf (stderr, "%c", buffer [i]);
|
||||
fprintf (stderr, "\n");
|
||||
//#endif
|
||||
lOffset += (flength + 5) * 8;
|
||||
llengths -= flength + 5;
|
||||
} while (llengths > 10);
|
||||
}
|
||||
// bytesOut (1, length, 1, ctx);
|
||||
return offset + length * 8;
|
||||
}
|
||||
|
||||
// The component header CRC is two bytes long,
|
||||
// and based on the ITU-T polynomial x^16 + x*12 + x^5 + 1.
|
||||
// The component header CRC is calculated from the service component
|
||||
// identifier, the field length and the first 13 bytes of the
|
||||
// component data. In the case of component data shorter
|
||||
// than 13 bytes, the component identifier, the field
|
||||
// length and all component data shall be taken into account.
|
||||
bool tdc_dataHandler::serviceComponentFrameheaderCRC (uint8_t *data,
|
||||
int16_t offset,
|
||||
int16_t maxL) {
|
||||
uint8_t testVector [18];
|
||||
int16_t i;
|
||||
int16_t length = getBits (data, offset + 8, 16);
|
||||
int16_t size = length < 13 ? length : 13;
|
||||
uint16_t crc;
|
||||
if (length < 0)
|
||||
return false; // assumed garbage
|
||||
crc = getBits (data, offset + 24, 16); // the crc
|
||||
testVector [0] = getBits (data, offset + 0, 8);
|
||||
testVector [1] = getBits (data, offset + 8, 8);
|
||||
testVector [2] = getBits (data, offset + 16, 8);
|
||||
for (i = 0; i < size; i ++)
|
||||
testVector [3 + i] = getBits (data, offset + 40 + i * 8, 8);
|
||||
testVector [3 + size ] = getBits (data, offset + 24, 8);
|
||||
testVector [3 + size + 1] = getBits (data, offset + 32, 8);
|
||||
(void)crc;
|
||||
return check_crc_bytes (testVector, 5 + size) == 0;
|
||||
}
|
||||
|
@@ -78,8 +78,8 @@ int i, j, root, iprim;
|
||||
generator [i] = myGalois. poly2power (generator [i]);
|
||||
}
|
||||
|
||||
reedSolomon::~reedSolomon (void) {
|
||||
delete generator;
|
||||
reedSolomon::~reedSolomon () {
|
||||
delete [] generator;
|
||||
}
|
||||
|
||||
//
|
||||
|
@@ -1468,7 +1468,7 @@ serviceId *selectedService;
|
||||
d -> DGflag = ServiceComps [j]. DGflag;
|
||||
d -> packetAddress = ServiceComps [j]. packetAddress;
|
||||
d -> appType = ServiceComps [j]. appType;
|
||||
d -> defined = (ServiceComps [j]. DSCTy != 0)?true:false;
|
||||
d -> defined = true;
|
||||
break;
|
||||
}
|
||||
fibLocker. unlock ();
|
||||
@@ -1515,7 +1515,7 @@ serviceId *selectedService;
|
||||
d -> ASCTy = ServiceComps [j]. ASCTy;
|
||||
d -> language = selectedService -> language;
|
||||
d -> programType = selectedService -> programType;
|
||||
d -> defined = (ServiceComps [j]. DSCTy != 0)?true:false;
|
||||
d -> defined = true;
|
||||
break;
|
||||
}
|
||||
fibLocker. unlock ();
|
||||
|
@@ -83,8 +83,8 @@ int16_t i;
|
||||
}
|
||||
//
|
||||
//
|
||||
interLeaver::~interLeaver (void) {
|
||||
delete permTable;
|
||||
interLeaver::~interLeaver () {
|
||||
delete [] permTable;
|
||||
}
|
||||
//
|
||||
// according to the standard, the map is a function from
|
||||
|
Reference in New Issue
Block a user