1
0
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:
Jan
2024-09-28 19:43:59 +02:00
parent 3981d584c0
commit 6e88fccde1
28 changed files with 2266 additions and 1194 deletions

View File

@@ -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.
![dab scanner with sdrplay input](/dab-scanner/dab-scanner.png?raw=true)

View File

@@ -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);
}

View File

@@ -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);
};

View File

@@ -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;

View File

@@ -365,6 +365,6 @@ closeAPI:
void sdrplayHandler_v3::setup_xmlDump () {
}
void sdrplayHandler_v3::close_mlDump () {
void sdrplayHandler_v3::close_xmlDump () {
}

View File

@@ -28,6 +28,7 @@
#include <time.h>
#include <cstring>
#include "wavfiles.h"
#include "device-exceptions.h"
static inline
int64_t getMyTime (void) {

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
};

View 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>);
};

View File

@@ -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

View 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;
};

View 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

View File

@@ -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;

View File

@@ -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 ();
}

View 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

View 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];
}

View 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;
}

View File

@@ -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.
128255 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;
}

View 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;
}

View File

@@ -78,8 +78,8 @@ int i, j, root, iprim;
generator [i] = myGalois. poly2power (generator [i]);
}
reedSolomon::~reedSolomon (void) {
delete generator;
reedSolomon::~reedSolomon () {
delete [] generator;
}
//

View File

@@ -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 ();

View File

@@ -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