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