1 /*
2 * Copyright (C) 2020
3 * Matthias P. Braendli (matthias.braendli@mpb.li)
4 *
5 * Copyright (C) 2014
6 * Jan van Katwijk (J.vanKatwijk@gmail.com)
7 * Lazy Chair Programming
8 *
9 * This file is part of the SDR-J (JSDR).
10 * SDR-J is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * SDR-J is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with SDR-J; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 * fib and fig processor
25 */
26 #include <iostream>
27 #include <algorithm>
28 #include <iomanip>
29 #include <cstring>
30
31 #include "fib-processor.h"
32 #include "charsets.h"
33 #include "MathHelper.h"
34
FIBProcessor(RadioControllerInterface & mr)35 FIBProcessor::FIBProcessor(RadioControllerInterface& mr) :
36 myRadioInterface(mr)
37 {
38 clearEnsemble();
39 }
40
41 // FIB's are segments of 256 bits. When here, we already
42 // passed the crc and we start unpacking into FIGs
43 // This is merely a dispatcher
processFIB(uint8_t * p,uint16_t fib)44 void FIBProcessor::processFIB(uint8_t *p, uint16_t fib)
45 {
46 int8_t processedBytes = 0;
47 uint8_t *d = p;
48
49 std::lock_guard<std::mutex> lock(mutex);
50
51 (void)fib;
52 while (processedBytes < 30) {
53 const uint8_t FIGtype = getBits_3 (d, 0);
54 switch (FIGtype) {
55 case 0:
56 process_FIG0(d);
57 break;
58
59 case 1:
60 process_FIG1(d);
61 break;
62
63 case 2:
64 process_FIG2(d);
65 break;
66
67 case 7:
68 return;
69
70 default:
71 //std::clog << "FIG%d present" << FIGtype << std::endl;
72 break;
73 }
74 // Thanks to Ronny Kunze, who discovered that I used
75 // a p rather than a d
76 processedBytes += getBits_5 (d, 3) + 1;
77 d = p + processedBytes * 8;
78 }
79 }
80 //
81 // Handle ensemble is all through FIG0
82 //
process_FIG0(uint8_t * d)83 void FIBProcessor::process_FIG0 (uint8_t *d)
84 {
85 uint8_t extension = getBits_5 (d, 8 + 3);
86 //uint8_t CN = getBits_1 (d, 8 + 0);
87
88 switch (extension) {
89 case 0: FIG0Extension0 (d); break;
90 case 1: FIG0Extension1 (d); break;
91 case 2: FIG0Extension2 (d); break;
92 case 3: FIG0Extension3 (d); break;
93 case 5: FIG0Extension5 (d); break;
94 case 8: FIG0Extension8 (d); break;
95 case 9: FIG0Extension9 (d); break;
96 case 10: FIG0Extension10 (d); break;
97 case 14: FIG0Extension14 (d); break;
98 case 13: FIG0Extension13 (d); break;
99 case 17: FIG0Extension17 (d); break;
100 case 18: FIG0Extension18 (d); break;
101 case 19: FIG0Extension19 (d); break;
102 case 21: FIG0Extension21 (d); break;
103 case 22: FIG0Extension22 (d); break;
104 default:
105 // std::clog << "fib-processor:" << "FIG0/%d passed by\n", extension) << std::endl;
106 break;
107 }
108 }
109
110
111 // FIG0/0 indicated a change in channel organization
112 // we are not equipped for that, so we just return
113 // control to the init
FIG0Extension0(uint8_t * d)114 void FIBProcessor::FIG0Extension0 (uint8_t *d)
115 {
116 uint8_t changeflag;
117 uint16_t highpart, lowpart;
118 int16_t occurrenceChange;
119 uint8_t CN = getBits_1 (d, 8 + 0);
120 (void)CN;
121
122 uint16_t eId = getBits(d, 16, 16);
123
124 if (ensembleId != eId) {
125 ensembleId = eId;
126 myRadioInterface.onNewEnsemble(ensembleId);
127 }
128
129 changeflag = getBits_2 (d, 16 + 16);
130
131 highpart = getBits_5 (d, 16 + 19) % 20;
132 (void)highpart;
133 lowpart = getBits_8 (d, 16 + 24) % 250;
134 (void)lowpart;
135 occurrenceChange = getBits_8 (d, 16 + 32);
136 (void)occurrenceChange;
137
138 // In transmission mode I, because four ETI frames make one transmission frame, we will
139 // see lowpart == 0 only every twelve seconds, and not 6 as expected by the 250 overflow value.
140 if (lowpart == 0) {
141 timeLastFCT0Frame = std::chrono::system_clock::now();
142 }
143
144 if (changeflag == 0)
145 return;
146 // else if (changeflag == 1) {
147 // std::clog << "fib-processor:" << "Changes in sub channel organization\n") << std::endl;
148 // std::clog << "fib-processor:" << "cifcount = %d\n", highpart * 250 + lowpart) << std::endl;
149 // std::clog << "fib-processor:" << "Change happening in %d CIFs\n", occurrenceChange) << std::endl;
150 // }
151 // else if (changeflag == 3) {
152 // std::clog << "fib-processor:" << "Changes in subchannel and service organization\n") << std::endl;
153 // std::clog << "fib-processor:" << "cifcount = %d\n", highpart * 250 + lowpart) << std::endl;
154 // std::clog << "fib-processor:" << "Change happening in %d CIFs\n", occurrenceChange) << std::endl;
155 // }
156 std::clog << "fib-processor: " << "changes in config not supported, choose again" << std::endl;
157 }
158
159 // FIG0 extension 1 creates a mapping between the
160 // sub channel identifications and the positions in the
161 // relevant CIF.
FIG0Extension1(uint8_t * d)162 void FIBProcessor::FIG0Extension1 (uint8_t *d)
163 {
164 int16_t used = 2; // offset in bytes
165 int16_t Length = getBits_5 (d, 3);
166 uint8_t PD_bit = getBits_1 (d, 8 + 2);
167 //uint8_t CN = getBits_1 (d, 8 + 0);
168
169 while (used < Length - 1)
170 used = HandleFIG0Extension1 (d, used, PD_bit);
171 }
172
173 // defining the channels
HandleFIG0Extension1(uint8_t * d,int16_t offset,uint8_t pd)174 int16_t FIBProcessor::HandleFIG0Extension1(
175 uint8_t *d,
176 int16_t offset,
177 uint8_t pd)
178 {
179 int16_t bitOffset = offset * 8;
180 const int16_t subChId = getBits_6 (d, bitOffset);
181 const int16_t startAdr = getBits(d, bitOffset + 6, 10);
182 subChannels[subChId].programmeNotData = pd;
183 subChannels[subChId].subChId = subChId;
184 subChannels[subChId].startAddr = startAdr;
185 if (getBits_1 (d, bitOffset + 16) == 0) { // UEP, short form
186 int16_t tableIx = getBits_6 (d, bitOffset + 18);
187 auto& ps = subChannels[subChId].protectionSettings;
188 ps.uepTableIndex = tableIx;
189 ps.shortForm = true;
190 ps.uepLevel = ProtLevel[tableIx][1];
191
192 subChannels[subChId].length = ProtLevel[tableIx][0];
193 bitOffset += 24;
194 }
195 else { // EEP, long form
196 auto& ps = subChannels[subChId].protectionSettings;
197 ps.shortForm = false;
198 int16_t option = getBits_3(d, bitOffset + 17);
199 if (option == 0) {
200 ps.eepProfile = EEPProtectionProfile::EEP_A;
201 }
202 else if (option == 1) {
203 ps.eepProfile = EEPProtectionProfile::EEP_B;
204 }
205
206 if (option == 0 or // EEP-A protection
207 option == 1) { // EEP-B protection
208 int16_t protLevel = getBits_2(d, bitOffset + 20);
209 switch (protLevel) {
210 case 0:
211 ps.eepLevel = EEPProtectionLevel::EEP_1;
212 break;
213 case 1:
214 ps.eepLevel = EEPProtectionLevel::EEP_2;
215 break;
216 case 2:
217 ps.eepLevel = EEPProtectionLevel::EEP_3;
218 break;
219 case 3:
220 ps.eepLevel = EEPProtectionLevel::EEP_4;
221 break;
222 default:
223 std::clog << "Warning, FIG0/1 for " << subChId <<
224 " has invalid EEP protection level " << protLevel <<
225 std::endl;
226 break;
227 }
228
229 int16_t subChanSize = getBits(d, bitOffset + 22, 10);
230 subChannels[subChId].length = subChanSize;
231 }
232 else {
233 std::clog << "Warning, FIG0/1 for " << subChId <<
234 " has invalid protection option " << option << std::endl;
235 }
236
237 bitOffset += 32;
238 }
239
240 return bitOffset / 8; // we return bytes
241 }
242
FIG0Extension2(uint8_t * d)243 void FIBProcessor::FIG0Extension2 (uint8_t *d)
244 {
245 int16_t used = 2; // offset in bytes
246 int16_t Length = getBits_5 (d, 3);
247 uint8_t PD_bit = getBits_1 (d, 8 + 2);
248 uint8_t CN = getBits_1 (d, 8 + 0);
249
250 while (used < Length) {
251 used = HandleFIG0Extension2(d, used, CN, PD_bit);
252 }
253 }
254
255 // Note Offset is in bytes
256 // With FIG0/2 we bind the channels to Service Ids
HandleFIG0Extension2(uint8_t * d,int16_t offset,uint8_t cn,uint8_t pd)257 int16_t FIBProcessor::HandleFIG0Extension2(
258 uint8_t *d,
259 int16_t offset,
260 uint8_t cn,
261 uint8_t pd)
262 {
263 (void)cn;
264 int16_t lOffset = 8 * offset;
265 int16_t i;
266 uint8_t ecc;
267 uint8_t cId;
268 uint32_t SId;
269 int16_t numberofComponents;
270
271 if (pd == 1) { // long Sid
272 ecc = getBits_8(d, lOffset); (void)ecc;
273 cId = getBits_4(d, lOffset + 1);
274 SId = getBits(d, lOffset, 32);
275 lOffset += 32;
276 }
277 else {
278 cId = getBits_4(d, lOffset); (void)cId;
279 SId = getBits(d, lOffset + 4, 12);
280 SId = getBits(d, lOffset, 16);
281 lOffset += 16;
282 }
283
284 // Keep track how often we see a service using a saturating counter.
285 // Every time a service is signalled, we increment the counter.
286 // If the counter is >= 2, we consider the service. Every second, we
287 // decrement all counters by one.
288 // This avoids that misdecoded services appear and stay in the list.
289 using namespace std::chrono;
290 const auto now = steady_clock::now();
291 if (timeLastServiceDecrement + seconds(1) < now) {
292
293 auto it = serviceRepeatCount.begin();
294 while (it != serviceRepeatCount.end()) {
295 if (it->second > 0) {
296 it->second--;
297 ++it;
298 }
299 else if (it->second == 0) {
300 dropService(it->second);
301 it = serviceRepeatCount.erase(it);
302 }
303 else {
304 ++it;
305 }
306 }
307
308 timeLastServiceDecrement = now;
309
310 #if 0
311 std::stringstream ss;
312 ss << "Counters: ";
313 for (auto& c : serviceRepeatCount) {
314 ss << " " << c.first << ":" << (int)c.second;
315 }
316 std::cerr << ss.str() << std::endl;
317 #endif
318 }
319
320 if (serviceRepeatCount[SId] < 4) {
321 serviceRepeatCount[SId]++;
322 }
323
324 if (findServiceId(SId) == nullptr and serviceRepeatCount[SId] >= 2) {
325 services.emplace_back(SId);
326 myRadioInterface.onServiceDetected(SId);
327 }
328
329 numberofComponents = getBits_4(d, lOffset + 4);
330 lOffset += 8;
331
332 for (i = 0; i < numberofComponents; i ++) {
333 uint8_t TMid = getBits_2 (d, lOffset);
334 if (TMid == 00) { // Audio
335 uint8_t ASCTy = getBits_6 (d, lOffset + 2);
336 uint8_t SubChId = getBits_6 (d, lOffset + 8);
337 uint8_t PS_flag = getBits_1 (d, lOffset + 14);
338 bindAudioService(TMid, SId, i, SubChId, PS_flag, ASCTy);
339 }
340 else if (TMid == 1) { // MSC stream data
341 uint8_t DSCTy = getBits_6 (d, lOffset + 2);
342 uint8_t SubChId = getBits_6 (d, lOffset + 8);
343 uint8_t PS_flag = getBits_1 (d, lOffset + 14);
344 bindDataStreamService(TMid, SId, i, SubChId, PS_flag, DSCTy);
345 }
346 else if (TMid == 3) { // MSC packet data
347 int16_t SCId = getBits (d, lOffset + 2, 12);
348 uint8_t PS_flag = getBits_1 (d, lOffset + 14);
349 uint8_t CA_flag = getBits_1 (d, lOffset + 15);
350 bindPacketService(TMid, SId, i, SCId, PS_flag, CA_flag);
351 }
352 else {
353 // reserved
354 }
355 lOffset += 16;
356 }
357 return lOffset / 8; // in Bytes
358 }
359
360 // The Extension 3 of FIG type 0 (FIG 0/3) gives
361 // additional information about the service component
362 // description in packet mode.
363 // manual: page 55
FIG0Extension3(uint8_t * d)364 void FIBProcessor::FIG0Extension3 (uint8_t *d)
365 {
366 int16_t used = 2;
367 int16_t Length = getBits_5 (d, 3);
368
369 while (used < Length) {
370 used = HandleFIG0Extension3 (d, used);
371 }
372 }
373
374 // DSCTy DataService Component Type
HandleFIG0Extension3(uint8_t * d,int16_t used)375 int16_t FIBProcessor::HandleFIG0Extension3(uint8_t *d, int16_t used)
376 {
377 int16_t SCId = getBits (d, used * 8, 12);
378 //int16_t CAOrgflag = getBits_1 (d, used * 8 + 15);
379 int16_t DGflag = getBits_1 (d, used * 8 + 16);
380 int16_t DSCTy = getBits_6 (d, used * 8 + 18);
381 int16_t SubChId = getBits_6 (d, used * 8 + 24);
382 int16_t packetAddress = getBits (d, used * 8 + 30, 10);
383 //uint16_t CAOrg = getBits (d, used * 8 + 40, 16);
384
385 ServiceComponent *packetComp = findPacketComponent(SCId);
386
387 used += 56 / 8;
388 if (packetComp) {
389 packetComp->subchannelId = SubChId;
390 packetComp->DSCTy = DSCTy;
391 packetComp->DGflag = DGflag;
392 packetComp->packetAddress = packetAddress;
393 }
394 return used;
395 }
396
FIG0Extension5(uint8_t * d)397 void FIBProcessor::FIG0Extension5 (uint8_t *d)
398 {
399 int16_t used = 2; // offset in bytes
400 int16_t Length = getBits_5 (d, 3);
401
402 while (used < Length) {
403 used = HandleFIG0Extension5 (d, used);
404 }
405 }
406
HandleFIG0Extension5(uint8_t * d,int16_t offset)407 int16_t FIBProcessor::HandleFIG0Extension5(uint8_t* d, int16_t offset)
408 {
409 int16_t loffset = offset * 8;
410 uint8_t lsFlag = getBits_1 (d, loffset);
411 int16_t subChId, serviceComp, language;
412
413 if (lsFlag == 0) { // short form
414 if (getBits_1 (d, loffset + 1) == 0) {
415 subChId = getBits_6 (d, loffset + 2);
416 language = getBits_8 (d, loffset + 8);
417 subChannels[subChId].language = language;
418 }
419 loffset += 16;
420 }
421 else { // long form
422 serviceComp = getBits (d, loffset + 4, 12);
423 language = getBits_8 (d, loffset + 16);
424 loffset += 24;
425 }
426 (void)serviceComp;
427
428 return loffset / 8;
429 }
430
FIG0Extension8(uint8_t * d)431 void FIBProcessor::FIG0Extension8 (uint8_t *d)
432 {
433 int16_t used = 2; // offset in bytes
434 int16_t Length = getBits_5 (d, 3);
435 uint8_t PD_bit = getBits_1 (d, 8 + 2);
436
437 while (used < Length) {
438 used = HandleFIG0Extension8 (d, used, PD_bit);
439 }
440 }
441
HandleFIG0Extension8(uint8_t * d,int16_t used,uint8_t pdBit)442 int16_t FIBProcessor::HandleFIG0Extension8(
443 uint8_t *d,
444 int16_t used,
445 uint8_t pdBit)
446 {
447 int16_t lOffset = used * 8;
448 uint32_t SId = getBits(d, lOffset, pdBit == 1 ? 32 : 16);
449 lOffset += (pdBit == 1 ? 32 : 16);
450
451 uint8_t extensionFlag;
452
453 extensionFlag = getBits_1(d, lOffset);
454 uint16_t SCIds = getBits_4(d, lOffset + 4);
455 lOffset += 4;
456
457 uint8_t lsFlag = getBits_1(d, lOffset);
458 if (lsFlag == 1) {
459 int16_t SCid = getBits(d, lOffset + 4, 12);
460 lOffset += 16;
461 // if (findPacketComponent ((SCIds << 4) | SCid) != NULL) {
462 // std::clog << "fib-processor:" << "packet component bestaat !!\n") << std::endl;
463 // }
464 }
465 else {
466 int16_t SubChId = getBits_6(d, lOffset + 4);
467 lOffset += 8;
468 }
469
470 if (extensionFlag) {
471 lOffset += 8; // skip Rfa
472 }
473 (void)SId;
474 (void)SCIds;
475 return lOffset / 8;
476 }
477
478 // FIG0/9 and FIG0/10 are copied from the work of
479 // Michael Hoehn
FIG0Extension9(uint8_t * d)480 void FIBProcessor::FIG0Extension9(uint8_t *d)
481 {
482 int16_t offset = 16;
483
484 dateTime.hourOffset = (getBits_1 (d, offset + 2) == 1) ?
485 -1 * getBits_4 (d, offset + 3):
486 getBits_4 (d, offset + 3);
487 dateTime.minuteOffset = (getBits_1 (d, offset + 7) == 1) ? 30 : 0;
488 timeOffsetReceived = true;
489
490 ensembleEcc = getBits(d, offset + 8, 8);
491 }
492
FIG0Extension10(uint8_t * fig)493 void FIBProcessor::FIG0Extension10(uint8_t *fig)
494 {
495 int16_t offset = 16;
496 int32_t mjd = getBits(fig, offset + 1, 17);
497 // Convert Modified Julian Date (according to wikipedia)
498 int32_t J = mjd + 2400001;
499 int32_t j = J + 32044;
500 int32_t g = j / 146097;
501 int32_t dg = j % 146097;
502 int32_t c = ((dg / 36524) + 1) * 3 / 4;
503 int32_t dc = dg - c * 36524;
504 int32_t b = dc / 1461;
505 int32_t db = dc%1461;
506 int32_t a = ((db / 365) + 1) * 3 / 4;
507 int32_t da = db - a * 365;
508 int32_t y = g * 400 + c * 100 + b * 4 + a;
509 int32_t m = ((da * 5 + 308) / 153) - 2;
510 int32_t d = da - ((m + 4) * 153 / 5) + 122;
511 int32_t Y = y - 4800 + ((m + 2) / 12);
512 int32_t M = ((m + 2) % 12) + 1;
513 int32_t D = d + 1;
514
515 dateTime.year = Y;
516 dateTime.month = M;
517 dateTime.day = D;
518 dateTime.hour = getBits_5(fig, offset + 21);
519 if (getBits_6(fig, offset + 26) != dateTime.minutes)
520 dateTime.seconds = 0; // handle overflow
521
522 dateTime.minutes = getBits_6(fig, offset + 26);
523 if (fig [offset + 20] == 1) {
524 dateTime.seconds = getBits_6(fig, offset + 32);
525 }
526
527 if (timeOffsetReceived) {
528 myRadioInterface.onDateTimeUpdate(dateTime);
529 }
530 }
531
FIG0Extension13(uint8_t * d)532 void FIBProcessor::FIG0Extension13 (uint8_t *d)
533 {
534 int16_t used = 2; // offset in bytes
535 int16_t Length = getBits_5 (d, 3);
536 uint8_t PD_bit = getBits_1 (d, 8 + 2);
537
538 while (used < Length) {
539 used = HandleFIG0Extension13 (d, used, PD_bit);
540 }
541 }
542
HandleFIG0Extension13(uint8_t * d,int16_t used,uint8_t pdBit)543 int16_t FIBProcessor::HandleFIG0Extension13(
544 uint8_t *d,
545 int16_t used,
546 uint8_t pdBit)
547 {
548 int16_t lOffset = used * 8;
549 uint32_t SId = getBits(d, lOffset, pdBit == 1 ? 32 : 16);
550 uint16_t SCIds;
551 int16_t NoApplications;
552 int16_t i;
553
554 lOffset += pdBit == 1 ? 32 : 16;
555 SCIds = getBits_4 (d, lOffset);
556 NoApplications = getBits_4 (d, lOffset + 4);
557 lOffset += 8;
558
559 // std::clog << "fib-processor: HandleFIG0Extension13 NoApplications " << NoApplications << " ";
560
561 for (i = 0; i < NoApplications; i++) {
562 int16_t appType = getBits (d, lOffset, 11);
563 int16_t length = getBits_5 (d, lOffset + 11);
564 lOffset += (11 + 5 + 8 * length);
565 // std::clog << " ";
566 switch (appType) {
567 case 0x000: // reserved for future use
568 case 0x001: // not used
569 break;
570
571 case 0x002: // MOT slideshow
572 // std::clog << "MOT slideshow"; break;
573 case 0x003: // MOT Broadcast Web Site
574 // std::clog << "MOT Broadcast Web Site "; break;
575 case 0x004: // TPEG
576 // std::clog << "TPEG length " << length; break;
577 case 0x005: // DGPS
578 // std::clog << "DGPS"; break;
579 case 0x006: // TMC
580 // std::clog << "TMC "; break;
581 case 0x007: // EPG
582 // std::clog << "EPG length " << length; break;
583 case 0x008: // DAB Java
584 // std::clog << "DAB Java"; break;
585 case 0x009: // DMB
586 // std::clog << "DMB"; break;
587 case 0x00a: // IPDC services
588 // std::clog << "IPDC services"; break;
589 case 0x00b: // Voice applications
590 // std::clog << "Voice applications"; break;
591 case 0x00c: // Middleware
592 // std::clog << "Middleware"; break;
593 case 0x00d: // Filecasting
594 // std::clog << "Filecasting"; break;
595 case 0x44a: // Journaline
596 // std::clog << "Journaline"; break;
597
598 default:
599 break;
600 }
601 }
602
603 // std::clog << std::endl;
604
605 (void)SId;
606 (void)SCIds;
607 return lOffset / 8;
608 }
609
FIG0Extension14(uint8_t * d)610 void FIBProcessor::FIG0Extension14 (uint8_t *d)
611 {
612 int16_t length = getBits_5 (d, 3); // in Bytes
613 int16_t used = 2; // in Bytes
614
615 while (used < length) {
616 int16_t subChId = getBits_6 (d, used * 8);
617 uint8_t fecScheme = getBits_2 (d, used * 8 + 6);
618 used = used + 1;
619
620 for (int i = 0; i < 64; i++) {
621 if (subChannels[i].subChId == subChId) {
622 subChannels[i].fecScheme = fecScheme;
623 }
624 }
625
626 }
627 }
628
FIG0Extension17(uint8_t * d)629 void FIBProcessor::FIG0Extension17(uint8_t *d)
630 {
631 int16_t length = getBits_5 (d, 3);
632 int16_t offset = 16;
633 Service *s;
634
635 while (offset < length * 8) {
636 uint16_t SId = getBits (d, offset, 16);
637 bool L_flag = getBits_1 (d, offset + 18);
638 bool CC_flag = getBits_1 (d, offset + 19);
639 int16_t type;
640 int16_t Language = 0x00; // init with unknown language
641 s = findServiceId(SId);
642 if (L_flag) { // language field present
643 Language = getBits_8 (d, offset + 24);
644 if (s) {
645 s->language = Language;
646 }
647 offset += 8;
648 }
649
650 type = getBits_5 (d, offset + 27);
651 if (s) {
652 s->programType = type;
653 }
654 if (CC_flag) { // cc flag
655 offset += 40;
656 }
657 else {
658 offset += 32;
659 }
660 }
661 }
662
FIG0Extension18(uint8_t * d)663 void FIBProcessor::FIG0Extension18(uint8_t *d)
664 {
665 int16_t offset = 16; // bits
666 uint16_t SId, AsuFlags;
667 int16_t Length = getBits_5 (d, 3);
668
669 while (offset / 8 < Length - 1 ) {
670 int16_t NumClusters = getBits_5 (d, offset + 35);
671 SId = getBits (d, offset, 16);
672 AsuFlags = getBits (d, offset + 16, 16);
673 // std::clog << "fib-processor:" << "Announcement %d for SId %d with %d clusters\n",
674 // AsuFlags, SId, NumClusters) << std::endl;
675 offset += 40 + NumClusters * 8;
676 }
677 (void)SId;
678 (void)AsuFlags;
679 }
680
FIG0Extension19(uint8_t * d)681 void FIBProcessor::FIG0Extension19(uint8_t *d)
682 {
683 int16_t offset = 16; // bits
684 int16_t Length = getBits_5 (d, 3);
685 uint8_t region_Id_Lower;
686
687 while (offset / 8 < Length - 1) {
688 uint8_t clusterId = getBits_8 (d, offset);
689 bool new_flag = getBits_1(d, offset + 24);
690 bool region_flag = getBits_1 (d, offset + 25);
691 uint8_t subChId = getBits_6 (d, offset + 26);
692
693 uint16_t aswFlags = getBits (d, offset + 8, 16);
694 // std::clog << "fib-processor:" <<
695 // "%s %s Announcement %d for Cluster %2u on SubCh %2u ",
696 // ((new_flag==1)?"new":"old"),
697 // ((region_flag==1)?"regional":""),
698 // aswFlags, clusterId,subChId) << std::endl;
699 if (region_flag) {
700 region_Id_Lower = getBits_6 (d, offset + 34);
701 offset += 40;
702 // fprintf(stderr,"for region %u",region_Id_Lower);
703 }
704 else {
705 offset += 32;
706 }
707
708 // fprintf(stderr,"\n");
709 (void)clusterId;
710 (void)new_flag;
711 (void)subChId;
712 (void)aswFlags;
713 }
714 (void)region_Id_Lower;
715 }
716
FIG0Extension21(uint8_t * d)717 void FIBProcessor::FIG0Extension21(uint8_t *d)
718 {
719 // std::clog << "fib-processor:" << "Frequency information\n") << std::endl;
720 (void)d;
721 }
722
FIG0Extension22(uint8_t * d)723 void FIBProcessor::FIG0Extension22(uint8_t *d)
724 {
725 int16_t Length = getBits_5 (d, 3);
726 int16_t offset = 16; // on bits
727 int16_t used = 2;
728
729 while (used < Length) {
730 used = HandleFIG0Extension22 (d, used);
731 }
732 (void)offset;
733 }
734
HandleFIG0Extension22(uint8_t * d,int16_t used)735 int16_t FIBProcessor::HandleFIG0Extension22(uint8_t *d, int16_t used)
736 {
737 uint8_t MS;
738 int16_t mainId;
739 int16_t noSubfields;
740
741 mainId = getBits_7 (d, used * 8 + 1);
742 (void)mainId;
743 MS = getBits_1 (d, used * 8);
744 if (MS == 0) { // fixed size
745 int16_t latitudeCoarse = getBits (d, used * 8 + 8, 16);
746 int16_t longitudeCoarse = getBits (d, used * 8 + 24, 16);
747 // std::clog << "fib-processor:" << "Id = %d, (%d %d)\n", mainId,
748 // latitudeCoarse, longitudeCoarse) << std::endl;
749 (void)latitudeCoarse;
750 (void)longitudeCoarse;
751 return used + 48 / 6;
752 }
753 // MS == 1
754
755 noSubfields = getBits_3 (d, used * 8 + 13);
756 // std::clog << "fib-processor:" << "Id = %d, subfields = %d\n", mainId, noSubfields) << std::endl;
757 used += (16 + noSubfields * 48) / 8;
758
759 return used;
760 }
761
762 // FIG 1 - Labels
process_FIG1(uint8_t * d)763 void FIBProcessor::process_FIG1(uint8_t *d)
764 {
765 uint32_t SId = 0;
766 int16_t offset = 0;
767 Service *service;
768 ServiceComponent *component;
769 uint8_t pd_flag;
770 uint8_t SCidS;
771 char label[17];
772
773 // FIG 1 first byte
774 const uint8_t charSet = getBits_4(d, 8);
775 const uint8_t oe = getBits_1(d, 8 + 4);
776 const uint8_t extension = getBits_3(d, 8 + 5);
777 label[16] = 0x00;
778 if (oe == 1) {
779 return;
780 }
781
782 switch (extension) {
783 case 0: // ensemble label
784 {
785 const uint32_t EId = getBits(d, 16, 16);
786 offset = 32;
787 for (int i = 0; i < 16; i ++) {
788 label[i] = getBits_8 (d, offset);
789 offset += 8;
790 }
791 // std::clog << "fib-processor:" << "Ensemblename: " << label << std::endl;
792 if (!oe and EId == ensembleId) {
793 ensembleLabel.fig1_flag = getBits(d, offset, 16);
794 ensembleLabel.fig1_label = label;
795 ensembleLabel.setCharset(charSet);
796 myRadioInterface.onSetEnsembleLabel(ensembleLabel);
797 }
798 break;
799 }
800
801 case 1: // 16 bit Identifier field for service label
802 SId = getBits(d, 16, 16);
803 offset = 32;
804 service = findServiceId(SId);
805 if (service) {
806 for (int i = 0; i < 16; i++) {
807 label[i] = getBits_8(d, offset);
808 offset += 8;
809 }
810 service->serviceLabel.fig1_flag = getBits(d, offset, 16);
811 service->serviceLabel.fig1_label = label;
812 service->serviceLabel.setCharset(charSet);
813 // std::clog << "fib-processor:" << "FIG1/1: SId = %4x\t%s\n", SId, label) << std::endl;
814 }
815 break;
816
817 /*
818 case 3: // Region label
819 //uint8_t region_id = getBits_6 (d, 16 + 2);
820 offset = 24;
821 for (int i = 0; i < 16; i ++) {
822 label[i] = getBits_8 (d, offset + 8 * i);
823 }
824
825 // std::clog << "fib-processor:" << "FIG1/3: RegionID = %2x\t%s\n", region_id, label) << std::endl;
826 break;
827 */
828
829 case 4: // Component label
830 pd_flag = getBits(d, 16, 1);
831 SCidS = getBits(d, 20, 4);
832 if (pd_flag) { // 32 bit identifier field for service component label
833 SId = getBits(d, 24, 32);
834 offset = 56;
835 }
836 else { // 16 bit identifier field for service component label
837 SId = getBits(d, 24, 16);
838 offset = 40;
839 }
840
841 for (int i = 0; i < 16; i ++) {
842 label[i] = getBits_8 (d, offset);
843 offset += 8;
844 }
845
846 component = findComponent(SId, SCidS);
847 if (component) {
848 component->componentLabel.fig1_flag = getBits(d, offset, 16);
849 component->componentLabel.setCharset(charSet);
850 component->componentLabel.fig1_label = label;
851 }
852 // std::clog << "fib-processor:" << "FIG1/4: Sid = %8x\tp/d=%d\tSCidS=%1X\tflag=%8X\t%s\n",
853 // SId, pd_flag, SCidS, flagfield, label) << std::endl;
854 break;
855
856
857 case 5: // 32 bit Identifier field for service label
858 SId = getBits(d, 16, 32);
859 offset = 48;
860 service = findServiceId(SId);
861 if (service) {
862 for (int i = 0; i < 16; i ++) {
863 label[i] = getBits_8(d, offset);
864 offset += 8;
865 }
866 service->serviceLabel.fig1_flag = getBits(d, offset, 16);
867 service->serviceLabel.fig1_label = label;
868 service->serviceLabel.setCharset(charSet);
869
870 #ifdef MSC_DATA__
871 myRadioInterface.onServiceDetected(SId);
872 #endif
873 }
874 break;
875
876 /*
877 case 6: // XPAD label
878 uint8_t XPAD_aid;
879 pd_flag = getBits(d, 16, 1);
880 SCidS = getBits(d, 20, 4);
881 if (pd_flag) { // 32 bits identifier for XPAD label
882 SId = getBits(d, 24, 32);
883 XPAD_aid = getBits(d, 59, 5);
884 offset = 64;
885 }
886 else { // 16 bit identifier for XPAD label
887 SId = getBits(d, 24, 16);
888 XPAD_aid = getBits(d, 43, 5);
889 offset = 48;
890 }
891
892 for (int i = 0; i < 16; i ++) {
893 label[i] = getBits_8 (d, offset + 8 * i);
894 }
895
896 // fprintf(stderr, "fib-processor:"
897 // "FIG1/6: SId = %8x\tp/d = %d\t SCidS = %1X\tXPAD_aid = %2u\t%s\n",
898 // SId, pd_flag, SCidS, XPAD_aid, label) << std::endl;
899 break;
900 */
901
902 default:
903 // std::clog << "fib-processor:" << "FIG1/%d: not handled now\n", extension) << std::endl;
904 break;
905 }
906 }
907
handle_ext_label_data_field(const uint8_t * f,uint8_t len_bytes,bool toggle_flag,uint8_t segment_index,uint8_t rfu,DabLabel & label)908 static void handle_ext_label_data_field(const uint8_t *f, uint8_t len_bytes,
909 bool toggle_flag, uint8_t segment_index, uint8_t rfu,
910 DabLabel& label)
911 {
912 if (label.toggle_flag != toggle_flag) {
913 label.segments.clear();
914 label.extended_label_charset = CharacterSet::Undefined;
915 label.toggle_flag = toggle_flag;
916 }
917
918 size_t len_character_field = len_bytes;
919
920 if (segment_index == 0) {
921 // Only if it's the first segment
922 const uint8_t encoding_flag = (f[0] & 0x80) >> 7;
923 const uint8_t segment_count = (f[0] & 0x70) >> 4;
924 label.segment_count = segment_count + 1;
925
926 if (encoding_flag) {
927 label.extended_label_charset = CharacterSet::UnicodeUcs2;
928 }
929 else {
930 label.extended_label_charset = CharacterSet::UnicodeUtf8;
931 }
932
933 if (rfu == 0) {
934 // const uint8_t rfa = (f[0] & 0x0F);
935 // const uint16_t char_flag = f[1] * 256 + f[2];
936
937 if (len_bytes <= 3) {
938 throw std::runtime_error("FIG2 label length too short");
939 }
940
941 f += 3;
942 len_character_field -= 3;
943 }
944 else {
945 // ETSI TS 103 176 draft V2.2.1 (2018-08) gives a new meaning to rfu
946 // TODO const uint8_t text_control = (f[0] & 0x0F);
947
948 if (len_bytes <= 1) {
949 throw std::runtime_error("FIG2 label length too short");
950 }
951
952 f += 1;
953 len_character_field -= 1;
954 }
955
956 label.fig2_rfu = rfu;
957 }
958
959 std::vector<uint8_t> labelbytes(f, f + len_character_field);
960 label.segments[segment_index] = labelbytes;
961 }
962
963 // UTF-8 or UCS2 Labels
process_FIG2(uint8_t * d)964 void FIBProcessor::process_FIG2(uint8_t *d)
965 {
966 // In order to reuse code with etisnoop, convert
967 // the bit-vector into a byte-vector
968 std::vector<uint8_t> fig_bytes;
969 for (size_t i = 0; i < 30; i++) {
970 fig_bytes.push_back(getBits_8(d, 8*i));
971 }
972
973 uint8_t *f = fig_bytes.data();
974
975 const uint8_t figlen = f[0] & 0x1F;
976 f++;
977
978 const uint8_t toggle_flag = (f[0] & 0x80) >> 7;
979 const uint8_t segment_index = (f[0] & 0x70) >> 4;
980 const uint16_t rfu = (f[0] & 0x08) >> 3;
981 const uint16_t ext = f[0] & 0x07;
982
983 size_t identifier_len;
984 switch (ext) {
985 case 0: // Ensemble label
986 identifier_len = 2;
987 break;
988 case 1: // Programme service label
989 identifier_len = 2;
990 break;
991 case 4: // Service component label
992 {
993 uint8_t pd = (f[1] & 0x80) >> 7;
994 identifier_len = (pd == 0) ? 3 : 5;
995 break;
996 }
997 case 5: // Data service label
998 identifier_len = 4;
999 break;
1000 default:
1001 return; // Unsupported
1002 }
1003
1004 const size_t header_length = 1; // FIG data field header
1005
1006 const uint8_t *figdata = f + header_length + identifier_len;
1007 const size_t data_len_bytes = figlen - header_length - identifier_len;
1008
1009 // ext is followed by Identifier field of Type 2 field,
1010 // whose length depends on ext
1011 switch (ext) {
1012 case 0: // Ensemble label
1013 { // ETSI EN 300 401 8.1.13
1014 uint16_t eid = f[1] * 256 + f[2];
1015 if (figlen <= header_length + identifier_len) {
1016 std::clog << "FIG2/0 length error " << (int)figlen << std::endl;
1017 }
1018 else if (eid == ensembleId) {
1019 handle_ext_label_data_field(figdata, data_len_bytes,
1020 toggle_flag, segment_index, rfu, ensembleLabel);
1021 }
1022 }
1023 break;
1024
1025 case 1: // Programme service label
1026 { // ETSI EN 300 401 8.1.14.1
1027 uint16_t sid = f[1] * 256 + f[2];
1028 if (figlen <= header_length + identifier_len) {
1029 std::clog << "FIG2/1 length error " << (int)figlen << std::endl;
1030 }
1031 else {
1032 auto *service = findServiceId(sid);
1033 if (service) {
1034 handle_ext_label_data_field(figdata, data_len_bytes,
1035 toggle_flag, segment_index, rfu, service->serviceLabel);
1036 }
1037 }
1038 }
1039 break;
1040
1041 case 4: // Service component label
1042 { // ETSI EN 300 401 8.1.14.3
1043 uint32_t sid;
1044 uint8_t pd = (f[1] & 0x80) >> 7;
1045 uint8_t SCIdS = f[1] & 0x0F;
1046 if (pd == 0) {
1047 sid = f[2] * 256 + \
1048 f[3];
1049 }
1050 else {
1051 sid = ((uint32_t)f[2] << 24) |
1052 ((uint32_t)f[3] << 16) |
1053 ((uint32_t)f[4] << 8) |
1054 ((uint32_t)f[5]);
1055 }
1056 if (figlen <= header_length + identifier_len) {
1057 std::clog << "FIG2/4 length error " << (int)figlen << std::endl;
1058 }
1059 else {
1060 auto *component = findComponent(sid, SCIdS);
1061 if (component) {
1062 handle_ext_label_data_field(figdata, data_len_bytes,
1063 toggle_flag, segment_index, rfu, component->componentLabel);
1064 }
1065 }
1066 }
1067 break;
1068
1069 case 5: // Data service label
1070 { // ETSI EN 300 401 8.1.14.2
1071 const uint32_t sid =
1072 ((uint32_t)f[1] << 24) |
1073 ((uint32_t)f[2] << 16) |
1074 ((uint32_t)f[3] << 8) |
1075 ((uint32_t)f[4]);
1076
1077 if (figlen <= header_length + identifier_len) {
1078 std::clog << "FIG2/5 length error " << (int)figlen << std::endl;
1079 }
1080 else {
1081 auto *service = findServiceId(sid);
1082 if (service) {
1083 handle_ext_label_data_field(figdata, data_len_bytes,
1084 toggle_flag, segment_index, rfu, service->serviceLabel);
1085 }
1086 }
1087 }
1088 break;
1089 }
1090 }
1091
1092 // locate a reference to the entry for the Service serviceId
findServiceId(uint32_t serviceId)1093 Service *FIBProcessor::findServiceId(uint32_t serviceId)
1094 {
1095 for (size_t i = 0; i < services.size(); i++) {
1096 if (services[i].serviceId == serviceId) {
1097 return &services[i];
1098 }
1099 }
1100
1101 return nullptr;
1102 }
1103
findComponent(uint32_t serviceId,int16_t SCIdS)1104 ServiceComponent *FIBProcessor::findComponent(uint32_t serviceId, int16_t SCIdS)
1105 {
1106 auto comp = std::find_if(components.begin(), components.end(),
1107 [&](const ServiceComponent& sc) {
1108 return sc.SId == serviceId && sc.componentNr == SCIdS;
1109 });
1110
1111 if (comp == components.end()) {
1112 return nullptr;
1113 }
1114 else {
1115 return &(*comp);
1116 }
1117 }
1118
findPacketComponent(int16_t SCId)1119 ServiceComponent *FIBProcessor::findPacketComponent(int16_t SCId)
1120 {
1121 for (auto& component : components) {
1122 if (component.TMid != 03) {
1123 continue;
1124 }
1125 if (component.SCId == SCId) {
1126 return &component;
1127 }
1128 }
1129 return nullptr;
1130 }
1131
1132 // bindAudioService is the main processor for - what the name suggests -
1133 // connecting the description of audioservices to a SID
bindAudioService(int8_t TMid,uint32_t SId,int16_t compnr,int16_t subChId,int16_t ps_flag,int16_t ASCTy)1134 void FIBProcessor::bindAudioService(
1135 int8_t TMid,
1136 uint32_t SId,
1137 int16_t compnr,
1138 int16_t subChId,
1139 int16_t ps_flag,
1140 int16_t ASCTy)
1141 {
1142 Service *s = findServiceId(SId);
1143 if (!s) return;
1144
1145 if (std::find_if(components.begin(), components.end(),
1146 [&](const ServiceComponent& sc) {
1147 return sc.SId == s->serviceId && sc.componentNr == compnr;
1148 }) == components.end()) {
1149 ServiceComponent newcomp;
1150 newcomp.TMid = TMid;
1151 newcomp.componentNr = compnr;
1152 newcomp.SId = SId;
1153 newcomp.subchannelId = subChId;
1154 newcomp.PS_flag = ps_flag;
1155 newcomp.ASCTy = ASCTy;
1156 components.push_back(newcomp);
1157
1158 // std::clog << "fib-processor:" << "service %8x (comp %d) is audio\n", SId, compnr) << std::endl;
1159 }
1160 }
1161
bindDataStreamService(int8_t TMid,uint32_t SId,int16_t compnr,int16_t subChId,int16_t ps_flag,int16_t DSCTy)1162 void FIBProcessor::bindDataStreamService(
1163 int8_t TMid,
1164 uint32_t SId,
1165 int16_t compnr,
1166 int16_t subChId,
1167 int16_t ps_flag,
1168 int16_t DSCTy)
1169 {
1170 Service *s = findServiceId(SId);
1171 if (!s) return;
1172
1173 if (std::find_if(components.begin(), components.end(),
1174 [&](const ServiceComponent& sc) {
1175 return sc.SId == s->serviceId && sc.componentNr == compnr;
1176 }) == components.end()) {
1177 ServiceComponent newcomp;
1178 newcomp.TMid = TMid;
1179 newcomp.SId = SId;
1180 newcomp.subchannelId = subChId;
1181 newcomp.componentNr = compnr;
1182 newcomp.PS_flag = ps_flag;
1183 newcomp.DSCTy = DSCTy;
1184 components.push_back(newcomp);
1185
1186 // std::clog << "fib-processor:" << "service %8x (comp %d) is packet\n", SId, compnr) << std::endl;
1187 }
1188 }
1189
1190 // bindPacketService is the main processor for - what the name suggests -
1191 // connecting the service component defining the service to the SId,
1192 /// Note that the subchannel is assigned through a FIG0/3
bindPacketService(int8_t TMid,uint32_t SId,int16_t compnr,int16_t SCId,int16_t ps_flag,int16_t CAflag)1193 void FIBProcessor::bindPacketService(
1194 int8_t TMid,
1195 uint32_t SId,
1196 int16_t compnr,
1197 int16_t SCId,
1198 int16_t ps_flag,
1199 int16_t CAflag)
1200 {
1201 Service *s = findServiceId(SId);
1202 if (!s) return;
1203
1204 if (std::find_if(components.begin(), components.end(),
1205 [&](const ServiceComponent& sc) {
1206 return sc.SId == s->serviceId && sc.componentNr == compnr;
1207 }) == components.end()) {
1208 ServiceComponent newcomp;
1209 newcomp.TMid = TMid;
1210 newcomp.SId = SId;
1211 newcomp.componentNr = compnr;
1212 newcomp.SCId = SCId;
1213 newcomp.PS_flag = ps_flag;
1214 newcomp.CAflag = CAflag;
1215 components.push_back(newcomp);
1216
1217 // std::clog << "fib-processor:" << "service %8x (comp %d) is packet\n", SId, compnr) << std::endl;
1218 }
1219 }
1220
dropService(uint32_t SId)1221 void FIBProcessor::dropService(uint32_t SId)
1222 {
1223 std::stringstream ss;
1224 ss << "Dropping service " << SId;
1225
1226 services.erase(std::remove_if(services.begin(), services.end(),
1227 [&](const Service& s) {
1228 return s.serviceId == SId;
1229 }
1230 ), services.end());
1231
1232 components.erase(std::remove_if(components.begin(), components.end(),
1233 [&](const ServiceComponent& c) {
1234 const bool drop = c.SId == SId;
1235 if (drop) {
1236 ss << ", comp " << c.componentNr;
1237 }
1238 return drop;
1239 }
1240 ), components.end());
1241
1242 // Check for orphaned subchannels
1243 for (auto& sub : subChannels) {
1244 if (sub.subChId == -1) {
1245 continue;
1246 }
1247
1248 auto c_it = std::find_if(components.begin(), components.end(),
1249 [&](const ServiceComponent& c) {
1250 return c.subchannelId == sub.subChId;
1251 });
1252
1253 const bool drop = c_it == components.end();
1254 if (drop) {
1255 ss << ", subch " << sub.subChId;
1256 sub.subChId = -1;
1257 }
1258 }
1259
1260 std::clog << ss.str() << std::endl;
1261 }
1262
clearEnsemble()1263 void FIBProcessor::clearEnsemble()
1264 {
1265 std::lock_guard<std::mutex> lock(mutex);
1266 components.clear();
1267 subChannels.resize(64);
1268 services.clear();
1269 serviceRepeatCount.clear();
1270 timeLastServiceDecrement = std::chrono::steady_clock::now();
1271 timeLastFCT0Frame = std::chrono::system_clock::now();
1272 }
1273
getServiceList() const1274 std::vector<Service> FIBProcessor::getServiceList() const
1275 {
1276 std::lock_guard<std::mutex> lock(mutex);
1277 return services;
1278 }
1279
getService(uint32_t sId) const1280 Service FIBProcessor::getService(uint32_t sId) const
1281 {
1282 std::lock_guard<std::mutex> lock(mutex);
1283
1284 auto srv = std::find_if(services.begin(), services.end(),
1285 [&](const Service& s) {
1286 return s.serviceId == sId;
1287 });
1288
1289 if (srv != services.end()) {
1290 return *srv;
1291 }
1292 else {
1293 return Service(0);
1294 }
1295 }
1296
getComponents(const Service & s) const1297 std::list<ServiceComponent> FIBProcessor::getComponents(const Service& s) const
1298 {
1299 std::list<ServiceComponent> c;
1300 std::lock_guard<std::mutex> lock(mutex);
1301 for (const auto& component : components) {
1302 if (component.SId == s.serviceId) {
1303 c.push_back(component);
1304 }
1305 }
1306
1307 return c;
1308 }
1309
getSubchannel(const ServiceComponent & sc) const1310 Subchannel FIBProcessor::getSubchannel(const ServiceComponent& sc) const
1311 {
1312 std::lock_guard<std::mutex> lock(mutex);
1313 return subChannels.at(sc.subchannelId);
1314 }
1315
getEnsembleId() const1316 uint16_t FIBProcessor::getEnsembleId() const
1317 {
1318 std::lock_guard<std::mutex> lock(mutex);
1319 return ensembleId;
1320 }
1321
getEnsembleEcc() const1322 uint8_t FIBProcessor::getEnsembleEcc() const
1323 {
1324 std::lock_guard<std::mutex> lock(mutex);
1325 return ensembleEcc;
1326 }
1327
getEnsembleLabel() const1328 DabLabel FIBProcessor::getEnsembleLabel() const
1329 {
1330 std::lock_guard<std::mutex> lock(mutex);
1331 return ensembleLabel;
1332 }
1333
getTimeLastFCT0Frame() const1334 std::chrono::system_clock::time_point FIBProcessor::getTimeLastFCT0Frame() const
1335 {
1336 std::lock_guard<std::mutex> lock(mutex);
1337 return timeLastFCT0Frame;
1338 }
1339