2017-02-06 14:09:08 +01:00
|
|
|
#
|
|
|
|
/*
|
2024-04-14 12:47:14 +02:00
|
|
|
* Copyright (C) 2015 .. 2023
|
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
|
2021-08-03 15:13:54 +02:00
|
|
|
*
|
2017-03-23 18:32:25 +01:00
|
|
|
* 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
|
|
|
|
*/
|
2024-01-03 12:26:08 +01:00
|
|
|
//#define _PAD_TRACE_
|
2017-02-06 14:09:08 +01:00
|
|
|
#include "pad-handler.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include "radio.h"
|
2023-10-14 11:27:18 +02:00
|
|
|
#include "crc-handlers.h"
|
2017-02-06 14:09:08 +01:00
|
|
|
#include "charsets.h"
|
2018-05-25 13:46:09 +02:00
|
|
|
#include "mot-object.h"
|
2017-02-06 14:09:08 +01:00
|
|
|
/**
|
|
|
|
* \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
|
|
|
*/
|
2023-09-23 11:50:09 +02:00
|
|
|
padHandler::padHandler (RadioInterface *mr,
|
2025-05-27 18:50:45 +02:00
|
|
|
bool backgroundFlag):
|
|
|
|
myRadioInterface (mr) {
|
2023-09-23 11:50:09 +02:00
|
|
|
this -> backgroundFlag = backgroundFlag;
|
|
|
|
|
2024-04-14 12:47:14 +02:00
|
|
|
connect (this, &padHandler::show_label,
|
|
|
|
mr, &RadioInterface::show_label);
|
|
|
|
connect (this, &padHandler::show_mothandling,
|
|
|
|
mr, &RadioInterface::show_mothandling);
|
2025-06-09 15:04:46 +02:00
|
|
|
connect (this, &padHandler::show_title,
|
|
|
|
mr, &RadioInterface::show_title);
|
2025-05-27 18:50:45 +02:00
|
|
|
// currentSlide = nullptr;
|
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;
|
2017-08-25 12:01:48 +02:00
|
|
|
//
|
|
|
|
// and for the shortPad we maintain
|
|
|
|
still_to_go = 0;
|
|
|
|
lastSegment = false;
|
|
|
|
firstSegment = false;
|
2017-08-25 21:24:45 +02:00
|
|
|
segmentNumber = -1;
|
2025-05-27 18:50:45 +02:00
|
|
|
//
|
|
|
|
//
|
|
|
|
segmentno = -1;
|
|
|
|
remainDataLength = 0;
|
|
|
|
isLastSegment = false;
|
|
|
|
moreXPad = false;
|
2025-06-07 18:45:00 +02:00
|
|
|
|
|
|
|
the_DL2. dlsText = "";
|
|
|
|
the_DL2. IT = 10;
|
|
|
|
the_DL2. IR = 10;
|
|
|
|
for (int i = 0; i < 4; i ++)
|
|
|
|
the_DL2. entity [i]. ct = 65;
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
|
|
|
|
2019-07-08 23:30:45 +02:00
|
|
|
padHandler::~padHandler() {
|
2025-05-27 18:50:45 +02:00
|
|
|
// if (currentSlide != nullptr)
|
|
|
|
// delete currentSlide;
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
|
|
|
|
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.
|
2025-06-09 15:04:46 +02:00
|
|
|
void padHandler::processPAD (const uint8_t *buffer, int16_t last,
|
2017-03-27 16:32:49 +02:00
|
|
|
uint8_t L1, uint8_t L0) {
|
|
|
|
uint8_t fpadType = (L1 >> 6) & 03;
|
|
|
|
|
2018-05-27 12:58:08 +02:00
|
|
|
if (fpadType != 00) {
|
2024-12-23 13:19:21 +01:00
|
|
|
// fprintf (stderr, "fPadtype = %x_padInd\n");
|
2017-02-06 14:09:08 +01:00
|
|
|
return;
|
2018-05-27 12:58:08 +02:00
|
|
|
}
|
2017-02-06 14:09:08 +01:00
|
|
|
//
|
|
|
|
// 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 :
|
2017-08-25 12:01:48 +02:00
|
|
|
handle_shortPAD (buffer, last, CI_flag);
|
2017-02-06 14:09:08 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 02:
|
2017-08-25 12:01:48 +02:00
|
|
|
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
|
2017-03-30 18:54:45 +02:00
|
|
|
// in that vector
|
2018-02-12 16:55:26 +01:00
|
|
|
//
|
2018-05-27 12:58:08 +02:00
|
|
|
// shortPad's are 4 byte values. If the CI is on, then type 2
|
2018-02-12 16:55:26 +01:00
|
|
|
// indicates the start of a segment. Type 3 the continuation.
|
|
|
|
// The start of a message, i.e. segment 0 is (a.o) found by
|
|
|
|
// a (1, 0) value of the firstSegment/lastSegment values.
|
|
|
|
// The end of a segment might be indicated by a specific pattern
|
|
|
|
// of these 2 values, but it is not clear to me how.
|
|
|
|
// For me, the end of a segment is when we collected the amount
|
|
|
|
// of values specified for the segment.
|
2025-06-09 15:04:46 +02:00
|
|
|
void padHandler::handle_shortPAD (const uint8_t *b,
|
|
|
|
int16_t last, uint8_t CIf) {
|
2017-02-06 14:09:08 +01:00
|
|
|
int16_t i;
|
|
|
|
|
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
|
2018-05-26 21:18:22 +02:00
|
|
|
|
2024-04-14 12:47:14 +02:00
|
|
|
if (firstSegment)
|
2023-02-12 13:04:38 +01:00
|
|
|
dynamicLabelText. clear ();
|
2017-08-24 19:30:52 +02:00
|
|
|
switch (AcTy) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0: // end marker
|
|
|
|
break;
|
2018-02-12 16:55:26 +01:00
|
|
|
//
|
2018-07-16 11:33:39 +02:00
|
|
|
case 2: // start of fragment, extract the length
|
|
|
|
if (firstSegment && !lastSegment) {
|
|
|
|
segmentNumber = b [last - 2] >> 4;
|
2024-12-23 13:19:21 +01:00
|
|
|
if (dynamicLabelText. size () > 0) {
|
|
|
|
QString displayText = toQStringUsingCharset
|
|
|
|
((char *)(dynamicLabelText. data()),
|
|
|
|
(CharacterSet)charSet,
|
|
|
|
dynamicLabelText. size());
|
2025-01-13 19:13:10 +01:00
|
|
|
show_label (displayText, (int)charSet);
|
2024-01-03 12:26:08 +01:00
|
|
|
}
|
2024-12-23 13:19:21 +01:00
|
|
|
dynamicLabelText = "";
|
2018-07-16 11:33:39 +02:00
|
|
|
}
|
|
|
|
still_to_go = b [last - 1] & 0x0F;
|
|
|
|
shortpadData. resize (0);
|
|
|
|
shortpadData. push_back (b [last - 3]);
|
|
|
|
break;
|
|
|
|
|
2018-02-12 16:55:26 +01:00
|
|
|
case 3: // continuation of fragment
|
|
|
|
for (i = 0; (i < 3) && (still_to_go > 0); i ++) {
|
|
|
|
still_to_go --;
|
|
|
|
shortpadData. push_back (b [last - 1 - i]);
|
|
|
|
}
|
|
|
|
|
2019-07-08 23:30:45 +02:00
|
|
|
if ((still_to_go <= 0) && (shortpadData. size() > 1)) {
|
2025-01-13 19:13:10 +01:00
|
|
|
// shortpadData. push_back (0);
|
2024-12-23 13:19:21 +01:00
|
|
|
dynamicLabelText. append ((const char *)shortpadData. data (),
|
|
|
|
shortpadData. size ());
|
|
|
|
shortpadData. resize (0);
|
2018-02-12 16:55:26 +01:00
|
|
|
}
|
|
|
|
break;
|
2017-08-24 19:30:52 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // No CI flag
|
|
|
|
// X-PAD field is all data
|
2017-08-25 12:01:48 +02:00
|
|
|
for (i = 0; (i < 4) && (still_to_go > 0); i ++) {
|
2018-02-12 16:55:26 +01:00
|
|
|
shortpadData. push_back (b [last - i]);
|
2017-08-25 12:01:48 +02:00
|
|
|
still_to_go --;
|
|
|
|
}
|
2021-08-03 15:13:54 +02:00
|
|
|
|
2017-08-25 21:24:45 +02:00
|
|
|
// at the end of a frame
|
2019-07-08 23:30:45 +02:00
|
|
|
if ((still_to_go <= 0) && (shortpadData. size() > 0)) {
|
2025-01-13 19:13:10 +01:00
|
|
|
// shortpadData . push_back (0);
|
2017-08-25 21:24:45 +02:00
|
|
|
//
|
|
|
|
// just to avoid doubling by unsollicited shortpads
|
2024-12-23 13:19:21 +01:00
|
|
|
dynamicLabelText. append ((const char *)shortpadData. data (),
|
2024-04-14 12:47:14 +02:00
|
|
|
shortpadData. size ());
|
2018-02-12 16:55:26 +01:00
|
|
|
shortpadData. resize (0);
|
2017-08-25 21:24:45 +02:00
|
|
|
// if we are at the end of the last segment (and the text is not empty)
|
|
|
|
// then show it.
|
2018-02-12 16:55:26 +01:00
|
|
|
if (!firstSegment && lastSegment) {
|
2024-01-03 12:26:08 +01:00
|
|
|
if (dynamicLabelText. size() > 0) {
|
2024-12-23 13:19:21 +01:00
|
|
|
QString displayText =
|
|
|
|
toQStringUsingCharset (
|
|
|
|
(const char *)dynamicLabelText. data (),
|
|
|
|
(CharacterSet) charSet,
|
|
|
|
dynamicLabelText. size ());
|
2025-06-09 15:04:46 +02:00
|
|
|
// fprintf (stderr, "%s \n", displayText. toLatin1 (). data ());
|
2025-01-13 19:13:10 +01:00
|
|
|
show_label (displayText, (int)charSet);
|
2024-01-03 12:26:08 +01:00
|
|
|
}
|
2019-07-08 23:30:45 +02:00
|
|
|
dynamicLabelText. clear();
|
2017-08-25 21:24:45 +02:00
|
|
|
}
|
2017-08-25 12:01:48 +02:00
|
|
|
}
|
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,
|
2017-03-30 18:54:45 +02:00
|
|
|
// i.e. we start (downwards) beginning at b [last];
|
2025-06-09 15:04:46 +02:00
|
|
|
void padHandler::handle_variablePAD (const 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];
|
|
|
|
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) {
|
2025-05-27 18:50:45 +02:00
|
|
|
// if (mscGroupElement && (xpadLength > 0)) {
|
|
|
|
if (xpadLength > 0) {
|
2021-05-13 13:22:57 +02:00
|
|
|
if (last < xpadLength - 1) {
|
2024-12-27 12:39:00 +01:00
|
|
|
// fprintf(stderr, "handle_variablePAD: last < xpadLength - 1\n");
|
2021-05-13 12:33:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2023-09-26 13:05:25 +02:00
|
|
|
|
2017-03-27 16:32:49 +02:00
|
|
|
data. resize (xpadLength);
|
2025-05-27 18:50:45 +02:00
|
|
|
for (int16_t j = 0; j < xpadLength; j ++)
|
2017-03-27 16:32:49 +02:00
|
|
|
data [j] = b [last - j];
|
2025-05-27 18:50:45 +02:00
|
|
|
switch (last_appType) {
|
|
|
|
// Dynamic label segment, start of X-PAD data group
|
|
|
|
case 2:
|
|
|
|
// Dynamic label segment, continuation of X-PAD data group
|
|
|
|
case 3:
|
|
|
|
dynamicLabel((uint8_t *)(data.data()), xpadLength, 3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 12: // MOT, start of X-PAD data group
|
|
|
|
case 13: // MOT, continuation of X-PAD data group
|
|
|
|
if (mscGroupElement)
|
|
|
|
add_MSC_element(data);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-03-27 16:32:49 +02:00
|
|
|
}
|
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
|
|
|
|
2023-09-26 13:05:25 +02:00
|
|
|
// The space for the CI's does belong to the CPadfield, so
|
|
|
|
// do not forget to take into account the '0'field if CI_Index < 4
|
2025-05-27 18:50:45 +02:00
|
|
|
// if (mscGroupElement) {
|
|
|
|
{
|
2017-03-27 16:32:49 +02:00
|
|
|
xpadLength = 0;
|
2025-05-27 18:50:45 +02:00
|
|
|
for (int16_t i = 0; i < CI_Index; i ++)
|
2017-03-27 16:32:49 +02:00
|
|
|
xpadLength += lengthTable [CI_table [i] >> 5];
|
|
|
|
xpadLength += CI_Index == 4 ? 4 : CI_Index + 1;
|
2018-05-27 12:58:08 +02:00
|
|
|
// fprintf (stderr, "xpadLength set to %d\n", xpadLength);
|
2017-03-27 16:32:49 +02:00
|
|
|
}
|
2018-05-27 12:58:08 +02:00
|
|
|
|
2017-03-27 16:32:49 +02:00
|
|
|
// Handle the contents
|
2025-05-27 18:50:45 +02:00
|
|
|
for (int16_t 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];
|
2018-05-27 12:58:08 +02:00
|
|
|
|
2023-09-26 13:05:25 +02:00
|
|
|
if (appType == 1) { // length spec
|
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);
|
2025-05-27 18:50:45 +02:00
|
|
|
for (int16_t j = 0; j < length; j ++)
|
2017-02-06 14:09:08 +01:00
|
|
|
data [j] = b [base - j];
|
|
|
|
|
|
|
|
switch (appType) {
|
|
|
|
default:
|
2018-02-08 16:32:45 +01:00
|
|
|
return; // sorry, we do not handle this
|
2017-02-06 14:09:08 +01:00
|
|
|
|
2025-06-09 15:04:46 +02:00
|
|
|
case 1: //
|
|
|
|
// fprintf (stderr, "Need to fix this\n");
|
|
|
|
return;
|
|
|
|
case 2: // Dynamic label segment, start of X-PAD data group
|
|
|
|
case 3: // Dynamic label segment, continuation of X-PAD data group
|
2019-07-08 23:30:45 +02:00
|
|
|
dynamicLabel ((uint8_t *)(data. data()),
|
|
|
|
data. size(), CI_table [i]);
|
2017-02-06 14:09:08 +01:00
|
|
|
break;
|
|
|
|
|
2018-02-08 16:32:45 +01:00
|
|
|
case 12: // MOT, start of X-PAD data group
|
2018-05-27 12:58:08 +02:00
|
|
|
new_MSC_element (data);
|
2017-02-06 14:09:08 +01:00
|
|
|
break;
|
|
|
|
|
2018-02-08 16:32:45 +01:00
|
|
|
case 13: // MOT, continuation of X-PAD data group
|
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) {
|
2025-06-10 18:42:18 +02:00
|
|
|
// fprintf (stderr, "Hier gaat het fout, base = %d\n", base);
|
2017-02-06 14:09:08 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// A dynamic label is created from a sequence of (dynamic) xpad
|
|
|
|
// fields, starting with CI = 2, continuing with CI = 3
|
2025-06-09 15:04:46 +02:00
|
|
|
void padHandler::dynamicLabel (const uint8_t *data, int16_t length, uint8_t CI) {
|
2017-02-06 14:09:08 +01:00
|
|
|
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
|
|
|
|
|
2024-01-03 12:26:08 +01:00
|
|
|
#ifdef _PAD_TRACE_
|
2023-09-26 13:05:25 +02:00
|
|
|
fprintf (stderr, "first %d last %d Cflag %d\n",
|
|
|
|
first, last, Cflag);
|
|
|
|
#endif
|
2017-02-06 14:09:08 +01:00
|
|
|
if (first) {
|
2024-01-02 12:52:57 +01:00
|
|
|
segmentno = 1;
|
2024-01-03 12:26:08 +01:00
|
|
|
#ifdef _PAD_TRACE_
|
|
|
|
fprintf (stderr, "segment 1\n");
|
|
|
|
#endif
|
2017-02-06 14:09:08 +01:00
|
|
|
charSet = (prefix >> 4) & 017;
|
2024-12-23 13:19:21 +01:00
|
|
|
dynamicLabelText. clear ();
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
2024-01-02 12:52:57 +01:00
|
|
|
else {
|
|
|
|
int test = ((prefix >> 4) & 07) + 1;
|
|
|
|
if (test != segmentno + 1) {
|
2024-01-03 12:26:08 +01:00
|
|
|
#ifdef _PAD_TRACE_
|
|
|
|
fprintf (stderr, "mismatch %d %d\n", test, segmentno);
|
|
|
|
#endif
|
2024-01-02 12:52:57 +01:00
|
|
|
segmentno = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
segmentno = ((prefix >> 4) & 07) + 1;
|
2024-01-03 12:26:08 +01:00
|
|
|
#ifdef _PAD_TRACE_
|
|
|
|
fprintf (stderr, "segment %d\n", segmentno);
|
|
|
|
#endif
|
2024-01-02 12:52:57 +01:00
|
|
|
}
|
2025-06-03 19:44:20 +02:00
|
|
|
//
|
|
|
|
// etsi TS 102 980 specifies the DL plus objects, I just let it go
|
|
|
|
// for the time being
|
2024-01-02 12:52:57 +01:00
|
|
|
if (Cflag) { // special dynamic label command
|
2025-06-03 19:44:20 +02:00
|
|
|
uint16_t Command = (prefix >> 8) & 0x0f;
|
|
|
|
switch (Command) {
|
|
|
|
case 1:
|
|
|
|
#ifdef _PAD_TRACE_
|
|
|
|
fprintf (stderr, "clear command\n");
|
|
|
|
#endif
|
|
|
|
dynamicLabelText. clear ();
|
|
|
|
segmentno = -1;
|
|
|
|
break;
|
|
|
|
case 2:
|
2024-01-03 12:26:08 +01:00
|
|
|
#ifdef _PAD_TRACE_
|
2025-06-03 19:44:20 +02:00
|
|
|
fprintf (stderr, "DL plus command\n");
|
2024-01-03 12:26:08 +01:00
|
|
|
#endif
|
2025-06-07 18:45:00 +02:00
|
|
|
if (!backgroundFlag)
|
|
|
|
add_toDL2 (&data [2]);
|
2025-06-03 19:44:20 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// fprintf (stderr, "unknown command %d\n", Command);
|
|
|
|
break;
|
|
|
|
}
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
|
|
|
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
|
2024-12-23 13:19:21 +01:00
|
|
|
dynamicLabelText. append ((const char *)(&data [2]), dataLength);
|
2017-02-06 14:09:08 +01:00
|
|
|
|
|
|
|
// if at the end, show the label
|
|
|
|
if (last) {
|
|
|
|
if (!moreXPad) {
|
2024-12-23 13:19:21 +01:00
|
|
|
QString displayText =
|
|
|
|
toQStringUsingCharset (
|
|
|
|
(const char *) dynamicLabelText. data (),
|
|
|
|
(CharacterSet) charSet,
|
|
|
|
dynamicLabelText. size ());
|
2025-06-07 18:45:00 +02:00
|
|
|
if (!backgroundFlag) {
|
|
|
|
add_toDL2 (displayText);
|
|
|
|
show_label (displayText, (int)charSet);
|
|
|
|
}
|
2024-01-02 12:52:57 +01:00
|
|
|
segmentno = -1;
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-12-23 13:19:21 +01:00
|
|
|
dynamicLabelText. append ((const char *)data, dataLength);
|
2017-02-06 14:09:08 +01:00
|
|
|
if (!moreXPad && isLastSegment) {
|
2024-12-23 13:19:21 +01:00
|
|
|
QString displayText = toQStringUsingCharset
|
|
|
|
((char *)(dynamicLabelText. data()),
|
|
|
|
(CharacterSet)charSet,
|
|
|
|
dynamicLabelText. size());
|
2025-06-07 18:45:00 +02:00
|
|
|
if (!backgroundFlag) {
|
|
|
|
add_toDL2 (displayText);
|
|
|
|
show_label (displayText, (int)charSet);
|
|
|
|
}
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
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"
|
2025-06-09 15:04:46 +02:00
|
|
|
void padHandler::new_MSC_element (const std::vector<uint8_t> &data) {
|
2018-08-20 20:22:09 +02:00
|
|
|
|
|
|
|
// if (mscGroupElement) {
|
2019-07-08 23:30:45 +02:00
|
|
|
//// if (msc_dataGroupBuffer. size() < dataGroupLength)
|
2018-08-20 20:22:09 +02:00
|
|
|
//// fprintf (stderr, "short ? %d %d\n",
|
2019-07-08 23:30:45 +02:00
|
|
|
//// msc_dataGroupBuffer. size(),
|
2018-08-20 20:22:09 +02:00
|
|
|
//// dataGroupLength);
|
2019-07-08 23:30:45 +02:00
|
|
|
// msc_dataGroupBuffer. clear();
|
2018-08-20 20:22:09 +02:00
|
|
|
// build_MSC_segment (data);
|
|
|
|
// mscGroupElement = true;
|
2023-11-03 17:47:31 +01:00
|
|
|
// show_mothandling (true);
|
2018-08-20 20:22:09 +02:00
|
|
|
// }
|
2018-05-27 12:58:08 +02:00
|
|
|
|
2020-03-02 15:13:34 +01:00
|
|
|
if (data. size() >= (uint16_t)dataGroupLength) { // msc element is single item
|
2019-07-08 23:30:45 +02:00
|
|
|
msc_dataGroupBuffer. clear();
|
2018-05-27 12:58:08 +02:00
|
|
|
build_MSC_segment (data);
|
|
|
|
mscGroupElement = false;
|
2023-11-03 17:47:31 +01:00
|
|
|
show_mothandling (true);
|
2018-08-21 09:30:07 +02:00
|
|
|
// fprintf (stderr, "msc element is single\n");
|
2018-05-27 12:58:08 +02:00
|
|
|
return;
|
2018-05-26 21:18:22 +02:00
|
|
|
}
|
2018-05-27 12:58:08 +02:00
|
|
|
|
2018-08-20 15:22:36 +02:00
|
|
|
mscGroupElement = true;
|
2019-07-08 23:30:45 +02:00
|
|
|
msc_dataGroupBuffer. clear();
|
2018-05-27 12:58:08 +02:00
|
|
|
msc_dataGroupBuffer = data;
|
2023-11-03 17:47:31 +01:00
|
|
|
show_mothandling (true);
|
2017-03-27 16:32:49 +02:00
|
|
|
}
|
|
|
|
|
2017-02-06 14:09:08 +01:00
|
|
|
//
|
2025-06-09 15:04:46 +02:00
|
|
|
void padHandler::add_MSC_element (const std::vector<uint8_t> &data) {
|
2019-07-08 23:30:45 +02:00
|
|
|
int32_t currentLength = msc_dataGroupBuffer. size();
|
2017-03-27 16:32:49 +02:00
|
|
|
//
|
|
|
|
// just to ensure that, when a "12" appType is missing, the
|
2018-05-17 10:48:38 +02:00
|
|
|
// data of "13" appType elements is not endlessly collected.
|
2018-05-26 21:18:22 +02:00
|
|
|
if (currentLength == 0) {
|
2017-02-06 14:09:08 +01:00
|
|
|
return;
|
2018-05-26 21:18:22 +02:00
|
|
|
}
|
2017-02-06 14:09:08 +01:00
|
|
|
|
2018-01-06 19:37:34 +01:00
|
|
|
msc_dataGroupBuffer. insert (std::end (msc_dataGroupBuffer),
|
|
|
|
std::begin (data), std::end (data));
|
2020-03-02 15:13:34 +01:00
|
|
|
if (msc_dataGroupBuffer. size() >= (uint32_t)dataGroupLength) {
|
2018-05-27 12:58:08 +02:00
|
|
|
build_MSC_segment (msc_dataGroupBuffer);
|
2019-07-08 23:30:45 +02:00
|
|
|
msc_dataGroupBuffer. clear();
|
2018-08-20 20:22:09 +02:00
|
|
|
// mscGroupElement = false;
|
2023-11-03 17:47:31 +01:00
|
|
|
show_mothandling (false);
|
2017-03-27 16:32:49 +02:00
|
|
|
}
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
|
|
|
|
2025-06-09 15:04:46 +02:00
|
|
|
void padHandler::build_MSC_segment (const std::vector<uint8_t> &data) {
|
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
|
2020-03-02 15:13:34 +01:00
|
|
|
int32_t size = data. size() < (uint32_t)dataGroupLength ? data. size() :
|
2018-05-27 13:17:14 +02:00
|
|
|
dataGroupLength;
|
2021-05-13 12:33:32 +02:00
|
|
|
|
2022-01-04 12:00:11 +01:00
|
|
|
if (size < 2) {
|
|
|
|
fprintf (stderr, "build_MSC_segment: data size < 2\n");
|
|
|
|
return;
|
|
|
|
}
|
2018-05-27 13:17:14 +02:00
|
|
|
|
2025-07-20 19:25:14 +02:00
|
|
|
const uint8_t groupType = data [0] & 0xF;
|
2020-03-02 15:13:34 +01:00
|
|
|
//uint8_t continuityIndex = (data [1] & 0xF0) >> 4;
|
|
|
|
//uint8_t repetitionIndex = data [1] & 0xF;
|
|
|
|
uint16_t transportId = 0; // default
|
2025-07-20 19:25:14 +02:00
|
|
|
int16_t segmentNumber = -1; // default
|
2020-03-02 15:13:34 +01:00
|
|
|
bool lastFlag = false; // default
|
|
|
|
uint16_t index;
|
2017-02-06 14:09:08 +01:00
|
|
|
|
2017-03-27 16:32:49 +02:00
|
|
|
if ((data [0] & 0x40) != 0) {
|
2019-07-08 23:30:45 +02:00
|
|
|
bool res = check_crc_bytes (data. data(), size - 2);
|
2017-02-06 14:09:08 +01:00
|
|
|
if (!res) {
|
2018-05-27 12:58:08 +02:00
|
|
|
// fprintf (stderr, "build_MSC_segment fails on crc check\n");
|
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
|
|
|
}
|
|
|
|
|
2018-05-26 21:18:22 +02:00
|
|
|
if ((groupType != 3) && (groupType != 4)) {
|
2025-06-10 18:42:18 +02:00
|
|
|
// fprintf (stderr, "groupType %d\n", groupType);
|
2017-02-06 14:09:08 +01:00
|
|
|
return; // do not know yet
|
2018-05-26 21:18:22 +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
|
2018-08-20 15:22:36 +02:00
|
|
|
// available, i.e. 2 bytes more.
|
|
|
|
// Theoretically, the segment number can be as large as 16384
|
2017-02-06 14:09:08 +01:00
|
|
|
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;
|
2018-08-20 15:22:36 +02:00
|
|
|
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];
|
2022-01-04 12:00:11 +01:00
|
|
|
// fprintf (stderr, "transportId = %d\n", transportId);
|
2017-02-06 14:09:08 +01:00
|
|
|
index += 3;
|
|
|
|
}
|
2022-01-04 12:00:11 +01:00
|
|
|
// else {
|
2018-08-07 10:31:17 +02:00
|
|
|
// fprintf (stderr, "sorry no transportId\n");
|
2022-01-04 12:00:11 +01:00
|
|
|
// return;
|
|
|
|
// }
|
2017-02-06 14:09:08 +01:00
|
|
|
index += (lengthIndicator - 2);
|
|
|
|
}
|
2017-03-27 16:32:49 +02:00
|
|
|
|
2022-01-04 12:00:11 +01:00
|
|
|
// if (transportId == 0) // no idea wat it means
|
|
|
|
// return;
|
2018-05-19 15:51:34 +02:00
|
|
|
|
|
|
|
uint32_t segmentSize = ((data [index + 0] & 0x1F) << 8) |
|
|
|
|
data [index + 1];
|
2022-01-04 12:00:11 +01:00
|
|
|
|
2018-05-19 15:51:34 +02:00
|
|
|
// handling MOT in the PAD, we only deal here with type 3/4
|
|
|
|
switch (groupType) {
|
|
|
|
case 3:
|
2018-07-10 11:33:27 +02:00
|
|
|
if (currentSlide == nullptr) {
|
2018-05-27 12:58:08 +02:00
|
|
|
// fprintf (stderr, "creating %d\n", transportId);
|
2025-05-27 18:50:45 +02:00
|
|
|
currentSlide. reset (new motObject (myRadioInterface,
|
2018-05-25 13:46:09 +02:00
|
|
|
false,
|
|
|
|
transportId,
|
|
|
|
&data [index + 2],
|
|
|
|
segmentSize,
|
2023-09-23 11:50:09 +02:00
|
|
|
lastFlag,
|
2025-05-27 18:50:45 +02:00
|
|
|
backgroundFlag));
|
2018-05-25 13:46:09 +02:00
|
|
|
}
|
|
|
|
else {
|
2019-07-08 23:30:45 +02:00
|
|
|
if (currentSlide -> get_transportId() == transportId)
|
2018-05-25 13:46:09 +02:00
|
|
|
break;
|
2018-05-27 12:58:08 +02:00
|
|
|
// fprintf (stderr, "out goes %d, in comes %d\n",
|
2019-07-08 23:30:45 +02:00
|
|
|
// currentSlide -> get_transportId(),
|
2018-05-27 12:58:08 +02:00
|
|
|
// transportId);
|
2025-05-27 18:50:45 +02:00
|
|
|
// delete currentSlide;
|
|
|
|
currentSlide. reset (new motObject (myRadioInterface,
|
2018-05-25 13:46:09 +02:00
|
|
|
false,
|
|
|
|
transportId,
|
|
|
|
&data [index + 2],
|
|
|
|
segmentSize,
|
2023-09-23 11:50:09 +02:00
|
|
|
lastFlag,
|
|
|
|
backgroundFlag
|
2025-05-27 18:50:45 +02:00
|
|
|
));
|
2018-05-25 13:46:09 +02:00
|
|
|
}
|
2018-05-19 15:51:34 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
2018-07-10 11:33:27 +02:00
|
|
|
if (currentSlide == nullptr)
|
2018-05-26 21:18:22 +02:00
|
|
|
return;
|
2019-07-08 23:30:45 +02:00
|
|
|
if (currentSlide -> get_transportId() == transportId) {
|
2018-05-27 12:58:08 +02:00
|
|
|
// fprintf (stderr, "add segment %d of %d\n",
|
|
|
|
// segmentNumber, transportId);
|
2018-05-19 15:51:34 +02:00
|
|
|
currentSlide -> addBodySegment (&data [index + 2],
|
|
|
|
segmentNumber,
|
|
|
|
segmentSize,
|
|
|
|
lastFlag);
|
2018-05-27 12:58:08 +02:00
|
|
|
}
|
2018-05-19 15:51:34 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: // cannot happen
|
2025-03-02 20:15:42 +01:00
|
|
|
fprintf (stderr, "Not yet handled mot in pad %d (%X)\n",
|
|
|
|
groupType, transportId);
|
2018-05-19 15:51:34 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
2025-06-07 18:45:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
void padHandler::add_toDL2 (const QString &text) {
|
|
|
|
if (the_DL2. dlsText != text) {
|
|
|
|
the_DL2. dlsText = text;
|
2025-07-20 19:25:14 +02:00
|
|
|
// fprintf (stderr, "dl2 fragment %s\n", text. toLatin1 (). data ());
|
2025-06-07 18:45:00 +02:00
|
|
|
the_DL2. valid = true; // non existent key
|
|
|
|
for (int i = 0; i < 4; i ++)
|
|
|
|
the_DL2. entity [i]. ct = 65;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-09 15:04:46 +02:00
|
|
|
void padHandler::add_toDL2 (const uint8_t *data) {
|
2025-06-07 18:45:00 +02:00
|
|
|
int IT = (data [0] & 0x08) >> 3;
|
|
|
|
int IR = (data [2] & 0x04) >> 2;
|
2025-07-31 21:30:27 +02:00
|
|
|
//int NT = data [2] & 0x03;
|
2025-07-20 19:25:14 +02:00
|
|
|
|
|
|
|
if ((the_DL2. IT == IT) && (the_DL2. IR == IR))
|
2025-06-07 18:45:00 +02:00
|
|
|
return;
|
2025-06-09 15:04:46 +02:00
|
|
|
|
2025-07-20 19:25:14 +02:00
|
|
|
if (the_DL2. IT != IT) { // toggle bit changed
|
|
|
|
the_DL2. IT = IT;
|
|
|
|
// if (IR != 0)
|
|
|
|
// fprintf (stderr, "Show a message\n");
|
|
|
|
// else
|
|
|
|
// fprintf (stderr, "hide a message\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (the_DL2. IR != IR) {
|
|
|
|
// if (IR == 0)
|
|
|
|
// fprintf (stderr, "Stop met de message\n");
|
|
|
|
// else
|
|
|
|
// fprintf (stderr, "Restart de message\n");
|
|
|
|
the_DL2. IR = IR;
|
2025-06-07 18:45:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|