1 /*
2  *    Copyright (C) 2018
3  *    Matthias P. Braendli (matthias.braendli@mpb.li)
4  *
5  *    Copyright (C) 2017
6  *    Albrecht Lohofener (albrechtloh@gmx.de)
7  *
8  *    This file is based on SDR-J
9  *    Copyright (C) 2010, 2011, 2012
10  *    Jan van Katwijk (J.vanKatwijk@gmail.com)
11  *
12  *    This file is part of the welle.io.
13  *    Many of the ideas as implemented in welle.io are derived from
14  *    other work, made available through the GNU general Public License.
15  *    All copyrights of the original authors are recognized.
16  *
17  *    welle.io is free software; you can redistribute it and/or modify
18  *    it under the terms of the GNU General Public License as published by
19  *    the Free Software Foundation; either version 2 of the License, or
20  *    (at your option) any later version.
21  *
22  *    welle.io is distributed in the hope that it will be useful,
23  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
24  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  *    GNU General Public License for more details.
26  *
27  *    You should have received a copy of the GNU General Public License
28  *    along with welle.io; if not, write to the Free Software
29  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30  *
31  */
32 
33 #include <QCoreApplication>
34 #include <QDebug>
35 #include <QSettings>
36 #include <QStandardPaths>
37 #include <stdexcept>
38 
39 #include "radio_controller.h"
40 #ifdef HAVE_SOAPYSDR
41 #include "soapy_sdr.h"
42 #endif /* HAVE_SOAPYSDR */
43 #include "input_factory.h"
44 #include "raw_file.h"
45 #include "rtl_tcp.h"
46 
47 #define AUDIOBUFFERSIZE 32768
48 
49 static QString serialise_serviceid(quint32 serviceid) {
50     return QString::asprintf("%x", serviceid);
51 }
52 
53 static quint32 deserialise_serviceid(const char *input)
54 {
55     long value = 0;
56     errno = 0;
57 
58     char* endptr = nullptr;
59     value = strtol(input, &endptr, 16);
60 
61     if ((value == LONG_MIN or value == LONG_MAX) and errno == ERANGE) {
62         return 0;
63     }
64     else if (value == 0 and errno != 0) {
65         return 0;
66     }
67     else if (input == endptr) {
68         return 0;
69     }
70     else if (*endptr != '\0') {
71         return 0;
72     }
73 
74     return value;
75 }
76 
77 CRadioController::CRadioController(QVariantMap& commandLineOptions, QObject *parent)
78     : QObject(parent)
79     , commandLineOptions(commandLineOptions)
80     , audioBuffer(2 * AUDIOBUFFERSIZE)
81     , audio(audioBuffer)
82 {
83     // Init the technical data
84     resetTechnicalData();
85 
86     // Init timers
87     connect(&labelTimer, &QTimer::timeout, this, &CRadioController::labelTimerTimeout);
88     connect(&stationTimer, &QTimer::timeout, this, &CRadioController::stationTimerTimeout);
89     connect(&channelTimer, &QTimer::timeout, this, &CRadioController::channelTimerTimeout);
90 
91     // Use the signal slot mechanism is necessary because the backend runs in a different thread
92     connect(this, &CRadioController::switchToNextChannel,
93             this, &CRadioController::nextChannel);
94 
95     connect(this, &CRadioController::ensembleIdUpdated,
96             this, &CRadioController::ensembleId);
97 
98     qRegisterMetaType<DabLabel>("DabLabel&");
99     connect(this, &CRadioController::ensembleLabelUpdated,
100             this, &CRadioController::ensembleLabel);
101 
102     connect(this, &CRadioController::serviceDetected,
103             this, &CRadioController::serviceId);
104 
105     qRegisterMetaType<dab_date_time_t>("dab_date_time_t");
106     connect(this, &CRadioController::dateTimeUpdated,
107             this, &CRadioController::displayDateTime);
108 }
109 
110 CRadioController::~CRadioController()
111 {
112     closeDevice();
113     qDebug() << "RadioController:" << "Deleting CRadioController";
114 }
115 
116 void CRadioController::closeDevice()
117 {
118     qDebug() << "RadioController:" << "Close device";
119 
120     radioReceiver.reset();
121     device.reset();
122     audio.reset();
123 
124     // Reset the technical data
125     resetTechnicalData();
126 
127     emit deviceClosed();
128 }
129 
130 CDeviceID CRadioController::openDevice(CDeviceID deviceId, bool force, QVariant param1, QVariant param2)
131 {
132     if(this->deviceId != deviceId || force) {
133         closeDevice();
134         device.reset(CInputFactory::GetDevice(*this, deviceId));
135 
136         // Set rtl_tcp settings
137         if (device->getID() == CDeviceID::RTL_TCP) {
138             CRTL_TCP_Client* RTL_TCP_Client = static_cast<CRTL_TCP_Client*>(device.get());
139 
140             RTL_TCP_Client->setServerAddress(param1.toString().toStdString());
141             RTL_TCP_Client->setPort(param2.toInt());
142         }
143 
144         // Set rtl_tcp settings
145         if (device->getID() == CDeviceID::RAWFILE) {
146             CRAWFile* rawFile = static_cast<CRAWFile*>(device.get());
147 #ifdef __ANDROID__
148             // Using QFile is necessary to get access to com.android.externalstorage.ExternalStorageProvider
149             rawFileAndroid.reset(new QFile(param1.toString()));
150             bool ret = rawFileAndroid->open(QIODevice::ReadOnly);
151             if(!ret) {
152                 setErrorMessage(tr("Error while opening file ") + param1.toString());
153             }
154             else {
155                 rawFile->setFileHandle(rawFileAndroid->handle(), param2.toString().toStdString());
156             }
157 #else
158             rawFile->setFileName(param1.toString().toStdString(), param2.toString().toStdString());
159 #endif
160         }
161 
162         initialise();
163     }
164 
165     return device->getID();
166 }
167 
168 CDeviceID CRadioController::openDevice()
169 {
170     closeDevice();
171     device.reset(CInputFactory::GetDevice(*this, "auto"));
172     initialise();
173 
174     return device->getID();
175 }
176 
177 void CRadioController::setDeviceParam(QString param, int value)
178 {
179     if (param == "biastee") {
180         deviceParametersInt[DeviceParam::BiasTee] = value;
181     }
182     else {
183         qDebug() << "Invalid device parameter setting: " << param;
184     }
185 
186     if (device) {
187         device->setDeviceParam(DeviceParam::BiasTee, value);
188     }
189 }
190 
191 void CRadioController::setDeviceParam(QString param, QString value)
192 {
193     DeviceParam dp;
194     bool deviceParamChanged = false;
195 
196     if (param == "SoapySDRAntenna") {
197         dp = DeviceParam::SoapySDRAntenna;
198     }
199     else if (param == "SoapySDRDriverArgs") {
200         dp = DeviceParam::SoapySDRDriverArgs;
201     }
202     else if (param == "SoapySDRClockSource") {
203         dp = DeviceParam::SoapySDRClockSource;
204     }
205     else {
206         qDebug() << "Invalid device parameter setting: " << param;
207         return;
208     }
209 
210     std::string v = value.toStdString();
211 
212     if (deviceParametersString[dp] != v) {
213         deviceParamChanged = true;
214         deviceParametersString[dp] = v;
215     }
216 
217     if (device && deviceParamChanged) {
218         device->setDeviceParam(dp, v);
219         if (dp == DeviceParam::SoapySDRDriverArgs)
220             openDevice(CDeviceID::SOAPYSDR,1);
221     }
222 }
223 
224 void CRadioController::play(QString channel, QString title, quint32 service)
225 {
226     if (channel == "") {
227         return;
228     }
229 
230     currentTitle = title;
231     emit titleChanged();
232 
233 
234     qDebug() << "RadioController:" << "Play:" << title << serialise_serviceid(service) << "on channel" << channel;
235 
236     if (isChannelScan == true) {
237         stopScan();
238     }
239 
240     bool isRestartOk = deviceRestart();
241     setChannel(channel, false);
242     setService(service);
243 
244     currentLastChannel = QStringList() << serialise_serviceid(service) << channel;
245     QSettings settings;
246     settings.setValue("lastchannel", currentLastChannel);
247 
248     if (isRestartOk) {
249         isPlaying = true;
250         emit isPlayingChanged(isPlaying);
251     } else {
252         resetTechnicalData();
253         currentTitle = title;
254         emit titleChanged();
255         currentText = tr("Playback failed");
256         emit textChanged();
257     }
258 }
259 
260 void CRadioController::stop()
261 {
262     if (radioReceiver) {
263         radioReceiver->stop();
264     }
265 
266     if (device) {
267         device->stop();
268     }
269     else
270         throw std::runtime_error("device is null in file " + std::string(__FILE__) +":"+ std::to_string(__LINE__));
271 
272     QString title = currentTitle;
273     resetTechnicalData();
274     currentTitle = title;
275     emit titleChanged();
276     currentText = tr("Stopped");
277     emit textChanged();
278 
279     audio.stop();
280     labelTimer.stop();
281 }
282 
283 void CRadioController::setService(uint32_t service, bool force)
284 {
285     if (currentService != service or force or isPlaying == false) {
286         currentService = service;
287         autoService = service;
288         emit stationChanged();
289         emit autoServiceChanged(autoService);
290 
291         // Wait if we found the station inside the signal
292         stationTimer.start(1000);
293 
294         // Clear old data
295         currentStationType = "";
296         emit stationTypChanged();
297 
298         currentLanguageType = "";
299         emit languageTypeChanged();
300 
301         currentText = "";
302         emit textChanged();
303 
304         emit motReseted();
305     }
306 }
307 
308 void CRadioController::setAutoPlay(bool isAutoPlayValue, QString channel, QString service)
309 {
310     isAutoPlay = isAutoPlayValue;
311     autoChannel = channel;
312     emit autoChannelChanged(autoChannel);
313     autoService = deserialise_serviceid(service.toStdString().c_str());
314     emit autoServiceChanged(autoService);
315     currentLastChannel = QStringList() << service << channel;
316 }
317 
318 void CRadioController::setVolume(qreal Volume)
319 {
320     currentVolume = Volume;
321     audio.setVolume(Volume);
322     emit volumeChanged(currentVolume);
323 }
324 
325 void CRadioController::setChannel(QString Channel, bool isScan, bool Force)
326 {
327     if (currentChannel != Channel || Force == true || isPlaying == false) {
328         if (device && device->getID() == CDeviceID::RAWFILE) {
329             currentChannel = "File";
330             if (!isScan)
331                 autoChannel = currentChannel;
332             currentEId = 0;
333             currentEnsembleLabel = "";
334             currentFrequency = 0;
335         }
336         else { // A real device
337             if(radioReceiver)
338                 radioReceiver->stop(); // Stop the demodulator in order to avoid working with old data
339             currentChannel = Channel;
340             if (!isScan)
341                 autoChannel = currentChannel;
342             currentEId = 0;
343             currentEnsembleLabel = "";
344 
345             // Convert channel into a frequency
346             currentFrequency = channels.getFrequency(Channel.toStdString());
347 
348             if(currentFrequency != 0 && device) {
349                 qDebug() << "RadioController: Tune to channel" <<  Channel << "->" << currentFrequency/1e6 << "MHz";
350                 device->setFrequency(currentFrequency);
351                 device->reset(); // Clear buffer
352             }
353         }
354 
355         // Restart demodulator and decoder
356         if(device) {
357             radioReceiver = std::make_unique<RadioReceiver>(*this, *device, rro, 1);
358             radioReceiver->setReceiverOptions(rro);
359             radioReceiver->restart(isScan);
360         }
361 
362         emit channelChanged();
363         if (!isScan)
364             emit autoChannelChanged(autoChannel);
365         emit ensembleChanged();
366         emit frequencyChanged();
367     }
368 }
369 
370 void CRadioController::setManualChannel(QString Channel)
371 {
372     // Otherwise tune to channel and play first found station
373     qDebug() << "RadioController: Tune to channel" <<  Channel;
374 
375     deviceRestart();
376 
377     currentTitle = Channel;
378     emit titleChanged();
379 
380     currentService = 0;
381     emit stationChanged();
382 
383     currentStationType = "";
384     emit stationTypChanged();
385 
386     currentLanguageType = "";
387     emit languageTypeChanged();
388 
389     currentText = "";
390     emit textChanged();
391 
392     emit motReseted();
393 
394     // Switch channel
395     setChannel(Channel, false, true);
396 }
397 
398 void CRadioController::startScan(void)
399 {
400     qDebug() << "RadioController:" << "Start channel scan";
401 
402     stop();
403 
404     deviceRestart();
405 
406     if(device && device->getID() == CDeviceID::RAWFILE) {
407         currentTitle = tr("RAW File");
408         const auto FirstChannel = QString::fromStdString(Channels::firstChannel);
409         setChannel(FirstChannel, false); // Just a dummy
410         emit scanStopped();
411     }
412     else
413     {
414         // Start with lowest frequency
415         QString Channel = QString::fromStdString(Channels::firstChannel);
416         setChannel(Channel, true);
417 
418         isChannelScan = true;
419         emit isChannelScanChanged(isChannelScan);
420         stationCount = 0;
421         currentTitle = tr("Scanning") + " ... " + Channel
422                 + " (" + QString::number((1 * 100 / NUMBEROFCHANNELS)) + "%)";
423         emit titleChanged();
424 
425         currentText = tr("Found channels") + ": " + QString::number(stationCount);
426         emit textChanged();
427 
428         currentService = 0;
429         emit stationChanged();
430 
431         currentStationType = "";
432         emit stationTypChanged();
433 
434         currentLanguageType = "";
435         emit languageTypeChanged();
436 
437         emit scanProgress(0);
438     }
439 }
440 
441 void CRadioController::stopScan(void)
442 {
443     qDebug() << "RadioController:" << "Stop channel scan";
444 
445     currentTitle = tr("No Station");
446     emit titleChanged();
447 
448     currentText = "";
449     emit textChanged();
450 
451     isChannelScan = false;
452     emit isChannelScanChanged(isChannelScan);
453     emit scanStopped();
454 
455     stop();
456 }
457 
458 void CRadioController::setAGC(bool isAGC)
459 {
460     this->isAGC = isAGC;
461 
462     if (device) {
463         device->setAgc(isAGC);
464 
465         if (!isAGC) {
466             device->setGain(currentManualGain);
467             qDebug() << "RadioController:" << "AGC off";
468         }
469         else {
470             qDebug() << "RadioController:" <<  "AGC on";
471         }
472     }
473 
474     emit agcChanged(isAGC);
475 }
476 
477 void CRadioController::disableCoarseCorrector(bool disable)
478 {
479     rro.disableCoarseCorrector = disable;
480     if (radioReceiver) {
481         radioReceiver->setReceiverOptions(rro);
482     }
483 }
484 
485 void CRadioController::enableTIIDecode(bool enable)
486 {
487     rro.decodeTII = enable;
488     if (radioReceiver) {
489         radioReceiver->setReceiverOptions(rro);
490     }
491 }
492 
493 void CRadioController::selectFFTWindowPlacement(int fft_window_placement_ix)
494 {
495     if (fft_window_placement_ix == 0) {
496         rro.fftPlacementMethod = FFTPlacementMethod::StrongestPeak;
497     }
498     else if (fft_window_placement_ix == 1) {
499         rro.fftPlacementMethod = FFTPlacementMethod::EarliestPeakWithBinning;
500     }
501     else if (fft_window_placement_ix == 2) {
502         rro.fftPlacementMethod = FFTPlacementMethod::ThresholdBeforePeak;
503     }
504     else {
505         std::clog << "Invalid FFT window placement " <<
506             fft_window_placement_ix << " chosen" << std::endl;
507         return;
508     }
509 
510     if (radioReceiver) {
511         radioReceiver->setReceiverOptions(rro);
512     }
513 }
514 
515 void CRadioController::setFreqSyncMethod(int fsm_ix)
516 {
517     rro.freqsyncMethod = static_cast<FreqsyncMethod>(fsm_ix);
518 
519     if (radioReceiver) {
520         radioReceiver->setReceiverOptions(rro);
521     }
522 }
523 
524 void CRadioController::setGain(int Gain)
525 {
526     currentManualGain = Gain;
527     emit gainChanged(currentManualGain);
528 
529     if (device) {
530         currentManualGainValue = device->setGain(Gain);
531         emit gainValueChanged(currentManualGainValue);
532 
533         int32_t gainCount_tmp = device->getGainCount();
534 
535         if(gainCount != gainCount_tmp) {
536             gainCount = gainCount_tmp;
537             emit gainCountChanged(gainCount);
538         }
539     }
540 }
541 
542 void CRadioController::initRecorder(int size)
543 {
544     device->initRecordBuffer(size);
545 }
546 
547 void CRadioController::triggerRecorder(QString filename)
548 {
549     // TODO just for testing
550     filename = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/welle-io-record.iq";
551     std::string filename_tmp = filename.toStdString();
552     device->writeRecordBufferToFile(filename_tmp);
553 }
554 
555 DABParams& CRadioController::getParams()
556 {
557     static DABParams dummyParams(1);
558 
559     if(radioReceiver)
560         return radioReceiver->getParams();
561     else
562         return dummyParams;
563 }
564 
565 int CRadioController::getCurrentFrequency()
566 {
567     return currentFrequency;
568 }
569 
570 std::vector<float> CRadioController::getImpulseResponse()
571 {
572     std::lock_guard<std::mutex> lock(impulseResponseBufferMutex);
573     auto buf = std::move(impulseResponseBuffer);
574     return buf;
575 }
576 
577 std::vector<DSPCOMPLEX> CRadioController::getSignalProbe()
578 {
579     int16_t T_u = getParams().T_u;
580 
581     if (device) {
582         return device->getSpectrumSamples(T_u);
583     }
584     else {
585         std::vector<DSPCOMPLEX> dummyBuf(static_cast<std::vector<DSPCOMPLEX>::size_type>(T_u));
586         return dummyBuf;
587     }
588 }
589 
590 std::vector<DSPCOMPLEX> CRadioController::getNullSymbol()
591 {
592     std::lock_guard<std::mutex> lock(nullSymbolBufferMutex);
593     auto buf = std::move(nullSymbolBuffer);
594     return buf;
595 }
596 
597 std::vector<DSPCOMPLEX> CRadioController::getConstellationPoint()
598 {
599     std::lock_guard<std::mutex> lock(constellationPointBufferMutex);
600     auto buf = std::move(constellationPointBuffer);
601     return buf;
602 }
603 
604 /********************
605  * Private methods  *
606  ********************/
607 void CRadioController::initialise(void)
608 {
609     for (const auto param_value : deviceParametersString) {
610         device->setDeviceParam(param_value.first, param_value.second);
611     }
612 
613     for (const auto param_value : deviceParametersInt) {
614         device->setDeviceParam(param_value.first, param_value.second);
615     }
616 
617     gainCount = device->getGainCount();
618     emit gainCountChanged(gainCount);
619     emit deviceReady();
620 
621     if (!isAGC) { // Manual AGC
622         device->setAgc(false);
623         currentManualGainValue = device->setGain(currentManualGain);
624         emit gainValueChanged(currentManualGainValue);
625 
626         qDebug() << "RadioController:" << "AGC off";
627     }
628     else {
629         device->setAgc(true);
630         qDebug() << "RadioController:" << "AGC on";
631     }
632 
633     audio.setVolume(currentVolume);
634 
635     deviceName = QString::fromStdString(device->getDescription());
636     emit deviceNameChanged();
637 
638     deviceId = device->getID();
639     emit deviceIdChanged();
640 
641     if(isAutoPlay) {
642         play(autoChannel, tr("Playing last station"), autoService);
643     }
644 }
645 
646 void CRadioController::resetTechnicalData(void)
647 {
648     currentChannel = tr("Unknown");
649     emit channelChanged();
650 
651     currentEId = 0;
652     currentEnsembleLabel = "";
653     emit ensembleChanged();
654 
655     currentFrequency = 0;
656     emit frequencyChanged();
657 
658     currentService = 0;
659     emit stationChanged();
660 
661     currentStationType = "";
662     emit stationTypChanged();
663 
664     currentLanguageType = "";
665     emit languageTypeChanged();
666 
667     currentTitle = tr("No Station");
668     emit titleChanged();
669 
670     currentText = "";
671     emit textChanged();
672 
673     isPlaying = false;
674     emit isPlayingChanged(isPlaying);
675 
676     errorMsg = "";
677     isSync = false;
678     emit isSyncChanged(isSync);
679     isFICCRC = false;
680     emit isFICCRCChanged(isFICCRC);
681     isSignal = false;
682     emit isSignalChanged(isSignal);
683     snr = 0;
684     emit snrChanged(snr);
685     frequencyCorrection = 0;
686     emit frequencyCorrectionChanged(frequencyCorrection);
687     frequencyCorrectionPpm = NAN;
688     emit frequencyCorrectionPpmChanged(frequencyCorrectionPpm);
689     bitRate = 0;
690     emit bitRateChanged(bitRate);
691     audioSampleRate = 0;
692     isDAB = true;
693     emit isDABChanged(isDAB);
694     frameErrors = 0;
695     emit frameErrorsChanged(frameErrors);
696     rsUncorrectedErrors = 0;
697     emit rsUncorrectedErrorsChanged(this->rsUncorrectedErrors);
698     emit rsCorrectedErrorsChanged(this->rsCorrectedErrors);
699     aaErrors = 0;
700     emit aacErrorsChanged(aaErrors);
701 
702     emit motReseted();
703 }
704 
705 bool CRadioController::deviceRestart()
706 {
707     bool isPlay = false;
708 
709     if(device) {
710         isPlay = device->restart();
711     } else {
712         return false;
713     }
714 
715     if(!isPlay) {
716         qDebug() << "RadioController:" << "Radio device is not ready or does not exist.";
717         emit showErrorMessage(tr("Radio device is not ready or does not exist."));
718         return false;
719     }
720 
721     labelTimer.start(40);
722     return true;
723 }
724 
725 /*****************
726  * Public slots *
727  *****************/
728 void CRadioController::ensembleId(quint16 eId)
729 {
730     qDebug() << "RadioController: ID of ensemble:" << eId;
731 
732     if (currentEId == eId)
733         return;
734 
735     currentEId = eId;
736 
737     //auto label = radioReceiver->getEnsembleLabel();
738     //currentEnsembleLabel = QString::fromStdString(label.utf8_label());
739 
740     //emit ensembleChanged();
741 }
742 
743 void CRadioController::ensembleLabel(DabLabel& label)
744 {
745     QString newLabel = QString::fromStdString(label.utf8_label());
746 
747     if (currentEnsembleLabel == newLabel)
748         return;
749 
750     qDebug() << "RadioController: Label of ensemble:" << newLabel;
751     currentEnsembleLabel = newLabel;
752 
753     emit ensembleChanged();
754 }
755 
756 void CRadioController::setErrorMessage(QString Text)
757 {
758     errorMsg = Text;
759     emit showErrorMessage(Text);
760 }
761 
762 void CRadioController::setErrorMessage(const std::string& head, const std::string& text)
763 {
764     if (text.empty()) {
765         setErrorMessage(tr(head.c_str()));
766     }
767     else {
768         setErrorMessage(tr(head.c_str()) + ": " + QString::fromStdString(text));
769     }
770 }
771 
772 void CRadioController::setInfoMessage(QString Text)
773 {
774     emit showInfoMessage(Text);
775 }
776 
777 /********************
778  * private slots *
779  ********************/
780 void CRadioController::labelTimerTimeout()
781 {
782     if (radioReceiver and not pendingLabels.empty()) {
783         const auto sId = pendingLabels.front();
784         pendingLabels.pop_front();
785 
786         std::string label;
787 
788         auto srv = radioReceiver->getService(sId);
789         if (srv.serviceId != 0) {
790             label = srv.serviceLabel.utf8_label();
791         }
792 
793         if (not label.empty()) {
794             const auto qlabel = QString::fromStdString(label);
795             emit newStationNameReceived(qlabel, sId, currentChannel);
796             qDebug() << "RadioController: Found service " << qPrintable(QString::number(sId, 16).toUpper()) << qlabel;
797 
798             if (currentService == sId) {
799                 currentTitle = qlabel;
800                 emit titleChanged();
801             }
802         }
803         else {
804             // Rotate pending labels to avoid getting stuck on a failing one
805             pendingLabels.push_back(sId);
806         }
807     }
808 }
809 
810 void CRadioController::stationTimerTimeout()
811 {
812     if (!radioReceiver)
813         return;
814 
815     const auto services = radioReceiver->getServiceList();
816 
817     for (const auto& s : services) {
818         if (s.serviceId == currentService) {
819             const auto comps = radioReceiver->getComponents(s);
820             for (const auto& sc : comps) {
821                 if (sc.transportMode() == TransportMode::Audio && (
822                         sc.audioType() == AudioServiceComponentType::DAB ||
823                         sc.audioType() == AudioServiceComponentType::DABPlus) ) {
824                     const auto& subch = radioReceiver->getSubchannel(sc);
825 
826                     if (not subch.valid()) {
827                         return;
828                     }
829 
830                     // We found the station inside the signal, lets stop the timer
831                     stationTimer.stop();
832 
833                     std::string dumpFileName;
834                     if (commandLineOptions["dumpFileName"] != "") {
835                         dumpFileName = commandLineOptions["dumpFileName"].toString().toStdString();
836                     }
837 
838                     bool success = radioReceiver->playSingleProgramme(*this, dumpFileName, s);
839                     if (!success) {
840                         qDebug() << "Selecting service failed";
841                     }
842                     else {
843                         currentStationType = DABConstants::getProgramTypeName(s.programType);
844                         emit stationTypChanged();
845 
846                         currentLanguageType = DABConstants::getLanguageName(s.language);
847                         emit languageTypeChanged();
848 
849                         bitRate = subch.bitrate();
850                         emit bitRateChanged(bitRate);
851 
852                         if (sc.audioType() == AudioServiceComponentType::DABPlus)
853                             isDAB = false;
854                         else
855                             isDAB = true;
856                         emit isDABChanged(isDAB);
857                     }
858 
859                     return;
860                 }
861             }
862         }
863     }
864 }
865 
866 void CRadioController::channelTimerTimeout(void)
867 {
868     channelTimer.stop();
869 
870     if(isChannelScan)
871         nextChannel(false);
872 }
873 
874 void CRadioController::displayDateTime(const dab_date_time_t& dateTime)
875 {
876     QDate Date;
877     QTime Time;
878 
879     Time.setHMS(dateTime.hour, dateTime.minutes, dateTime.seconds);
880     currentDateTime.setTime(Time);
881 
882     Date.setDate(dateTime.year, dateTime.month, dateTime.day);
883     currentDateTime.setDate(Date);
884 
885     int OffsetFromUtc = dateTime.hourOffset * 3600 +
886                         dateTime.minuteOffset * 60;
887     currentDateTime.setOffsetFromUtc(OffsetFromUtc);
888     currentDateTime.setTimeSpec(Qt::OffsetFromUTC);
889 
890     emit dateTimeChanged(currentDateTime);
891 }
892 
893 void CRadioController::nextChannel(bool isWait)
894 {
895     if (isWait) { // It might be a channel, wait 10 seconds
896         channelTimer.start(10000);
897     }
898     else {
899         auto Channel = QString::fromStdString(channels.getNextChannel());
900 
901         if(!Channel.isEmpty()) {
902             setChannel(Channel, true);
903 
904             int index = channels.getCurrentIndex() + 1;
905 
906             currentTitle = tr("Scanning") + " ... " + Channel
907                     + " (" + QString::number(index * 100 / NUMBEROFCHANNELS) + "%)";
908             emit titleChanged();
909 
910             emit scanProgress(index);
911         }
912         else {
913             stopScan();
914         }
915     }
916 }
917 
918 /*********************
919  * Backend callbacks *
920  *********************/
921 void CRadioController::onServiceDetected(uint32_t sId)
922 {
923     // you may not call radioReceiver->getService() because it internally holds the FIG mutex.
924     emit serviceDetected(sId);
925 }
926 
927 void CRadioController::serviceId(quint32 sId)
928 {
929     if (isChannelScan == true) {
930         stationCount++;
931         currentText = tr("Found channels") + ": " + QString::number(stationCount);
932         emit textChanged();
933     }
934 
935     if (sId <= 0xFFFF) {
936         // Exclude data services from the list
937         pendingLabels.push_back(sId);
938     }
939 }
940 
941 void CRadioController::onNewEnsemble(quint16 eId)
942 {
943     emit ensembleIdUpdated(eId);
944 }
945 
946 void CRadioController::onSetEnsembleLabel(DabLabel& label)
947 {
948     emit ensembleLabelUpdated(label);
949 }
950 
951 void CRadioController::onDateTimeUpdate(const dab_date_time_t& dateTime)
952 {
953     emit dateTimeUpdated(dateTime);
954 }
955 
956 void CRadioController::onFIBDecodeSuccess(bool crcCheckOk, const uint8_t* fib)
957 {
958     (void)fib;
959     if (isFICCRC == crcCheckOk)
960         return;
961     isFICCRC = crcCheckOk;
962     emit isFICCRCChanged(isFICCRC);
963 }
964 
965 void CRadioController::onNewImpulseResponse(std::vector<float>&& data)
966 {
967     std::lock_guard<std::mutex> lock(impulseResponseBufferMutex);
968     impulseResponseBuffer = std::move(data);
969 }
970 
971 void CRadioController::onConstellationPoints(std::vector<DSPCOMPLEX>&& data)
972 {
973     std::lock_guard<std::mutex> lock(constellationPointBufferMutex);
974     constellationPointBuffer = std::move(data);
975 }
976 
977 void CRadioController::onNewNullSymbol(std::vector<DSPCOMPLEX>&& data)
978 {
979     std::lock_guard<std::mutex> lock(nullSymbolBufferMutex);
980     nullSymbolBuffer = std::move(data);
981 }
982 
983 void CRadioController::onTIIMeasurement(tii_measurement_t&& m)
984 {
985     qDebug().noquote() << "TII comb " << m.comb <<
986         " pattern " << m.pattern <<
987         " delay " << m.delay_samples <<
988         "= " << m.getDelayKm() << " km" <<
989         " with error " << m.error;
990 }
991 
992 void CRadioController::onMessage(message_level_t level, const std::string& text, const std::string& text2)
993 {
994     QString fullText;
995     if (text2.empty())
996       fullText = tr(text.c_str());
997     else
998       fullText = tr(text.c_str()) + QString::fromStdString(text2);
999 
1000     switch (level) {
1001         case message_level_t::Information:
1002             emit showInfoMessage(fullText);
1003             break;
1004         case message_level_t::Error:
1005             emit showErrorMessage(fullText);
1006             break;
1007     }
1008 }
1009 
1010 void CRadioController::onSNR(float snr)
1011 {
1012     if (this->snr == snr)
1013         return;
1014     this->snr = snr;
1015     emit snrChanged(this->snr);
1016 }
1017 
1018 void CRadioController::onFrequencyCorrectorChange(int fine, int coarse)
1019 {
1020     if (frequencyCorrection == coarse + fine)
1021         return;
1022     frequencyCorrection = coarse + fine;
1023     emit frequencyCorrectionChanged(frequencyCorrection);
1024 
1025     if (currentFrequency != 0)
1026         frequencyCorrectionPpm = -1000000.0f * static_cast<float>(frequencyCorrection) / static_cast<float>(currentFrequency);
1027     else
1028         frequencyCorrectionPpm = NAN;
1029     emit frequencyCorrectionPpmChanged(frequencyCorrectionPpm);
1030 }
1031 
1032 void CRadioController::onSyncChange(char isSync)
1033 {
1034     bool sync = (isSync == SYNCED) ? true : false;
1035     if (this->isSync == sync)
1036         return;
1037     this->isSync = sync;
1038     emit isSyncChanged(isSync);
1039 }
1040 
1041 void CRadioController::onSignalPresence(bool isSignal)
1042 {
1043     if (this->isSignal != isSignal) {
1044         this->isSignal = isSignal;
1045         emit isSignalChanged(isSignal);
1046     }
1047 
1048     if (isChannelScan)
1049         emit switchToNextChannel(isSignal);
1050 }
1051 
1052 void CRadioController::onNewAudio(std::vector<int16_t>&& audioData, int sampleRate, const std::string& mode)
1053 {
1054     audioBuffer.putDataIntoBuffer(audioData.data(), static_cast<int32_t>(audioData.size()));
1055 
1056     if (audioSampleRate != sampleRate) {
1057         qDebug() << "RadioController: Audio sample rate" <<  sampleRate << "Hz, mode=" <<
1058             QString::fromStdString(mode);
1059         audioSampleRate = sampleRate;
1060 
1061         audio.setRate(sampleRate);
1062     }
1063 
1064     if (audioMode != QString::fromStdString(mode)) {
1065         audioMode = QString::fromStdString(mode);
1066         emit audioModeChanged(audioMode);
1067     }
1068 }
1069 
1070 void CRadioController::onFrameErrors(int frameErrors)
1071 {
1072     if (this->frameErrors == frameErrors)
1073         return;
1074     this->frameErrors = frameErrors;
1075     emit frameErrorsChanged(this->frameErrors);
1076 }
1077 
1078 void CRadioController::onRsErrors(bool uncorrectedErrors, int numCorrectedErrors)
1079 {
1080     if (this->rsUncorrectedErrors != uncorrectedErrors)
1081     {
1082         this->rsUncorrectedErrors = uncorrectedErrors;
1083         emit rsUncorrectedErrorsChanged(this->rsUncorrectedErrors);
1084     }
1085 
1086     if (this->rsCorrectedErrors != numCorrectedErrors)
1087     {
1088         this->rsCorrectedErrors = numCorrectedErrors;
1089         emit rsCorrectedErrorsChanged(this->rsCorrectedErrors);
1090     }
1091 }
1092 
1093 void CRadioController::onAacErrors(int aacErrors)
1094 {
1095     if (this->aaErrors == aacErrors)
1096         return;
1097     this->aaErrors = aacErrors;
1098     emit aacErrorsChanged(this->aaErrors);
1099 }
1100 
1101 void CRadioController::onNewDynamicLabel(const std::string& label)
1102 {
1103     auto qlabel = QString::fromUtf8(label.c_str());
1104     if (this->currentText != qlabel) {
1105         this->currentText = qlabel;
1106         emit textChanged();
1107     }
1108 }
1109 
1110 void CRadioController::onMOT(const mot_file_t& mot_file)
1111 {
1112     emit motChanged(mot_file);
1113 }
1114 
1115 void CRadioController::onPADLengthError(size_t announced_xpad_len, size_t xpad_len)
1116 {
1117     qDebug() << "X-PAD length mismatch, expected:" << announced_xpad_len << " effective:" << xpad_len;
1118 }
1119 
1120 void CRadioController::onInputFailure()
1121 {
1122     stop();
1123 }
1124