#include "wfmain.h" #include "icomserver.h" #include "ui_wfmain.h" #include "rigidentities.h" #include "logcategories.h" // This code is copyright 2017-2024 Elliott H. Liggett and Phil E. Taylor // All rights reserved // Log support: QScopedPointer m_logFile; QMutex logMutex; QMutex logTextMutex; QScopedPointer logStream; QVector> logStringBuffer; #ifdef QT_DEBUG bool debugModeLogging = true; #else bool debugModeLogging = false; #endif bool insaneDebugLogging = false; bool rigctlDebugLogging = false; wfmain::wfmain(const QString settingsFile, const QString logFile, bool debugMode, QWidget *parent ) : QMainWindow(parent), ui(new Ui::wfmain), logFilename(logFile) { QGuiApplication::setApplicationDisplayName("wfview"); QGuiApplication::setApplicationName(QString("wfview")); setWindowIcon(QIcon( QString(":resources/wfview.png"))); this->debugMode = debugMode; debugModeLogging = debugMode; qInfo() << "wfmain constructed" << this; setWindowTitle(QString("wfview")); ui->setupUi(this); ui->monitorLabel->setText("Mon"); logWindow = new loggingWindow(logFile); initLogging(); logWindow->setInitialDebugState(debugMode); qInfo(logSystem()).noquote() << QString("wfview version: %1 (Git:%2 on %3 at %4 by %5@%6)") .arg(QString(WFVIEW_VERSION),GITSHORT,__DATE__,__TIME__,UNAME,HOST); qInfo(logSystem()).noquote() << QString("Operating System: %0 (%1)").arg(QSysInfo::prettyProductName(),QSysInfo::buildCpuArchitecture()); qInfo(logSystem()).noquote() << "Looking for External Dependencies:"; qInfo(logSystem()).noquote() << QString("QT Runtime Version: %0").arg(qVersion()); if (strncmp(QT_VERSION_STR, qVersion(),sizeof(QT_VERSION_STR))) { qWarning(logSystem()).noquote() << QString("QT Build Version Mismatch: %0").arg(QT_VERSION_STR); } qInfo(logSystem()).noquote() << QString("OPUS Version: %0").arg(opus_get_version_string()); #ifdef HID_API_VERSION_MAJOR qInfo(logSystem()).noquote() << QString("HIDAPI Version: %0.%1.%2") .arg(HID_API_VERSION_MAJOR) .arg(HID_API_VERSION_MINOR) .arg(HID_API_VERSION_PATCH); if (HID_API_VERSION != HID_API_MAKE_VERSION(hid_version()->major, hid_version()->minor, hid_version()->patch)) { qWarning(logSystem()).noquote() << QString("HIDAPI Version mismatch: %0.%1.%2") .arg(hid_version()->major) .arg(hid_version()->minor) .arg(hid_version()->patch); } #endif #ifdef EIGEN_WORLD_VERSION qInfo(logSystem()).noquote() << QString("EIGEN Version: %0.%1.%2").arg(EIGEN_WORLD_VERSION).arg(EIGEN_MAJOR_VERSION).arg(EIGEN_MINOR_VERSION); #endif #ifdef QCUSTOMPLOT_VERSION_STR qInfo(logSystem()).noquote() << QString("QCUSTOMPLOT Version: %0").arg(QCUSTOMPLOT_VERSION_STR); #endif #ifdef RTAUDIO_VERSION qInfo(logSystem()).noquote() << QString("RTAUDIO Version: %0").arg(RTAUDIO_VERSION); if (RTAUDIO_VERSION != RtAudio::getVersion()) { qWarning(logSystem()).noquote() << QString("RTAUDIO Version Mismatch: %0").arg(RtAudio::getVersion().c_str()); } #endif qInfo(logSystem()).noquote() << QString("PORTAUDIO Version: %0").arg(Pa_GetVersionText()); cal = new calibrationWindow(); rpt = new repeaterSetup(); sat = new satelliteSetup(); abtBox = new aboutbox(); selRad = new selectRadio(); bandbtns = new bandbuttons(); finputbtns = new frequencyinputwidget(); setupui = new settingswidget(); connect(setupui, SIGNAL(havePortError(errorType)), this, SLOT(receivePortError(errorType))); this->setObjectName("wfmain"); queue = cachingQueue::getInstance(this); //connect(queue,SIGNAL(sendValue(cacheItem)),this,SLOT(receiveValue(cacheItem))); //connect(queue,SIGNAL(sendMessage(QString)),this,SLOT(showStatusBarText(QString))); //connect(queue,SIGNAL(intervalUpdated(qint64)),this,SLOT(updatedQueueInterval(qint64))); //connect(queue, SIGNAL(finished()), queue, SLOT(deleteLater())); // Disable all controls until connected. enableControls(false); // We need to populate the list of rigs as early as possible so do it now // Setup the connectiontimer as we may need it soon! //ConnectionTimer.setSingleShot(true); //connect(&ConnectionTimer,SIGNAL(timeout()), this,SLOT(connectionTimeout())); // Make sure we know about any changes to rigCaps //connect(queue, SIGNAL(rigCapsUpdated(rigCapabilities*)), this, SLOT(receiveRigCaps(rigCapabilities*))); setupKeyShortcuts(); setupMainUI(); connectSettingsWidget(); setSerialDevicesUI(); setDefPrefs(); getSettingsFilePath(settingsFile); setDefaultColorPresets(); loadSettings(); // Look for saved preferences logWindow->ingestSettings(prefs); setManufacturer(prefs.manufacturer); setServerToPrefs(); //setAudioDevicesUI(); // no need to call this as it will be called by the updated() signal setTuningSteps(); // TODO: Combine into preferences changeFullScreenMode(prefs.useFullScreen); useSystemTheme(prefs.useSystemTheme); // Don't update prefs until system theme is loaded. // This is so we can popup any dialog boxes in the correct theme. setupui->updateIfPrefs((int)if_all); setupui->updateColPrefs((int)col_all); setupui->updateRaPrefs((int)ra_all); setupui->updateRsPrefs((int)rs_all); // Most of these come from the rig anyway setupui->updateCtPrefs((int)ct_all); setupui->updateClusterPrefs((int)cl_all); setupui->updateLanPrefs((int)l_all); setupui->updateUdpPrefs((int)u_all); setupui->updateServerConfigs((int)s_all); finputbtns->setAutomaticSidebandSwitching(prefs.automaticSidebandSwitching); qDebug(logSystem()) << "Running setInitialTiming()"; //setInitialTiming(); fts = new FirstTimeSetup(); if(prefs.hasRunSetup) { qDebug(logSystem()) << "Running openRig()"; openRig(); } else { qInfo(logSystem()) << "Detected first-time run. Showing the First Time Setup widget."; connect(fts, &FirstTimeSetup::exitProgram, this, [=]() { qInfo(logSystem()) << "User elected exit program."; prefs.settingsChanged = false; prefs.confirmExit = false; QTimer::singleShot(10, this, [&](){ on_exitBtn_clicked(); }); }); connect(fts, &FirstTimeSetup::showSettings, this, [=](const bool networkEnabled) { qInfo(logSystem()) << "User elected to visit the Settings UI."; prefs.enableLAN = networkEnabled; setupui->updateLanPrefs((int)l_all); showAndRaiseWidget(setupui); prefs.settingsChanged = true; prefs.hasRunSetup = true; }); connect(fts, &FirstTimeSetup::skipSetup, this, [=]() { qInfo(logSystem()) << "User elected to skip the setup. Marking setup complete."; prefs.settingsChanged = true; prefs.hasRunSetup = true; }); fts->exec(); } cluster = new dxClusterClient(); clusterThread = new QThread(this); clusterThread->setObjectName("dxcluster()"); cluster->moveToThread(clusterThread); connect(this, SIGNAL(setClusterEnableUdp(bool)), cluster, SLOT(enableUdp(bool))); connect(this, SIGNAL(setClusterEnableTcp(bool)), cluster, SLOT(enableTcp(bool))); connect(this, SIGNAL(setClusterUdpPort(int)), cluster, SLOT(setUdpPort(int))); connect(this, SIGNAL(setClusterServerName(QString)), cluster, SLOT(setTcpServerName(QString))); connect(this, SIGNAL(setClusterTcpPort(int)), cluster, SLOT(setTcpPort(int))); connect(this, SIGNAL(setClusterUserName(QString)), cluster, SLOT(setTcpUserName(QString))); connect(this, SIGNAL(setClusterPassword(QString)), cluster, SLOT(setTcpPassword(QString))); connect(this, SIGNAL(setClusterTimeout(int)), cluster, SLOT(setTcpTimeout(int))); connect(this, SIGNAL(setClusterSkimmerSpots(bool)), cluster, SLOT(enableSkimmerSpots(bool))); connect(clusterThread, SIGNAL(finished()), cluster, SLOT(deleteLater())); clusterThread->start(); emit setClusterUdpPort(prefs.clusterUdpPort); emit setClusterEnableUdp(prefs.clusterUdpEnable); for (int f = 0; f < prefs.clusters.size(); f++) { if (prefs.clusters[f].isdefault) { emit setClusterServerName(prefs.clusters[f].server); emit setClusterTcpPort(prefs.clusters[f].port); emit setClusterUserName(prefs.clusters[f].userName); emit setClusterPassword(prefs.clusters[f].password); emit setClusterTimeout(prefs.clusters[f].timeout); emit setClusterSkimmerSpots(prefs.clusterSkimmerSpotsEnable); } } emit setClusterEnableTcp(prefs.clusterTcpEnable); amTransmitting = false; setupLambdaSlots(); #if defined(USB_CONTROLLER) #if defined(USB_HOTPLUG) && defined(Q_OS_LINUX) uDev = udev_new(); if (!uDev) { qInfo(logUsbControl()) << "Cannot register udev, hotplug of USB devices is not available"; return; } uDevMonitor = udev_monitor_new_from_netlink(uDev, "udev"); if (!uDevMonitor) { qInfo(logUsbControl()) << "Cannot register udev_monitor, hotplug of USB devices is not available"; return; } int fd = udev_monitor_get_fd(uDevMonitor); uDevNotifier = new QSocketNotifier(fd, QSocketNotifier::Read,this); connect(uDevNotifier, SIGNAL(activated(int)), this, SLOT(uDevEvent())); udev_monitor_enable_receiving(uDevMonitor); #endif #endif // Add right-click menu this->setContextMenuPolicy(Qt::CustomContextMenu); connect (this, &QMainWindow::customContextMenuRequested,this,&::wfmain::popupScreenMenu); screenMenu = new QMenu(this); QLabel *header = new QLabel("Enable Display"); header->setStyleSheet("QLabel {border-bottom: 1px solid; padding:5px;}"); QWidgetAction* headerAction = new QWidgetAction(screenMenu); headerAction->setDefaultWidget(header); screenMenu->addAction(headerAction); QList items = this->findChildren(); for (const auto &g : items) { if (g->objectName() !="" && g->isVisible()) { QCheckBox *chk = new QCheckBox(g->objectName(),screenMenu); chk->setChecked(true); chk->setStyleSheet("QCheckBox {padding:5px;}"); #if (QT_VERSION >= QT_VERSION_CHECK(6,7,0)) QObject::connect(chk, &QCheckBox::checkStateChanged, this, [g](Qt::CheckState state){ #else QObject::connect(chk, &QCheckBox::stateChanged, this, [g](int state){ #endif g->setVisible(state); }); QWidgetAction *chkAction = new QWidgetAction(screenMenu); chkAction->setDefaultWidget(chk); screenMenu->addAction(chkAction); } } for (int i=1;i<5;i++) { //QAction* act = new QAction(QString("Action %0").arg(i),this); //act->setCheckable(true); //screenMenu->addAction(act); } } void wfmain::popupScreenMenu(QPoint pos) { screenMenu->popup(this->mapToGlobal(pos)); } wfmain::~wfmain() { qInfo() << "wfmain destroyed" << this; if(rigThread != nullptr) { rigThread->quit(); rigThread->wait(); } if (serverThread != nullptr) { serverThread->quit(); serverThread->wait(); } // Delete all shortcuts for (const auto &s: shortcuts) { delete s; } shortcuts.clear(); // Each rig needs deleting before we close. for (auto &rig: serverConfig.rigs) { delete rig; } if (clusterThread != nullptr) { clusterThread->quit(); clusterThread->wait(); } if (tciThread != nullptr) { tciThread->quit(); tciThread->wait(); } if (rigCtl != nullptr) { delete rigCtl; } if (colorPrefs != nullptr) { delete colorPrefs; } delete rpt; delete ui; delete settings; #if defined(USB_CONTROLLER) if (usbControllerThread != nullptr) { usbControllerThread->quit(); usbControllerThread->wait(); } #if defined(Q_OS_LINUX) if (uDevMonitor) { udev_monitor_unref(uDevMonitor); udev_unref(uDev); delete uDevNotifier; } #endif #endif //logStream->flush(); } void wfmain::closeEvent(QCloseEvent *event) { if (on_exitBtn_clicked()) event->ignore(); } void wfmain::openRig() { // This function is intended to handle opening a connection to the rig. // the connection can be either serial or network, // and this function is also responsible for initiating the search for a rig model and capabilities. // Any errors, such as unable to open connection or unable to open port, are to be reported to the user. //TODO: if(hasRunPreviously) //TODO: if(useNetwork){... // } else { // if (prefs.fileWasNotFound) { // showRigSettings(); // rig setting dialog box for network/serial, CIV, hostname, port, baud rate, serial device, etc // TODO: How do we know if the setting was loaded? emit connectionStatus(true); // Signal any other parts that need to know if we are connecting/connected. ui->connectBtn->setText("Cancel connection"); // We are attempting to connect connStatus = connConnecting; isRadioAdmin = true; // Set user to admin, will be reset if not. // M0VSE: This could be in a better place maybe? if (prefs.audioSystem == tciAudio) { prefs.rxSetup.tci = tci; prefs.txSetup.tci = tci; } makeRig(); if (prefs.enableLAN) { // "We need to setup the tx/rx audio: udpPrefs.waterfallFormat = prefs.waterfallFormat; // 60 second connection timeout. ConnectionTimer.start(60000); emit sendNetworkCommSetup(rigList, prefs.radioCIVAddr, udpPrefs, prefs.rxSetup, prefs.txSetup, prefs.virtualSerialPort, prefs.tcpPort); } else { if( (prefs.serialPortRadio.toLower() == QString("auto"))) { findSerialPort(); } else { serialPortRig = prefs.serialPortRadio; } emit sendSerialCommSetup(rigList, prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort, prefs.tcpPort,prefs.waterfallFormat); ui->statusBar->showMessage(QString("Connecting to rig using serial port ").append(serialPortRig), 1000); } } // Deprecated (moved to makeRig()) void wfmain::rigConnections() { } void wfmain::makeRig() { if (rigThread == nullptr) { switch (prefs.manufacturer){ case manufIcom: rig = new icomCommander(); break; case manufKenwood: rig = new kenwoodCommander(); break; case manufYaesu: rig = new yaesuCommander(); break; default: qCritical() << "Unknown Manufacturer, aborting..."; break; } rigThread = new QThread(this); rigThread->setObjectName("rigCommander()"); // Thread: rig->moveToThread(rigThread); connect(rigThread, SIGNAL(started()), rig, SLOT(process())); connect(rigThread, SIGNAL(finished()), rig, SLOT(deleteLater())); rigThread->start(); // Rig status and Errors: connect(rig, SIGNAL(havePortError(errorType)), this, SLOT(receivePortError(errorType))); connect(rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); connect(rig, SIGNAL(haveNetworkAudioLevels(networkAudioLevels)), this, SLOT(receiveNetworkAudioLevels(networkAudioLevels))); connect(rig, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); connect(rig, SIGNAL(setRadioUsage(quint8, bool, quint8, QString, QString)), selRad, SLOT(setInUse(quint8, bool, quint8, QString, QString))); connect(rig, SIGNAL(setRadioUsage(quint8, bool, quint8, QString, QString)), this, SLOT(radioInUse(quint8, bool, quint8, QString, QString))); connect(selRad, SIGNAL(selectedRadio(quint8)), rig, SLOT(setCurrentRadio(quint8))); // Rig comm setup: connect(this, SIGNAL(setPTTType(pttType_t)), rig, SLOT(setPTTType(pttType_t))); connect(rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); connect(this, SIGNAL(sendCloseComm()), rig, SLOT(closeComm())); connect(this, SIGNAL(sendChangeLatency(quint16)), rig, SLOT(changeLatency(quint16))); connect(this, SIGNAL(setRigID(quint16)), rig, SLOT(setRigID(quint16))); connect(rig, SIGNAL(commReady()), this, SLOT(receiveCommReady())); if (serverConfig.enabled) { qInfo(logRigServer()) << "**** Connecting rig instance to server"; connect(rig, SIGNAL(haveAudioData(audioPacket)), server, SLOT(receiveAudioData(audioPacket))); connect(rig, SIGNAL(haveDataForServer(QByteArray)), server, SLOT(dataForServer(QByteArray))); connect(server, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); } connect(this, SIGNAL(setCIVAddr(quint16)), rig, SLOT(setCIVAddr(quint16))); connect(this, SIGNAL(sendPowerOn()), rig, SLOT(powerOn())); connect(this, SIGNAL(sendPowerOff()), rig, SLOT(powerOff())); connect(this, SIGNAL(getDebug()), rig, SLOT(getDebug())); connect(rig, SIGNAL(haveRptOffsetFrequency(freqt)), rpt, SLOT(handleRptOffsetFrequency(freqt))); connect(this->rpt, &repeaterSetup::getTone, this->rig, [=]() { qDebug(logSystem()) << "Asking for TONE"; queue->add(priorityImmediate,funcRepeaterTone,false,false);}); connect(this->rpt, &repeaterSetup::getTSQL, this->rig, [=]() { qDebug(logSystem()) << "Asking for TSQL"; queue->add(priorityImmediate,funcRepeaterTSQL,false,false);}); connect(this->rpt, &repeaterSetup::setDTCS, this->rig, [=](const toneInfo& t) { qDebug(logSystem()) << "Setting DCS, code =" << t.tone; queue->add(priorityImmediate,queueItem(funcRepeaterDTCS,QVariant::fromValue(t),false));}); connect(this->rpt, &repeaterSetup::getRptAccessMode, this->rig, [=]() { if (rigCaps->commands.contains(funcToneSquelchType)) { queue->add(priorityImmediate,funcToneSquelchType,false,false); } else { queue->add(priorityImmediate,funcRepeaterTone,false,false); queue->add(priorityImmediate,funcRepeaterTSQL,false,false); } }); connect(this->rig, &rigCommander::haveDuplexMode, this->rpt, [=](const duplexMode_t &dm) { if(dm==dmSplitOn) this->splitModeEnabled = true; else this->splitModeEnabled = false; }); connect(this->rpt, &repeaterSetup::selectVFO, this->rig, [=](const vfo_t &v) { queue->add(priorityImmediate,queueItem(funcSelectVFO,QVariant::fromValue(v),false));}); connect(this->rpt, &repeaterSetup::equalizeVFOsAB, this->rig, [=]() { queue->add(priorityImmediate,funcVFOEqualAB,false,false);}); connect(this->rpt, &repeaterSetup::equalizeVFOsMS, this->rig, [=]() { queue->add(priorityImmediate,funcVFOEqualMS,false,false);}); connect(this->rpt, &repeaterSetup::swapVFOs, this->rig, [=]() { queue->add(priorityImmediate,funcVFOSwapMS,false,false);}); connect(this->rpt, &repeaterSetup::setRptDuplexOffset, this->rig, [=](const freqt &fOffset) { queue->add(priorityImmediate,queueItem(funcSendFreqOffset,QVariant::fromValue(fOffset),false));}); connect(this->rpt, &repeaterSetup::getRptDuplexOffset, this->rig, [=]() { queue->add(priorityImmediate,funcReadFreqOffset,false,false);}); // Create link for server so it can have easy access to rig. if (serverConfig.rigs.first() != nullptr) { serverConfig.rigs.first()->rig = rig; serverConfig.rigs.first()->rigThread = rigThread; } } } void wfmain::removeRig() { ConnectionTimer.stop(); if (rigThread != nullptr) { rigThread->quit(); rigThread->wait(); rig = nullptr; rigThread = nullptr; } if (cw != nullptr) { prefs.cwCutNumbers = cw->getCutNumbers(); prefs.cwSendImmediate = cw->getSendImmediate(); prefs.cwSidetoneEnabled = cw->getSidetoneEnable(); prefs.cwSidetoneLevel = cw->getSidetoneLevel(); prefs.cwMacroList = cw->getMacroText(); delete cw; cw = nullptr; ui->cwButton->setEnabled(false); } for (const auto &receiver:receivers) { delete receiver; } receivers.clear(); currentReceiver=0; // Disable all controls until connected. enableControls(false); } void wfmain::findSerialPort() { // Find the ICOM radio connected, or, if none, fall back to OS default. // qInfo(logSystem()) << "Searching for serial port..."; bool found = false; // First try to find first Icom port: for(const QSerialPortInfo & serialPortInfo: QSerialPortInfo::availablePorts()) { if (serialPortInfo.serialNumber().left(3) == "IC-") { qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); #if defined(Q_OS_LINUX) || defined(Q_OS_MAC) serialPortRig = (QString("/dev/") + serialPortInfo.portName()); #else serialPortRig = serialPortInfo.portName(); #endif found = true; break; } } if (!found) { QDirIterator it73("/dev/serial/by-id", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories); QDirIterator it97("/dev/serial", QStringList() << "*IC-9700*A*", QDir::Files, QDirIterator::Subdirectories); QDirIterator it785x("/dev/serial", QStringList() << "*IC-785*A*", QDir::Files, QDirIterator::Subdirectories); QDirIterator it705("/dev/serial", QStringList() << "*IC-705*A", QDir::Files, QDirIterator::Subdirectories); QDirIterator it7610("/dev/serial", QStringList() << "*IC-7610*A", QDir::Files, QDirIterator::Subdirectories); QDirIterator itR8600("/dev/serial", QStringList() << "*IC-R8600*A", QDir::Files, QDirIterator::Subdirectories); if(!it73.filePath().isEmpty()) { // IC-7300 serialPortRig = it73.filePath(); // first } else if(!it97.filePath().isEmpty()) { // IC-9700 serialPortRig = it97.filePath(); } else if(!it785x.filePath().isEmpty()) { // IC-785x serialPortRig = it785x.filePath(); } else if(!it705.filePath().isEmpty()) { // IC-705 serialPortRig = it705.filePath(); } else if(!it7610.filePath().isEmpty()) { // IC-7610 serialPortRig = it7610.filePath(); } else if(!itR8600.filePath().isEmpty()) { // IC-R8600 serialPortRig = itR8600.filePath(); } else { //fall back: qInfo(logSystem()) << "Could not find Icom serial port. Falling back to OS default. Use --port to specify, or modify preferences."; #ifdef Q_OS_MAC serialPortRig = QString("/dev/tty.SLAB_USBtoUART"); #endif #ifdef Q_OS_LINUX serialPortRig = QString("/dev/ttyUSB0"); #endif #ifdef Q_OS_WIN serialPortRig = QString("COM1"); #endif } } } void wfmain::receiveCommReady() { //qInfo(logSystem()) << "Received CommReady!! "; if(!prefs.enableLAN) { // If we're not using the LAN, then we're on serial, and // we already know the baud rate and can calculate the timing parameters. calculateTimingParameters(); } if(prefs.radioCIVAddr == 0) { // tell rigCommander to broadcast a request for all rig IDs. // qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)"; ui->statusBar->showMessage(QString("Searching CI-V bus for connected radios."), 1000); queue->addUnique(priorityHigh,funcTransceiverId,true); } else { // don't bother, they told us the CIV they want, stick with it. // We still query the rigID to find the model, but at least we know the CIV. qInfo(logSystem()) << QString("Skipping automatic CIV, using user-supplied value of 0x%1").arg(prefs.radioCIVAddr,2,16) ; showStatusBarText(QString("Using user-supplied radio CI-V address of 0x%1").arg(prefs.radioCIVAddr, 2, 16)); if(prefs.CIVisRadioModel) { qInfo(logSystem()) << QString("Skipping Rig ID query, using user-supplied model from CI-V address: 0x%1").arg(prefs.radioCIVAddr,2,16) ; emit setCIVAddr(prefs.radioCIVAddr); emit setRigID(prefs.radioCIVAddr); } else { emit setCIVAddr(prefs.radioCIVAddr); queue->addUnique(priorityHigh,funcTransceiverId,true); } } if (prefs.autoPowerOn) { // After 2s send powerOn command QTimer::singleShot(2000, rig, SLOT(powerOn())); } } void wfmain::receivePortError(errorType err) { if (err.alert) { connectionHandler(false); // Force disconnect QMessageBox::critical(this, err.device, QString("%0: %1").arg(err.device,err.message), QMessageBox::Ok); } else { qInfo(logSystem()) << "wfmain: received error for device: " << err.device << " with message: " << err.message; ui->statusBar->showMessage(QString("ERROR: using device ").append(err.device).append(": ").append(err.message), 10000); } // TODO: Dialog box, exit, etc } void wfmain::receiveStatusUpdate(networkStatus status) { if (queue->interval()==-1) { return; } // If we have received very high network latency, increase queue interval, don't set it to higher than 500ms though. if (prefs.polling_ms != 0 && queue->interval() == quint32(delayedCmdIntervalLAN_ms) && (status.networkLatency/2) > quint32(delayedCmdIntervalLAN_ms)) { qInfo(logRig()) << QString("1/2 network latency %0 exceeds configured queue interval %1, increasing").arg(status.networkLatency/2).arg(delayedCmdIntervalLAN_ms); queue->interval(qMin(quint32(status.networkLatency/2),quint32(500))); } else if (prefs.polling_ms != 0 && queue->interval() != quint32(delayedCmdIntervalLAN_ms) && (status.networkLatency/2) < quint32(delayedCmdIntervalLAN_ms)) { // Maybe it was a temporary issue, restore to default queue->interval(delayedCmdIntervalLAN_ms); } //this->rigStatus->setText(QString("%0/%1 %2").arg(mainElapsed).arg(subElapsed).arg(status.message)); this->rigStatus->setText(status.message); selRad->audioOutputLevel(status.rxAudioLevel); selRad->audioInputLevel(status.txAudioLevel); selRad->addTimeDifference(status.timeDifference); //qInfo(logSystem()) << "Got Status Update" << status.rxAudioLevel; } void wfmain::receiveNetworkAudioLevels(networkAudioLevels l) { meter_t m = meterNone; if(l.haveRxLevels) { m = meterRxAudio; receiveMeter(m, l.rxAudioPeak); } if(l.haveTxLevels) { m = meterTxMod; receiveMeter(m, l.txAudioPeak); } } void wfmain::setupMainUI() { // Set scroll wheel response (tick interval) // and set arrow key response (single step) ui->rfGainSlider->setTickInterval(100); ui->rfGainSlider->setSingleStep(10); ui->afGainSlider->setTickInterval(100); ui->afGainSlider->setSingleStep(10); ui->sqlSlider->setTickInterval(100); ui->sqlSlider->setSingleStep(10); ui->txPowerSlider->setTickInterval(100); ui->txPowerSlider->setSingleStep(10); ui->micGainSlider->setTickInterval(100); ui->micGainSlider->setSingleStep(10); ui->monitorSlider->setTickInterval(100); ui->monitorSlider->setSingleStep(10); // Keep this code when the rest is removed from this function: qDebug(logSystem()) << "Running with debugging options enabled."; rigStatus = new QLabel(this); ui->statusBar->addPermanentWidget(rigStatus); ui->statusBar->showMessage("Connecting to rig...", 1000); pttLed = new QLedLabel(this); ui->statusBar->addPermanentWidget(pttLed); pttLed->setState(QLedLabel::State::StateOk); pttLed->setToolTip("Receiving"); connectedLed = new QLedLabel(this); ui->statusBar->addPermanentWidget(connectedLed); rigName = new QLabel(this); rigName->setAlignment(Qt::AlignRight); ui->statusBar->addPermanentWidget(rigName); rigName->setText("NONE"); rigName->setFixedWidth(60); freqt f; f.MHzDouble = 0.0; f.Hz = 0; oldFreqDialVal = ui->freqDial->value(); ui->tuneLockChk->setChecked(false); freqLock = false; connect( ui->txPowerSlider, &QSlider::valueChanged, this, [=](const int &newValue) { if (ui->txPowerSlider->maximum()) statusFromSliderPercent("Tx Power", 255*newValue/ui->txPowerSlider->maximum()); } ); connect( ui->rfGainSlider, &QSlider::valueChanged, this, [=](const int &newValue) { if (ui->rfGainSlider->maximum()) statusFromSliderPercent("RF Gain", 255*newValue/ui->rfGainSlider->maximum()); } ); connect( ui->afGainSlider, &QSlider::valueChanged, this, [=](const int &newValue) { if (ui->afGainSlider->maximum()) statusFromSliderPercent("AF Gain", 255*newValue/ui->afGainSlider->maximum()); } ); connect( ui->micGainSlider, &QSlider::valueChanged, this, [=](const int &newValue) { if (ui->micGainSlider->maximum()) statusFromSliderPercent("TX Audio Gain", 255*newValue/ui->micGainSlider->maximum()); } ); connect( ui->sqlSlider, &QSlider::valueChanged, this, [=](const int &newValue) { if (ui->sqlSlider->maximum()) statusFromSliderPercent("Squelch", 255*newValue/ui->sqlSlider->maximum()); } ); connect( ui->monitorSlider, &QSlider::valueChanged, this, [=](const int &newValue) { if (ui->monitorSlider->maximum()) statusFromSliderPercent("Monitor", 255*newValue/ui->monitorSlider->maximum()); } ); } void wfmain::connectSettingsWidget() { connect(setupui, SIGNAL(changedClusterPref(prefClusterItem)), this, SLOT(extChangedClusterPref(prefClusterItem))); connect(setupui, SIGNAL(changedClusterPrefs(quint64)), this, SLOT(extChangedClusterPrefs(quint64))); connect(setupui, SIGNAL(changedCtPref(prefCtItem)), this, SLOT(extChangedCtPref(prefCtItem))); connect(setupui, SIGNAL(changedCtPrefs(quint64)), this, SLOT(extChangedCtPrefs(quint64))); connect(setupui, SIGNAL(changedIfPref(prefIfItem)), this, SLOT(extChangedIfPref(prefIfItem))); connect(setupui, SIGNAL(changedIfPrefs(quint64)), this, SLOT(extChangedIfPrefs(quint64))); connect(setupui, SIGNAL(changedColPref(prefColItem)), this, SLOT(extChangedColPref(prefColItem))); connect(setupui, SIGNAL(changedColPrefs(quint64)), this, SLOT(extChangedColPrefs(quint64))); connect(setupui, SIGNAL(changedLanPref(prefLanItem)), this, SLOT(extChangedLanPref(prefLanItem))); connect(setupui, SIGNAL(changedLanPrefs(quint64)), this, SLOT(extChangedLanPrefs(quint64))); connect(setupui, SIGNAL(changedRaPref(prefRaItem)), this, SLOT(extChangedRaPref(prefRaItem))); connect(setupui, SIGNAL(changedRaPrefs(quint64)), this, SLOT(extChangedRaPrefs(quint64))); connect(setupui, SIGNAL(changedRsPref(prefRsItem)), this, SLOT(extChangedRsPref(prefRsItem))); connect(setupui, SIGNAL(changedRsPrefs(quint64)), this, SLOT(extChangedRsPrefs(quint64))); connect(setupui, SIGNAL(changedUdpPref(prefUDPItem)), this, SLOT(extChangedUdpPref(prefUDPItem))); connect(setupui, SIGNAL(changedUdpPrefs(quint64)), this, SLOT(extChangedUdpPrefs(quint64))); connect(setupui, SIGNAL(changedServerPref(prefServerItem)), this, SLOT(extChangedServerPref(prefServerItem))); connect(setupui, SIGNAL(changedServerPrefs(quint64)), this, SLOT(extChangedServerPrefs(quint64))); connect(this, SIGNAL(connectionStatus(bool)), setupui, SLOT(connectionStatus(bool))); connect(setupui, SIGNAL(connectButtonPressed()), this, SLOT(handleExtConnectBtn())); connect(setupui, SIGNAL(saveSettingsButtonPressed()), this, SLOT(on_saveSettingsBtn_clicked())); connect(setupui, SIGNAL(revertSettingsButtonPressed()), this, SLOT(handleRevertSettingsBtn())); } // NOT Migrated, EHL TODO, carefully remove this function void wfmain::getSettingsFilePath(QString settingsFile) { if (settingsFile.isNull()) { settings = new QSettings(); } else { QString file = settingsFile; QFile info(settingsFile); QString path=""; if (!QFileInfo(info).isAbsolute()) { path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); if (path.isEmpty()) { path = QDir::homePath(); } path = path + "/"; file = info.fileName(); } settings = new QSettings(path + file, QSettings::Format::IniFormat); } } void wfmain::setInitialTiming() { loopTickCounter = 0; delayedCmdIntervalLAN_ms = 70; // interval for regular delayed commands, including initial rig/UI state queries delayedCmdIntervalSerial_ms = 100; // interval for regular delayed commands, including initial rig/UI state queries delayedCmdStartupInterval_ms = 250; // interval for rigID polling queue->interval(delayedCmdStartupInterval_ms); pttTimer = new QTimer(this); pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms pttTimer->setSingleShot(true); connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit())); timeSync = new QTimer(this); connect(timeSync, SIGNAL(timeout()), this, SLOT(setRadioTimeDateSend())); waitingToSetTimeDate = false; lastFreqCmdTime_ms = QDateTime::currentMSecsSinceEpoch() - 5000; // 5 seconds ago } void wfmain::setServerToPrefs() { // Start server if enabled in config //ui->serverSetupGroup->setEnabled(serverConfig.enabled); if (serverThread != nullptr) { serverThread->quit(); serverThread->wait(); serverThread = nullptr; server = nullptr; ui->statusBar->showMessage(QString("Server disabled"), 1000); } if (serverConfig.enabled) { serverConfig.lan = prefs.enableLAN; switch (prefs.manufacturer) { case manufIcom: server = new icomServer(&serverConfig); break; case manufKenwood: server = new kenwoodServer(&serverConfig); break; case manufYaesu: server = new yaesuServer(&serverConfig); break; default: return; } serverThread = new QThread(this); server->moveToThread(serverThread); if (serverConfig.lan) { connect(server, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); } else { qInfo(logAudio()) << "Audio Input device " << serverConfig.rigs.first()->rxAudioSetup.name; qInfo(logAudio()) << "Audio Output device " << serverConfig.rigs.first()->txAudioSetup.name; } connect(this, SIGNAL(initServer()), server, SLOT(init())); connect(serverThread, SIGNAL(finished()), server, SLOT(deleteLater())); serverThread->start(); emit initServer(); ui->statusBar->showMessage(QString("Server enabled"), 1000); } } void wfmain::configureVFOs() { qInfo(logSystem()) << "Running configureVFOs()"; if (QThread::currentThread() != QCoreApplication::instance()->thread()) { qCritical(logSystem()) << "Thread is NOT the main UI thread, cannot create VFO"; return; } if (receivers.size()) { foreach (receiverWidget* receiver, receivers) { ui->vfoLayout->removeWidget(receiver); delete receiver; } receivers.clear(); } for(uchar i=0;inumReceiver;i++) {/* receiverWidget* receiver = new receiverWidget(rigCaps->hasSpectrum,i,rigCaps->numVFO,this); receiver->prepareScope(rigCaps->spectAmpMax, rigCaps->spectLenMax); receiver->setSeparators(prefs.groupSeparator,prefs.decimalSeparator); //receiver->setUnderlayMode(prefs.underlayMode); receiver->wfAntiAliased(prefs.wfAntiAlias); receiver->wfInterpolate(prefs.wfInterpolate); receiver->setScrollSpeedXY(prefs.scopeScrollX, prefs.scopeScrollY); receiver->prepareWf(i==0?prefs.mainWflength:prefs.subWflength); receiver->setRange(i==0?prefs.mainPlotFloor:prefs.subPlotFloor,i==0?prefs.mainPlotCeiling:prefs.subPlotCeiling); receiver->setTheme(WaterfallItem::Theme(i==0?prefs.mainWfTheme:prefs.subWfTheme),false); receiver->setClickDragTuning(prefs.clickDragTuningEnable); receiver->setTuningFloorZeros(prefs.niceTS); receiver->resizePlasmaBuffer(prefs.underlayBufferSize); receiver->setUnit((FctlUnit)prefs.frequencyUnits); colorPrefsType p = colorPreset[prefs.currentColorPresetNumber]; receiver->colorPreset(&p); receiver->setIdentity(i==0?"Main Band":"Sub Band"); ui->vfoLayout->addWidget(receiver); ui->vfoLayout->setStretchFactor(receiver, 1); // Hide any secondary receivers until we need them! receiver->selected(i==0?true:false); receiver->setVisible(i==0?true:false); connect(receiver, SIGNAL(frequencyRange(uchar, double, double)), cluster, SLOT(freqRange(uchar, double, double))); connect(cluster, SIGNAL(sendSpots(uchar, QVector)), receiver, SLOT(receiveSpots(uchar, QVector))); connect(cluster, SIGNAL(sendOutput(QString)), this, SLOT(receiveClusterOutput(QString))); connect(receiver, SIGNAL(updateSettings(uchar,int,quint16,int,int)), this, SLOT(receiveScopeSettings(uchar,int,quint16,int,int))); connect(receiver, SIGNAL(dataChanged(modeInfo)), this, SLOT(dataModeChanged(modeInfo))); connect(receiver, &receiverWidget::bandChanged, receiver, [=](const uchar& rx, const bandType& b) { if (rx == currentReceiver && rigCaps->commands.contains(funcAntenna)) { ui->antennaSelCombo->setEnabled(b.ants); if (b.ants) { queue->addUnique(priorityMediumHigh,queueItem(funcAntenna,true,i)); } else { queue->del(funcAntenna,i); } } }); connect(receiver,SIGNAL(showStatusBarText(QString)),this,SLOT(showStatusBarText(QString))); connect(receiver,SIGNAL(sendScopeImage(uchar)),this,SLOT(receiveScopeImage(uchar))); receivers.append(receiver); // Currently tracking is disabled //if (receivers.size() > 1) //{ // connect(receivers[0], SIGNAL(sendTrack(int)),receivers[1], SLOT(receiveTrack(int))); //} */ } } void wfmain::setSerialDevicesUI() { } QShortcut* wfmain::setupKeyShortcut(const QKeySequence k, bool appWide) { QShortcut* sc=new QShortcut(this); sc->setKey(k); if(appWide) sc->setContext(Qt::ApplicationShortcut); connect(sc, &QShortcut::activated, this, [=]() { this->runShortcut(k); }); return sc; } void wfmain::setupKeyShortcuts() { shortcuts.append(setupKeyShortcut(Qt::Key_F1, true)); shortcuts.append(setupKeyShortcut(Qt::Key_F2, true)); shortcuts.append(setupKeyShortcut(Qt::Key_F3, true)); shortcuts.append(setupKeyShortcut(Qt::Key_F4, true)); shortcuts.append(setupKeyShortcut(Qt::Key_F5)); shortcuts.append(setupKeyShortcut(Qt::Key_F6)); shortcuts.append(setupKeyShortcut(Qt::Key_F7)); shortcuts.append(setupKeyShortcut(Qt::Key_F8)); shortcuts.append(setupKeyShortcut(Qt::Key_F9)); shortcuts.append(setupKeyShortcut(Qt::Key_F10)); shortcuts.append(setupKeyShortcut(Qt::Key_F11)); shortcuts.append(setupKeyShortcut(Qt::Key_F12)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_T)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_R)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_P)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_I)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_U)); shortcuts.append(setupKeyShortcut(Qt::Key_Asterisk)); shortcuts.append(setupKeyShortcut(Qt::Key_Slash)); shortcuts.append(setupKeyShortcut(Qt::Key_Backslash)); shortcuts.append(setupKeyShortcut(Qt::Key_Minus)); shortcuts.append(setupKeyShortcut(Qt::Key_Plus)); shortcuts.append(setupKeyShortcut(Qt::SHIFT | Qt::Key_Minus)); shortcuts.append(setupKeyShortcut(Qt::SHIFT | Qt::Key_Plus)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_Minus)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_Plus)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_Q)); shortcuts.append(setupKeyShortcut(Qt::Key_PageUp)); shortcuts.append(setupKeyShortcut(Qt::Key_PageDown)); shortcuts.append(setupKeyShortcut(Qt::Key_F)); shortcuts.append(setupKeyShortcut(Qt::Key_M)); shortcuts.append(setupKeyShortcut(Qt::Key_K)); shortcuts.append(setupKeyShortcut(Qt::Key_J)); shortcuts.append(setupKeyShortcut(Qt::SHIFT | Qt::Key_K)); shortcuts.append(setupKeyShortcut(Qt::SHIFT | Qt::Key_J)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_K)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_J)); shortcuts.append(setupKeyShortcut(Qt::Key_H)); shortcuts.append(setupKeyShortcut(Qt::Key_L)); shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_D)); #if (QT_VERSION > QT_VERSION_CHECK(6,0,0)) shortcuts.append(setupKeyShortcut(Qt::CTRL | Qt::Key_D)); #endif } void wfmain::runShortcut(const QKeySequence k) { qInfo() << "Running shortcut for key:" << k; if (k == Qt::Key_F1) { this->raise(); } else if (k==Qt::Key_F11) { if(onFullscreen) { this->showNormal(); onFullscreen = false; prefs.useFullScreen = false; } else { this->showFullScreen(); onFullscreen = true; prefs.useFullScreen = true; } setupui->updateIfPref(if_useFullScreen); } else if (k==(Qt::CTRL | Qt::Key_Q)) { on_exitBtn_clicked(); } else if (k==(Qt::CTRL | Qt::Key_D) || k==(Qt::CTRL | Qt::SHIFT | Qt::Key_D)) { debugBtn_clicked(); } else if (rigCaps == nullptr) { // No rig yet qWarning() << "Cannot run shortcut as not connected to rig"; return; } // All of the below commands require a connected rig bool freqUpdate=false; freqt f; if (k == Qt::Key_F2) { showAndRaiseWidget(bandbtns); } else if (k==Qt::Key_F3 || k==Qt::Key_Asterisk) { showAndRaiseWidget(finputbtns); } else if (k==Qt::Key_F4) { showAndRaiseWidget(setupui); } else if (k==Qt::Key_F5) { changeMode(modeLSB, false, currentReceiver); } else if (k==Qt::Key_F6) { changeMode(modeUSB, false, currentReceiver); } else if (k==Qt::Key_F7) { changeMode(modeAM, false, currentReceiver); } else if (k==Qt::Key_F8) { changeMode(modeCW, false, currentReceiver); } else if (k==Qt::Key_F9) { changeMode(modeUSB, true, currentReceiver); } else if (k==Qt::Key_F10) { changeMode(modeFM, false, currentReceiver); } else if (k==Qt::Key_F12) { showStatusBarText("Sending speech command to radio."); queue->add(priorityImmediate,queueItem(funcSpeech,QVariant::fromValue(uchar(0U)),false,currentReceiver)); } else if (k==(Qt::CTRL | Qt::Key_T)) { // Transmit qDebug(logSystem()) << "Activated Control-T shortcut"; showStatusBarText(QString("Transmitting. Press Control-R to receive.")); extChangedRsPrefs(rs_pttOn); } else if (k==(Qt::CTRL | Qt::Key_R)) { // Receive extChangedRsPrefs(rs_pttOff); } else if (k==(Qt::CTRL | Qt::Key_P)) { // Toggle PTT if(amTransmitting) { extChangedRsPrefs(rs_pttOff); } else { extChangedRsPrefs(rs_pttOn); showStatusBarText(QString("Transmitting. Press Control-P again to receive.")); } } else if (k==(Qt::CTRL | Qt::Key_I)) { // Enable ATU ui->tuneEnableChk->click(); } else if (k==(Qt::CTRL | Qt::Key_U)) { // Run ATU tuning cycle ui->tuneNowBtn->click(); } else if (k==Qt::Key_Slash) { for (size_t i = 0; i < rigCaps->modes.size(); i++) { if (rigCaps->modes[i].mk == receivers[currentReceiver]->getMode().mk) { if (i + 1 < rigCaps->modes.size()) { changeMode(rigCaps->modes[i + 1].mk,false,currentReceiver); } else { changeMode(rigCaps->modes[0].mk,false,currentReceiver); } break; } } } else if (k==Qt::Key_Backslash) { bool found = false; availableBands band = bandUnknown; auto b = rigCaps->bands.cend(); while (b != rigCaps->bands.cbegin()) { b--; if ((b->region == "" || prefs.region == b->region)) { if (!found && b->band == bandbtns->currentBand()) { found = true; } else if (found && b->band != bandbtns->currentBand()) { qInfo() << "Got new band:" << b->band << "Name:" << b->name << "region" << b->region; band = b->band; break; } } } if (band == bandUnknown) { band = rigCaps->bands[rigCaps->bands.size() - 1].band; } bandbtns->setBand(band); } else if (k==Qt::Key_Minus|| k==Qt::Key_J) { f.Hz = roundFrequencyWithStep(receivers[0]->getFrequency().Hz, -1, tsPlusHz); freqUpdate=true; } else if (k==Qt::Key_Plus || k==Qt::Key_K) { f.Hz = roundFrequencyWithStep(receivers[0]->getFrequency().Hz, 1, tsPlusHz); freqUpdate=true; } else if (k==(Qt::SHIFT | Qt::Key_Minus) || k==(Qt::SHIFT | Qt::Key_J)) { f.Hz = roundFrequencyWithStep(receivers[0]->getFrequency().Hz, -1, tsPlusShiftHz); freqUpdate=true; } else if (k==(Qt::SHIFT | Qt::Key_Plus) || k==(Qt::SHIFT | Qt::Key_K)) { f.Hz = roundFrequencyWithStep(receivers[0]->getFrequency().Hz, 1, tsPlusShiftHz); freqUpdate=true; } else if (k==(Qt::CTRL | Qt::Key_Minus) || k==(Qt::CTRL | Qt::Key_J)) { f.Hz = roundFrequencyWithStep(receivers[0]->getFrequency().Hz, -1, tsPlusControlHz); freqUpdate=true; } else if (k==(Qt::CTRL | Qt::Key_Plus) || k==(Qt::CTRL | Qt::Key_K)) { f.Hz = roundFrequencyWithStep(receivers[0]->getFrequency().Hz, 1, tsPlusControlHz); freqUpdate=true; } else if (k==Qt::Key_PageUp) { f.Hz = receivers[0]->getFrequency().Hz + tsPageHz; freqUpdate=true; } else if (k==Qt::Key_PageDown) { f.Hz = receivers[0]->getFrequency().Hz - tsPageHz; freqUpdate=true; } else if (k==Qt::Key_F) { showStatusBarText("Sending speech command (frequency) to radio."); queue->add(priorityImmediate,queueItem(funcSpeech, QVariant::fromValue(1),false,currentReceiver)); } else if (k==Qt::Key_M) { showStatusBarText("Sending speech command (mode) to radio."); queue->add(priorityImmediate,queueItem(funcSpeech, QVariant::fromValue(2),false,currentReceiver)); } else if (k==Qt::Key_H) { f.Hz = roundFrequencyWithStep(receivers.first()->getFrequency().Hz, -1, tsKnobHz); freqUpdate = true; } else if (k==Qt::Key_L) { f.Hz = roundFrequencyWithStep(receivers.first()->getFrequency().Hz, 1, tsKnobHz); freqUpdate = true; } if (!freqLock && freqUpdate) { vfoCommandType t = queue->getVfoCommand(vfoA,currentReceiver,true); f.MHzDouble = f.Hz / (double)1E6; queue->add(priorityImmediate,queueItem(t.freqFunc, QVariant::fromValue(f),false,uchar(t.receiver))); } } void wfmain::setupUsbControllerDevice() { #if defined (USB_CONTROLLER) if (usbWindow == nullptr) { usbWindow = new controllerSetup(); } usbControllerDev = new usbController(); usbControllerThread = new QThread(this); usbControllerThread->setObjectName("usb()"); usbControllerDev->moveToThread(usbControllerThread); connect(usbControllerThread, SIGNAL(started()), usbControllerDev, SLOT(run())); connect(usbControllerThread, SIGNAL(finished()), usbControllerDev, SLOT(deleteLater())); connect(usbControllerThread, SIGNAL(finished()), usbWindow, SLOT(deleteLater())); // Delete window when controller is deleted connect(usbControllerDev, SIGNAL(sendJog(int)), this, SLOT(changeFrequency(int))); connect(usbControllerDev, SIGNAL(doShuttle(bool,quint8)), this, SLOT(doShuttle(bool,quint8))); connect(usbControllerDev, SIGNAL(button(const COMMAND*)), this, SLOT(buttonControl(const COMMAND*))); connect(usbControllerDev, SIGNAL(removeDevice(USBDEVICE*)), usbWindow, SLOT(removeDevice(USBDEVICE*))); connect(usbControllerDev, SIGNAL(initUI(usbDevMap*, QVector