1
0
mirror of https://github.com/JvanKatwijk/qt-dab.git synced 2025-10-06 16:22:41 +02:00
Files
SDR-DAB_Qt-DAB/src/backend/data/pad-handler.cpp

430 lines
13 KiB
C++
Raw Normal View History

2017-02-06 14:09:08 +01:00
#
/*
2017-03-23 18:32:25 +01:00
* Copyright (C) 2015 .. 2017
2017-02-06 14:09:08 +01:00
* Jan van Katwijk (J.vanKatwijk@gmail.com)
* Lazy Chair Computing
*
2017-03-23 18:32:25 +01:00
* This file is part of the Qt-DAB
* Qt-DAB is free software; you can redistribute it and/or modify
2017-02-06 14:09:08 +01:00
* 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.
*
2017-03-23 18:32:25 +01:00
* Qt-DAB is distributed in the hope that it will be useful,
2017-02-06 14:09:08 +01:00
* 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
2017-03-28 18:40:11 +02:00
* along with Qt-DAB; if not, write to the Free Software
2017-02-06 14:09:08 +01:00
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pad-handler.h"
#include <cstring>
#include "radio.h"
#include "charsets.h"
#include "mot-data.h"
/**
* \class padHandler
2017-03-27 16:32:49 +02:00
* Handles the pad segments passed on from mp2- and mp4Processor
2017-02-06 14:09:08 +01:00
*/
2017-05-31 17:46:42 +02:00
padHandler::padHandler (RadioInterface *mr, QString picturesPath) {
2017-02-06 14:09:08 +01:00
myRadioInterface = mr;
connect (this, SIGNAL (showLabel (QString)),
mr, SLOT (showLabel (QString)));
2017-03-27 16:32:49 +02:00
connect (this, SIGNAL (show_motHandling (bool)),
mr, SLOT (show_motHandling (bool)));
2017-05-31 17:46:42 +02:00
my_motHandler = new motHandler (mr, picturesPath);
2017-03-27 16:32:49 +02:00
//
// mscGroupElement indicates whether we are handling an
// msc datagroup or not.
mscGroupElement = false;
2017-12-27 19:11:36 +01:00
dataGroupLength = 0;
2017-03-27 16:32:49 +02:00
// xpadLength tells - if mscGroupElement is "on" - the size of the
// xpadfields, needed for handling xpads without CI's
xpadLength = -1;
//
// and for the shortPad we maintain
still_to_go = 0;
lastSegment = false;
firstSegment = false;
2017-08-25 21:24:45 +02:00
segmentNumber = -1;
2017-02-06 14:09:08 +01:00
}
padHandler::~padHandler (void) {
delete my_motHandler;
}
2017-03-27 16:32:49 +02:00
// Data is stored reverse, we pass the vector and the index of the
// last element of the XPad data.
// L0 is the "top" byte of the L field, L1 the next to top one.
void padHandler::processPAD (uint8_t *buffer, int16_t last,
uint8_t L1, uint8_t L0) {
uint8_t fpadType = (L1 >> 6) & 03;
2017-02-06 14:09:08 +01:00
if (fpadType != 00)
return;
//
// OK, we'll try
2017-03-27 16:32:49 +02:00
2017-08-24 19:30:52 +02:00
uint8_t x_padInd = (L1 >> 4) & 03;
uint8_t CI_flag = L0 & 02;
2017-02-06 14:09:08 +01:00
switch (x_padInd) {
2017-03-27 16:32:49 +02:00
default:
break;
2017-02-06 14:09:08 +01:00
case 01 :
handle_shortPAD (buffer, last, CI_flag);
2017-02-06 14:09:08 +01:00
break;
case 02:
handle_variablePAD (buffer, last, CI_flag);
2017-02-06 14:09:08 +01:00
break;
}
}
2017-03-27 16:32:49 +02:00
// Since the data is stored in reversed order, we pass
// on the vector address and the offset of the last element
// in that vector
2017-08-24 19:30:52 +02:00
void padHandler::handle_shortPAD (uint8_t *b, int16_t last, uint8_t CIf) {
2017-02-06 14:09:08 +01:00
int16_t i;
2017-08-25 21:24:45 +02:00
static
uint8_t data [20];
static
int16_t index = 0;
2017-02-06 14:09:08 +01:00
2017-08-24 19:30:52 +02:00
if (CIf != 0) { // has a CI flag
2017-08-25 21:24:45 +02:00
uint8_t CI = b [last];
firstSegment = (b [last - 1] & 0x40) != 0;
lastSegment = (b [last - 1] & 0x20) != 0;
charSet = b [last - 2] & 0x0F;
uint8_t AcTy = CI & 037; // application type
2017-08-24 19:30:52 +02:00
switch (AcTy) {
default:
2017-08-25 21:24:45 +02:00
// fprintf (stderr, "AcTy: %d\n", AcTy);
2017-08-24 19:30:52 +02:00
break;
case 0: // end marker
break;
case 2: // start of fragment, extract the length
2017-08-25 21:24:45 +02:00
if (firstSegment) {
dynamicLabelText. clear ();
2017-08-25 21:24:45 +02:00
segmentNumber = 0;
2017-08-25 08:51:16 +02:00
}
2017-08-25 21:24:45 +02:00
else {
if ((b [last - 2] >> 4) != segmentNumber + 1) {
segmentNumber = -1;
return;
}
}
segmentNumber = b [last - 2] >> 4;
still_to_go = b [last - 1] & 0x0F;
index = 0; // for the fragmnt
data [index ++] = b [last - 3];
2017-08-24 19:30:52 +02:00
break;
}
}
else { // No CI flag
// X-PAD field is all data
for (i = 0; (i < 4) && (still_to_go > 0); i ++) {
2017-08-25 21:24:45 +02:00
data [index ++] = b [last - i];
still_to_go --;
}
2017-08-25 21:24:45 +02:00
// at the end of a frame
if ((still_to_go <= 0) && (index > 0)) {
data [index] = 0;
//
// just to avoid doubling by unsollicited shortpads
index = 0;
dynamicLabelText.
append (toQStringUsingCharset ((char *)data,
(CharacterSet)charSet));
// if we are at the end of the last segment (and the text is not empty)
// then show it.
if (lastSegment) {
2018-01-06 19:37:34 +01:00
if (dynamicLabelText. size () > 0)
2017-08-25 21:24:45 +02:00
showLabel (dynamicLabelText);
dynamicLabelText. clear ();
}
}
2017-08-24 19:30:52 +02:00
}
2017-02-06 14:09:08 +01:00
}
///////////////////////////////////////////////////////////////////////
//
// Here we end up when F_PAD type = 00 and X-PAD Ind = 02
static
int16_t lengthTable [] = {4, 6, 8, 12, 16, 24, 32, 48};
2017-03-27 16:32:49 +02:00
// Since the data is reversed, we pass on the vector address
// and the offset of the last element in the vector,
// i.e. we start (downwards) beginning at b [last];
2017-02-06 14:09:08 +01:00
void padHandler::handle_variablePAD (uint8_t *b,
2017-03-27 16:32:49 +02:00
int16_t last, uint8_t CI_flag) {
int16_t CI_Index = 0;
uint8_t CI_table [4];
2017-02-06 14:09:08 +01:00
int16_t i, j;
2017-03-27 16:32:49 +02:00
int16_t base = last;
2018-01-06 19:37:34 +01:00
std::vector<uint8_t> data; // for the local addition
2017-02-06 14:09:08 +01:00
2017-03-27 16:32:49 +02:00
// If an xpadfield shows with a CI_flag == 0, and if we are
// dealing with an msc field, the size to be taken is
// the size of the latest xpadfield that had a CI_flag != 0
if (CI_flag == 0) {
if (mscGroupElement && (xpadLength > 0)) {
data. resize (xpadLength);
for (j = 0; j < xpadLength; j ++)
data [j] = b [last - j];
add_MSC_element (data);
}
2017-02-06 14:09:08 +01:00
return;
2017-03-27 16:32:49 +02:00
}
2017-02-06 14:09:08 +01:00
//
// The CI flag in the F_PAD data is set, so we have local CI's
// 7.4.2.2: Contents indicators are one byte long
2017-03-27 16:32:49 +02:00
while (((b [base] & 037) != 0) && (CI_Index < 4))
CI_table [CI_Index ++] = b [base --];
2017-02-06 14:09:08 +01:00
2017-03-27 16:32:49 +02:00
if (CI_Index < 4) // we have a "0" indicator, adjust base
2017-02-06 14:09:08 +01:00
base -= 1;
2017-03-27 16:32:49 +02:00
// The space for the CI's does belong to the Cpadfield, so
// but do not forget to take into account the '0'field if CI_Index < 4
if (mscGroupElement) {
xpadLength = 0;
for (i = 0; i < CI_Index; i ++)
xpadLength += lengthTable [CI_table [i] >> 5];
xpadLength += CI_Index == 4 ? 4 : CI_Index + 1;
}
2017-02-06 14:09:08 +01:00
//
2017-03-27 16:32:49 +02:00
// Handle the contents
for (i = 0; i < CI_Index; i ++) {
2017-02-06 14:09:08 +01:00
uint8_t appType = CI_table [i] & 037;
int16_t length = lengthTable [CI_table [i] >> 5];
if (appType == 1) {
2017-03-27 16:32:49 +02:00
dataGroupLength = ((b [base] & 077) << 8) | b [base - 1];
2017-02-06 14:09:08 +01:00
base -= 4;
last_appType = 1;
continue;
}
2017-03-27 16:32:49 +02:00
// collect data, reverse the reversed bytes
data. resize (length);
2017-02-06 14:09:08 +01:00
for (j = 0; j < length; j ++)
data [j] = b [base - j];
switch (appType) {
default:
return; // sorry, we do not handle this
case 2:
case 3:
2017-03-27 16:32:49 +02:00
dynamicLabel ((uint8_t *)(data. data ()),
2018-01-06 19:37:34 +01:00
data. size (), CI_table [i]);
2017-02-06 14:09:08 +01:00
break;
case 12:
2017-03-27 16:32:49 +02:00
new_MSC_element (data, dataGroupLength);
2017-02-06 14:09:08 +01:00
break;
case 13:
2017-03-27 16:32:49 +02:00
add_MSC_element (data);
2017-02-06 14:09:08 +01:00
break;
}
last_appType = appType;
base -= length;
2017-03-27 16:32:49 +02:00
if (base < 0 && i < CI_Index - 1) {
2017-02-06 14:09:08 +01:00
fprintf (stderr, "Hier gaat het fout, base = %d\n", base);
return;
}
}
}
//
// A dynamic label is created from a sequence of (dynamic) xpad
// fields, starting with CI = 2, continuing with CI = 3
void padHandler::dynamicLabel (uint8_t *data, int16_t length, uint8_t CI) {
static int16_t segmentno = 0;
static int16_t remainDataLength = 0;
static bool isLastSegment = false;
static bool moreXPad = false;
int16_t dataLength = 0;
if ((CI & 037) == 02) { // start of segment
uint16_t prefix = (data [0] << 8) | data [1];
uint8_t field_1 = (prefix >> 8) & 017;
uint8_t Cflag = (prefix >> 12) & 01;
uint8_t first = (prefix >> 14) & 01;
uint8_t last = (prefix >> 13) & 01;
dataLength = length - 2; // The length with header removed
if (first) {
segmentno = 1;
charSet = (prefix >> 4) & 017;
dynamicLabelText. clear ();
}
else
2017-06-02 14:08:03 +02:00
segmentno = ((prefix >> 4) & 07) + 1;
2017-02-06 14:09:08 +01:00
if (Cflag) { // special dynamic label command
// the only specified command is to clear the display
dynamicLabelText. clear ();
}
else { // Dynamic text length
int16_t totalDataLength = field_1 + 1;
if (length - 2 < totalDataLength) {
dataLength = length - 2; // the length is shortened by header
moreXPad = true;
}
else {
dataLength = totalDataLength; // no more xpad app's 3
moreXPad = false;
}
// convert dynamic label
QString segmentText = toQStringUsingCharset (
(const char *)&data [2],
(CharacterSet) charSet,
dataLength);
dynamicLabelText. append (segmentText);
// if at the end, show the label
if (last) {
if (!moreXPad) {
showLabel (dynamicLabelText);
}
else
isLastSegment = true;
}
else
isLastSegment = false;
// calculate remaining data length
remainDataLength = totalDataLength - dataLength;
}
}
else
if (((CI & 037) == 03) && moreXPad) {
if (remainDataLength > length) {
dataLength = length;
remainDataLength -= length;
}
else {
dataLength = remainDataLength;
moreXPad = false;
}
QString segmentText = toQStringUsingCharset (
(const char *) data,
(CharacterSet) charSet,
dataLength);
2018-01-06 19:37:34 +01:00
dynamicLabelText. append (segmentText);
2017-02-06 14:09:08 +01:00
if (!moreXPad && isLastSegment) {
showLabel (dynamicLabelText);
}
}
}
//
2017-03-27 16:32:49 +02:00
// Called at the start of the msc datagroupfield,
// the msc_length was given by the preceding appType "1"
2018-01-06 19:37:34 +01:00
void padHandler::new_MSC_element (std::vector<uint8_t> data,
int msc_length) {
2017-03-27 16:32:49 +02:00
mscGroupElement = true;
msc_dataGroupBuffer. clear ();
msc_dataGroupBuffer = data;
msc_dataGroupLength = msc_length;
show_motHandling (true);
}
2017-02-06 14:09:08 +01:00
//
2018-01-06 19:37:34 +01:00
void padHandler::add_MSC_element (std::vector<uint8_t> data) {
2017-02-06 14:09:08 +01:00
int16_t i;
2018-01-06 19:37:34 +01:00
int16_t currentLength = msc_dataGroupBuffer. size ();
2017-03-27 16:32:49 +02:00
//
// just to ensure that, when a "12" appType is missing, the
// data of "13" appType elements is not endless collected.
if (currentLength == 0)
2017-02-06 14:09:08 +01:00
return;
2018-01-06 19:37:34 +01:00
msc_dataGroupBuffer. insert (std::end (msc_dataGroupBuffer),
std::begin (data), std::end (data));
if (msc_dataGroupBuffer. size () >= msc_dataGroupLength) {
2017-03-27 16:32:49 +02:00
build_MSC_segment (msc_dataGroupBuffer, msc_dataGroupLength);
msc_dataGroupBuffer. clear ();
// mscGroupElement = false;
xpadLength = -1;
show_motHandling (false);
}
2017-02-06 14:09:08 +01:00
}
2018-01-06 19:37:34 +01:00
void padHandler::build_MSC_segment (std::vector<uint8_t> data,
2017-03-27 16:32:49 +02:00
int msc_length) {
2017-02-06 14:09:08 +01:00
// we have a MOT segment, let us look what is in it
// according to DAB 300 401 (page 37) the header (MSC data group)
// is
2018-01-06 19:37:34 +01:00
int16_t size = data. size ();
2017-03-27 16:32:49 +02:00
uint8_t groupType = data [0] & 0xF;
2017-12-27 19:11:36 +01:00
uint8_t continuityIndex = (data [1] & 0xF0) >> 4;
2017-03-27 16:32:49 +02:00
uint8_t repetitionIndex = data [1] & 0xF;
2017-02-06 14:09:08 +01:00
int16_t segmentNumber = -1; // default
int16_t transportId = -1; // default
bool lastFlag = false; // default
uint16_t index;
2017-03-27 16:32:49 +02:00
if ((data [0] & 0x40) != 0) {
2018-01-06 19:37:34 +01:00
bool res = check_crc_bytes (data. data (), msc_length - 2);
2017-02-06 14:09:08 +01:00
if (!res) {
2017-04-04 14:04:25 +02:00
// fprintf (stderr, "crc failed ");
2017-02-06 14:09:08 +01:00
return;
}
2017-03-31 16:44:07 +02:00
// else
2017-04-04 14:04:25 +02:00
// fprintf (stderr, "crc success ");
2017-02-06 14:09:08 +01:00
}
if ((groupType != 3) && (groupType != 4))
return; // do not know yet
2017-04-04 14:04:25 +02:00
2017-02-06 14:09:08 +01:00
// extensionflag
2017-03-27 16:32:49 +02:00
bool extensionFlag = (data [0] & 0x80) != 0;
2017-02-06 14:09:08 +01:00
// if the segmentflag is on, then a lastflag and segmentnumber are
// available, i.e. 2 bytes more
index = extensionFlag ? 4 : 2;
2017-03-27 16:32:49 +02:00
bool segmentFlag = (data [0] & 0x20) != 0;
2017-02-06 14:09:08 +01:00
if ((segmentFlag) != 0) {
2017-03-27 16:32:49 +02:00
lastFlag = data [index] & 0x80;
segmentNumber = ((data [index] & 0x7F) << 8) | data [index + 1];
2017-02-06 14:09:08 +01:00
index += 2;
}
2017-04-04 14:04:25 +02:00
2017-02-06 14:09:08 +01:00
// if the user access flag is on there is a user accessfield
2017-03-27 16:32:49 +02:00
if ((data [0] & 0x10) != 0) {
int16_t lengthIndicator = data [index] & 0x0F;
if ((data [index] & 0x10) != 0) { //transportid flag
transportId = data [index + 1] << 8 |
data [index + 2];
2017-02-06 14:09:08 +01:00
index += 3;
}
else {
fprintf (stderr, "sorry no transportId\n");
return;
}
index += (lengthIndicator - 2);
}
2017-03-27 16:32:49 +02:00
// the segment is handled by the mot handler, which also
// handles the MOT's from the regular data services
my_motHandler -> process_mscGroup (&data [index],
2017-02-06 14:09:08 +01:00
groupType,
lastFlag,
segmentNumber,
transportId);
}