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