From 6c02a78d62b55f188b5076b7ba249c16d145c66d Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 7 Dec 2020 19:32:43 +0100 Subject: [PATCH] VOR single channel: first working release of VOR localizer feature plugin --- plugins/channelrx/demodvorsc/vordemodsc.cpp | 15 +- plugins/channelrx/demodvorsc/vordemodscgui.ui | 10 +- .../channelrx/demodvorsc/vordemodscsink.cpp | 4 +- plugins/feature/vorlocalizer/vorlocalizer.cpp | 240 ++++++- plugins/feature/vorlocalizer/vorlocalizer.h | 26 +- .../feature/vorlocalizer/vorlocalizergui.cpp | 257 +++++--- .../feature/vorlocalizer/vorlocalizergui.h | 6 +- .../feature/vorlocalizer/vorlocalizergui.ui | 124 +++- .../vorlocalizer/vorlocalizerreport.cpp | 2 +- .../feature/vorlocalizer/vorlocalizerreport.h | 48 +- .../vorlocalizer/vorlocalizersettings.cpp | 16 + .../vorlocalizer/vorlocalizersettings.h | 30 +- .../vorlocalizer/vorlocalizerworker.cpp | 587 ++++++++++++++++-- .../feature/vorlocalizer/vorlocalizerworker.h | 83 ++- sdrbase/pipes/messagepipes.cpp | 7 +- sdrbase/pipes/messagepipes.h | 2 +- sdrbase/resources/webapi/doc/html2/index.html | 32 +- .../doc/swagger/include/VORDemodSC.yaml | 27 +- .../doc/swagger/include/VORLocalizer.yaml | 6 + sdrbase/util/average.h | 59 ++ sdrbase/webapi/webapiutils.cpp | 45 ++ sdrbase/webapi/webapiutils.h | 2 + .../api/swagger/include/VORDemodSC.yaml | 27 +- .../api/swagger/include/VORLocalizer.yaml | 6 + swagger/sdrangel/code/html2/index.html | 32 +- .../code/qt5/client/SWGVORDemodSCReport.cpp | 92 +++ .../code/qt5/client/SWGVORDemodSCReport.h | 24 + .../qt5/client/SWGVORLocalizerSettings.cpp | 46 ++ .../code/qt5/client/SWGVORLocalizerSettings.h | 12 + 29 files changed, 1671 insertions(+), 196 deletions(-) create mode 100644 sdrbase/util/average.h diff --git a/plugins/channelrx/demodvorsc/vordemodsc.cpp b/plugins/channelrx/demodvorsc/vordemodsc.cpp index a309b3edf..1b2d240b9 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.cpp +++ b/plugins/channelrx/demodvorsc/vordemodsc.cpp @@ -436,9 +436,17 @@ void VORDemodSC::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& respon response.getVorDemodScReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); response.getVorDemodScReport()->setSquelch(m_basebandSink->getSquelchOpen() ? 1 : 0); response.getVorDemodScReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); + response.getVorDemodScReport()->setNavId(m_settings.m_navId); response.getVorDemodScReport()->setRadial(m_radial); response.getVorDemodScReport()->setRefMag(m_refMag); response.getVorDemodScReport()->setVarMag(m_varMag); + float refMagDB = std::round(20.0*std::log10(m_refMag)); + float varMagDB = std::round(20.0*std::log10(m_varMag)); + bool validRefMag = refMagDB > m_settings.m_refThresholdDB; + bool validVarMag = varMagDB > m_settings.m_varThresholdDB; + response.getVorDemodScReport()->setValidRadial(validRefMag && validVarMag ? 1 : 0); + response.getVorDemodScReport()->setValidRefMag(validRefMag ? 1 : 0); + response.getVorDemodScReport()->setValidVarMag(validVarMag ? 1 : 0); if (response.getVorDemodScReport()->getMorseIdent()) { *response.getVorDemodScReport()->getMorseIdent() = m_morseIdent; @@ -502,12 +510,15 @@ void VORDemodSC::featuresSendSettings(QList& channelSettingsKeys, const void VORDemodSC::sendChannelReport(QList *messageQueues) { - SWGSDRangel::SWGChannelReport *swgChannelReport = new SWGSDRangel::SWGChannelReport(); - webapiFormatChannelReport(*swgChannelReport); QList::iterator it = messageQueues->begin(); for (; it != messageQueues->end(); ++it) { + SWGSDRangel::SWGChannelReport *swgChannelReport = new SWGSDRangel::SWGChannelReport(); + swgChannelReport->setDirection(0); + swgChannelReport->setChannelType(new QString(m_channelId)); + swgChannelReport->setVorDemodScReport(new SWGSDRangel::SWGVORDemodSCReport()); + webapiFormatChannelReport(*swgChannelReport); MainCore::MsgChannelReport *msg = MainCore::MsgChannelReport::create(this, swgChannelReport); (*it)->push(msg); } diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.ui b/plugins/channelrx/demodvorsc/vordemodscgui.ui index 963daf35e..a8f01752c 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.ui +++ b/plugins/channelrx/demodvorsc/vordemodscgui.ui @@ -303,6 +303,9 @@ 100 + + 1 + 0 @@ -367,6 +370,9 @@ 0 + + 1 + -40 @@ -416,6 +422,9 @@ 100 + + 1 + @@ -655,7 +664,6 @@ - diff --git a/plugins/channelrx/demodvorsc/vordemodscsink.cpp b/plugins/channelrx/demodvorsc/vordemodscsink.cpp index dc37c4699..5c31f63a2 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsink.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscsink.cpp @@ -302,7 +302,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) { if (m_ident != "") { - qDebug() << m_ident << " " << Morse::toString(m_ident); + qDebug() << "VORDemodSCSink::processOneSample:" << m_ident << " " << Morse::toString(m_ident); if (getMessageQueueToChannel()) { @@ -343,7 +343,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) m_ident = m_ident.simplified(); if (m_ident != "") { - qDebug() << m_ident << " " << Morse::toString(m_ident); + qDebug() << "VORDemodSCSink::processOneSample:" << m_ident << " " << Morse::toString(m_ident); if (getMessageQueueToChannel()) { diff --git a/plugins/feature/vorlocalizer/vorlocalizer.cpp b/plugins/feature/vorlocalizer/vorlocalizer.cpp index 56e2033df..a28c43ed0 100644 --- a/plugins/feature/vorlocalizer/vorlocalizer.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizer.cpp @@ -25,9 +25,16 @@ #include "SWGFeatureActions.h" #include "SWGSimplePTTReport.h" #include "SWGDeviceState.h" +#include "SWGChannelReport.h" #include "dsp/dspengine.h" +#include "dsp/dspdevicesourceengine.h" +#include "dsp/devicesamplesource.h" +#include "device/deviceset.h" +#include "channel/channelapi.h" +#include "maincore.h" +#include "vorlocalizerreport.h" #include "vorlocalizerworker.h" #include "vorlocalizer.h" @@ -41,8 +48,7 @@ const char* const VORLocalizer::m_featureIdURI = "sdrangel.feature.vorlocalizer" const char* const VORLocalizer::m_featureId = "VORLocalizer"; VORLocalizer::VORLocalizer(WebAPIAdapterInterface *webAPIAdapterInterface) : - Feature(m_featureIdURI, webAPIAdapterInterface), - m_ptt(false) + Feature(m_featureIdURI, webAPIAdapterInterface) { setObjectName(m_featureId); m_worker = new VorLocalizerWorker(webAPIAdapterInterface); @@ -64,7 +70,8 @@ void VORLocalizer::start() qDebug("VORLocalizer::start"); m_worker->reset(); - m_worker->setMessageQueueToGUI(getMessageQueueToGUI()); + m_worker->setMessageQueueToFeature(getInputMessageQueue()); + m_worker->setAvailableChannels(&m_availableChannels); bool ok = m_worker->startWork(); m_state = ok ? StRunning : StError; m_thread.start(); @@ -108,11 +115,129 @@ bool VORLocalizer::handleMessage(const Message& cmd) else if (MsgRefreshChannels::match(cmd)) { qDebug() << "VORLocalizer::handleMessage: MsgRefreshChannels"; - VorLocalizerWorker::MsgRefreshChannels *msg = VorLocalizerWorker::MsgRefreshChannels::create(); - m_worker->getInputMessageQueue()->push(msg); + updateChannels(); + return true; } - else + else if (MainCore::MsgChannelReport::match(cmd)) + { + MainCore::MsgChannelReport& report = (MainCore::MsgChannelReport&) cmd; + SWGSDRangel::SWGChannelReport* swgChannelReport = report.getSWGReport(); + QString *channelType = swgChannelReport->getChannelType(); + + if (*channelType == "VORDemodSC") + { + SWGSDRangel::SWGVORDemodSCReport *swgVORDemodSCReport = swgChannelReport->getVorDemodScReport(); + int navId = swgVORDemodSCReport->getNavId(); + + if (navId < 0) { // disregard message for unallocated channels + return true; + } + + bool singlePlan = (m_vorSinglePlans.contains(navId)) ? m_vorSinglePlans[navId] : false; + + // qDebug() << "VORLocalizer::handleMessage: MainCore::MsgChannelReport(VORDemodSC): " + // << "navId:" << navId + // << "singlePlanProvided" << m_vorSinglePlans.contains(navId) + // << "singlePlan:" << singlePlan; + + if (m_vorChannelReports.contains(navId)) + { + m_vorChannelReports[navId].m_radial = swgVORDemodSCReport->getRadial(); + m_vorChannelReports[navId].m_refMag = swgVORDemodSCReport->getRefMag(); + m_vorChannelReports[navId].m_varMag = swgVORDemodSCReport->getVarMag(); + m_vorChannelReports[navId].m_validRadial = swgVORDemodSCReport->getValidRadial() != 0; + m_vorChannelReports[navId].m_validRefMag = swgVORDemodSCReport->getValidRefMag() != 0; + m_vorChannelReports[navId].m_validVarMag = swgVORDemodSCReport->getValidVarMag() != 0; + m_vorChannelReports[navId].m_morseIdent = *swgVORDemodSCReport->getMorseIdent(); + } + else + { + m_vorChannelReports[navId] = VORChannelReport{ + swgVORDemodSCReport->getRadial(), + swgVORDemodSCReport->getRefMag(), + swgVORDemodSCReport->getVarMag(), + AverageUtil(), + AverageUtil(), + AverageUtil(), + swgVORDemodSCReport->getValidRadial() != 0, + swgVORDemodSCReport->getValidRefMag() != 0, + swgVORDemodSCReport->getValidVarMag() != 0, + *swgVORDemodSCReport->getMorseIdent() + }; + } + + if (m_vorChannelReports[navId].m_validRadial) { + m_vorChannelReports[navId].m_radialAvg(swgVORDemodSCReport->getRadial()); + } + if (m_vorChannelReports[navId].m_validRefMag) { + m_vorChannelReports[navId].m_refMagAvg(swgVORDemodSCReport->getRefMag()); + } + if (m_vorChannelReports[navId].m_validVarMag) { + m_vorChannelReports[navId].m_varMagAvg(swgVORDemodSCReport->getVarMag()); + } + + if (getMessageQueueToGUI()) + { + float radial = ((m_vorChannelReports[navId].m_radialAvg.getNumSamples() == 0) || singlePlan) ? + m_vorChannelReports[navId].m_radial : + m_vorChannelReports[navId].m_radialAvg.instantAverage(); + float refMag = ((m_vorChannelReports[navId].m_refMagAvg.getNumSamples() == 0) || singlePlan) ? + m_vorChannelReports[navId].m_refMag : + m_vorChannelReports[navId].m_refMagAvg.instantAverage(); + float varMag = ((m_vorChannelReports[navId].m_varMagAvg.getNumSamples() == 0) || singlePlan) ? + m_vorChannelReports[navId].m_varMag : + m_vorChannelReports[navId].m_varMagAvg.instantAverage(); + bool validRadial = singlePlan ? m_vorChannelReports[navId].m_validRadial : + m_vorChannelReports[navId].m_radialAvg.getNumSamples() != 0 || m_vorChannelReports[navId].m_validRadial; + bool validRefMag = singlePlan ? m_vorChannelReports[navId].m_validRefMag : + m_vorChannelReports[navId].m_refMagAvg.getNumSamples() != 0 || m_vorChannelReports[navId].m_validRefMag; + bool validVarMag = singlePlan ? m_vorChannelReports[navId].m_validVarMag : + m_vorChannelReports[navId].m_varMagAvg.getNumSamples() != 0 || m_vorChannelReports[navId].m_validVarMag; + VORLocalizerReport::MsgReportRadial *msgRadial = VORLocalizerReport::MsgReportRadial::create( + navId, + radial, + refMag, + varMag, + validRadial, + validRefMag, + validVarMag + ); + getMessageQueueToGUI()->push(msgRadial); + VORLocalizerReport::MsgReportIdent *msgIdent = VORLocalizerReport::MsgReportIdent::create( + navId, + m_vorChannelReports[navId].m_morseIdent + ); + getMessageQueueToGUI()->push(msgIdent); + } + } + + return true; + } + else if (VORLocalizerReport::MsgReportServiceddVORs::match(cmd)) + { + qDebug() << "VORLocalizer::handleMessage: MsgReportServiceddVORs:"; + VORLocalizerReport::MsgReportServiceddVORs& report = (VORLocalizerReport::MsgReportServiceddVORs&) cmd; + std::vector& vorNavIds = report.getNavIds(); + m_vorSinglePlans = report.getSinglePlans(); + + for (std::vector::const_iterator it = vorNavIds.begin(); it != vorNavIds.end(); ++it) + { + m_vorChannelReports[*it].m_radialAvg.reset(); + m_vorChannelReports[*it].m_refMagAvg.reset(); + m_vorChannelReports[*it].m_varMagAvg.reset(); + } + + if (getMessageQueueToGUI()) + { + VORLocalizerReport::MsgReportServiceddVORs *msgToGUI = VORLocalizerReport::MsgReportServiceddVORs::create(); + msgToGUI->getNavIds() = vorNavIds; + getMessageQueueToGUI()->push(msgToGUI); + } + + return true; + } + else { return false; } @@ -146,6 +271,8 @@ void VORLocalizer::applySettings(const VORLocalizerSettings& settings, bool forc << " m_title: " << settings.m_title << " m_rgbColor: " << settings.m_rgbColor << " m_magDecAdjust: " << settings.m_magDecAdjust + << " m_rrTime: " << settings.m_rrTime + << " m_centerShift: " << settings.m_centerShift << " force: " << force; QList reverseAPIKeys; @@ -159,6 +286,12 @@ void VORLocalizer::applySettings(const VORLocalizerSettings& settings, bool forc if ((m_settings.m_magDecAdjust != settings.m_magDecAdjust) || force) { reverseAPIKeys.append("magDecAdjust"); } + if ((m_settings.m_rrTime != settings.m_rrTime) || force) { + reverseAPIKeys.append("rrTime"); + } + if ((m_settings.m_centerShift != settings.m_centerShift) || force) { + reverseAPIKeys.append("centerShift"); + } VorLocalizerWorker::MsgConfigureVORLocalizerWorker *msg = VorLocalizerWorker::MsgConfigureVORLocalizerWorker::create( settings, force @@ -178,6 +311,75 @@ void VORLocalizer::applySettings(const VORLocalizerSettings& settings, bool forc m_settings = settings; } +void VORLocalizer::updateChannels() +{ + MainCore *mainCore = MainCore::instance(); + MessagePipes& messagePipes = mainCore->getMessagePipes(); + std::vector& deviceSets = mainCore->getDeviceSets(); + std::vector::const_iterator it = deviceSets.begin(); + m_availableChannels.clear(); + + int deviceIndex = 0; + + for (; it != deviceSets.end(); ++it, deviceIndex++) + { + DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine; + + if (deviceSourceEngine) + { + DeviceSampleSource *deviceSource = deviceSourceEngine->getSource(); + quint64 deviceCenterFrequency = deviceSource->getCenterFrequency(); + int basebandSampleRate = deviceSource->getSampleRate(); + + for (int chi = 0; chi < (*it)->getNumberOfChannels(); chi++) + { + ChannelAPI *channel = (*it)->getChannelAt(chi); + + if (channel->getURI() == "sdrangel.channel.vordemodsc") + { + if (!m_availableChannels.contains(channel)) + { + MessageQueue *messageQueue = messagePipes.registerChannelToFeature(channel, this, "report"); + QObject::connect( + messageQueue, + &MessageQueue::messageEnqueued, + this, + [=](){ this->handleChannelMessageQueue(messageQueue); }, + Qt::QueuedConnection + ); + } + + VORLocalizerSettings::AvailableChannel availableChannel = + VORLocalizerSettings::AvailableChannel{deviceIndex, chi, channel, deviceCenterFrequency, basebandSampleRate, -1}; + m_availableChannels[channel] = availableChannel; + } + } + } + } + + if (getMessageQueueToGUI()) + { + VORLocalizerReport::MsgReportChannels *msgToGUI = VORLocalizerReport::MsgReportChannels::create(); + std::vector& msgChannels = msgToGUI->getChannels(); + QHash::iterator it = m_availableChannels.begin(); + + for (; it != m_availableChannels.end(); ++it) + { + VORLocalizerReport::MsgReportChannels::Channel msgChannel = + VORLocalizerReport::MsgReportChannels::Channel{ + it->m_deviceSetIndex, + it->m_channelIndex + }; + msgChannels.push_back(msgChannel); + } + + getMessageQueueToGUI()->push(msgToGUI); + } + + VorLocalizerWorker::MsgRefreshChannels *msgToWorker = VorLocalizerWorker::MsgRefreshChannels::create(); + m_worker->getInputMessageQueue()->push(msgToWorker); +} + int VORLocalizer::webapiRun(bool run, SWGSDRangel::SWGDeviceState& response, QString& errorMessage) @@ -237,6 +439,8 @@ void VORLocalizer::webapiFormatFeatureSettings( response.getVorLocalizerSettings()->setRgbColor(settings.m_rgbColor); response.getVorLocalizerSettings()->setMagDecAdjust(settings.m_magDecAdjust); + response.getVorLocalizerSettings()->setRrTime(settings.m_rrTime); + response.getVorLocalizerSettings()->setCenterShift(settings.m_centerShift); response.getVorLocalizerSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); @@ -265,6 +469,12 @@ void VORLocalizer::webapiUpdateFeatureSettings( if (featureSettingsKeys.contains("magDecAdjust")) { settings.m_magDecAdjust = response.getVorLocalizerSettings()->getMagDecAdjust(); } + if (featureSettingsKeys.contains("rrTime")) { + settings.m_rrTime = response.getVorLocalizerSettings()->getRrTime(); + } + if (featureSettingsKeys.contains("centerShift")) { + settings.m_centerShift = response.getVorLocalizerSettings()->getCenterShift(); + } if (featureSettingsKeys.contains("useReverseAPI")) { settings.m_useReverseAPI = response.getVorLocalizerSettings()->getUseReverseApi() != 0; } @@ -302,6 +512,12 @@ void VORLocalizer::webapiReverseSendSettings(QList& channelSettingsKeys if (channelSettingsKeys.contains("magDecAdjust") || force) { swgVORLocalizerSettings->setMagDecAdjust(settings.m_magDecAdjust); } + if (channelSettingsKeys.contains("rrTime") || force) { + swgVORLocalizerSettings->setRrTime(settings.m_rrTime); + } + if (channelSettingsKeys.contains("centerShift") || force) { + swgVORLocalizerSettings->setCenterShift(settings.m_centerShift); + } QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings") .arg(settings.m_reverseAPIAddress) @@ -343,3 +559,15 @@ void VORLocalizer::networkManagerFinished(QNetworkReply *reply) reply->deleteLater(); } + +void VORLocalizer::handleChannelMessageQueue(MessageQueue* messageQueue) +{ + Message* message; + + while ((message = messageQueue->pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} diff --git a/plugins/feature/vorlocalizer/vorlocalizer.h b/plugins/feature/vorlocalizer/vorlocalizer.h index f3abb1809..dc3381684 100644 --- a/plugins/feature/vorlocalizer/vorlocalizer.h +++ b/plugins/feature/vorlocalizer/vorlocalizer.h @@ -23,6 +23,7 @@ #include "feature/feature.h" #include "util/message.h" +#include "util/average.h" #include "vorlocalizersettings.h" @@ -170,10 +171,30 @@ public: static const char* const m_featureId; private: + struct VORChannelReport + { + float m_radial; //!< current detected radial + float m_refMag; //!< current reference signal magnitude + float m_varMag; //!< current variable signal magnitude + AverageUtil m_radialAvg; + AverageUtil m_refMagAvg; + AverageUtil m_varMagAvg; + bool m_validRadial; + bool m_validRefMag; + bool m_validVarMag; + QString m_morseIdent; //!< identification morse code transcript + + VORChannelReport() = default; + VORChannelReport(const VORChannelReport&) = default; + VORChannelReport& operator=(const VORChannelReport&) = default; + }; + QThread m_thread; VorLocalizerWorker *m_worker; VORLocalizerSettings m_settings; - bool m_ptt; + QHash m_availableChannels; + QHash m_vorChannelReports; + QHash m_vorSinglePlans; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; @@ -181,10 +202,13 @@ private: void start(); void stop(); void applySettings(const VORLocalizerSettings& settings, bool force = false); + void updateChannels(); void webapiReverseSendSettings(QList& featureSettingsKeys, const VORLocalizerSettings& settings, bool force); private slots: void networkManagerFinished(QNetworkReply *reply); + void handleChannelMessageQueue(MessageQueue* messageQueue); + }; #endif // INCLUDE_FEATURE_VORLOCALIZER_H_ diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index d0bfebd4a..c572df644 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -333,8 +333,10 @@ static bool calcIntersectionPoint(float lat1, float lon1, float bearing1, float double cosLat1 = cos(lat1Rad); double cosLat2 = cos(lat2Rad); double delta12 = 2.0 * asin(sqrt(sindlat*sindlat+cosLat1*cosLat2*sindlon*sindlon)); - if (abs(delta12) < std::numeric_limits::epsilon()) + + if (abs(delta12) < std::numeric_limits::epsilon()) { return false; + } double sinLat1 = sin(lat1Rad); double sinLat2 = sin(lat2Rad); @@ -342,8 +344,8 @@ static bool calcIntersectionPoint(float lat1, float lon1, float bearing1, float double cosDelta12 = cos(delta12); double thetaA = acos((sinLat2-sinLat1*cosDelta12)/(sinDelta12*cosLat1)); double thetaB = acos((sinLat1-sinLat2*cosDelta12)/(sinDelta12*cosLat2)); - double theta12, theta21; + if (sin(lon2Rad-lon1Rad) > 0.0) { theta12 = thetaA; @@ -354,14 +356,19 @@ static bool calcIntersectionPoint(float lat1, float lon1, float bearing1, float theta12 = 2.0*M_PI-thetaA; theta21 = thetaB; } + double alpha1 = theta13 - theta12; double alpha2 = theta21 - theta23; double sinAlpha1 = sin(alpha1); double sinAlpha2 = sin(alpha2); - if ((sinAlpha1 == 0.0) && (sinAlpha2 == 0.0)) + + if ((sinAlpha1 == 0.0) && (sinAlpha2 == 0.0)) { return false; - if (sinAlpha1*sinAlpha2 < 0.0) + } + if (sinAlpha1*sinAlpha2 < 0.0) { return false; + } + double cosAlpha1 = cos(alpha1); double cosAlpha2 = cos(alpha2); double cosAlpha3 = -cosAlpha1*cosAlpha2+sinAlpha1*sinAlpha2*cos(delta12); @@ -382,7 +389,7 @@ VORGUI::VORGUI(NavAid *navAid, VORLocalizerGUI *gui) : // These are deleted by QTableWidget m_nameItem = new QTableWidgetItem(); m_frequencyItem = new QTableWidgetItem(); - m_offsetItem = new QTableWidgetItem(); + m_navIdItem = new QTableWidgetItem(); m_radialItem = new QTableWidgetItem(); m_identItem = new QTableWidgetItem(); m_morseItem = new QTableWidgetItem(); @@ -412,15 +419,18 @@ VORGUI::VORGUI(NavAid *navAid, VORLocalizerGUI *gui) : void VORGUI::on_audioMute_toggled(bool checked) { - m_gui->m_settings.m_subChannelSettings.value(m_navAid->m_id)->m_audioMute = checked; + m_gui->m_settings.m_subChannelSettings[m_navAid->m_id].m_audioMute = checked; m_gui->applySettings(); } QVariant VORModel::data(const QModelIndex &index, int role) const { int row = index.row(); - if ((row < 0) || (row >= m_vors.count())) + + if ((row < 0) || (row >= m_vors.count())) { return QVariant(); + } + if (role == VORModel::positionRole) { // Coordinates to display the VOR icon at @@ -436,15 +446,22 @@ QVariant VORModel::data(const QModelIndex &index, int role) const QStringList list; list.append(QString("Name: %1").arg(m_vors[row]->m_name)); list.append(QString("Frequency: %1 MHz").arg(m_vors[row]->m_frequencykHz / 1000.0f, 0, 'f', 1)); - if (m_vors[row]->m_channel != "") + + if (m_vors[row]->m_channel != "") { list.append(QString("Channel: %1").arg(m_vors[row]->m_channel)); + } + list.append(QString("Ident: %1 %2").arg(m_vors[row]->m_ident).arg(Morse::toSpacedUnicodeMorse(m_vors[row]->m_ident))); list.append(QString("Range: %1 nm").arg(m_vors[row]->m_range)); - if (m_vors[row]->m_alignedTrueNorth) + + if (m_vors[row]->m_alignedTrueNorth) { list.append(QString("Magnetic declination: Aligned to true North")); - else if (m_vors[row]->m_magneticDeclination != 0.0f) + } else if (m_vors[row]->m_magneticDeclination != 0.0f) { list.append(QString("Magnetic declination: %1%2").arg(std::round(m_vors[row]->m_magneticDeclination)).arg(QChar(0x00b0))); + } + QString data = list.join("\n"); + return QVariant::fromValue(data); } else if (role == VORModel::vorImageRole) @@ -455,10 +472,11 @@ QVariant VORModel::data(const QModelIndex &index, int role) const else if (role == VORModel::bubbleColourRole) { // Select a background colour for the text bubble next to the VOR - if (m_selected[row]) + if (m_selected[row]) { return QVariant::fromValue(QColor("lightgreen")); - else + } else { return QVariant::fromValue(QColor("lightblue")); + } } else if (role == VORModel::vorRadialRole) { @@ -471,10 +489,13 @@ QVariant VORModel::data(const QModelIndex &index, int role) const float endLat, endLong; float bearing; - if (m_gui->m_settings.m_magDecAdjust && !m_vors[row]->m_alignedTrueNorth) + + if (m_gui->m_settings.m_magDecAdjust && !m_vors[row]->m_alignedTrueNorth) { bearing = m_radials[row] - m_vors[row]->m_magneticDeclination; - else + } else { bearing = m_radials[row]; + } + calcRadialEndPoint(m_vors[row]->m_latitude, m_vors[row]->m_longitude, m_vors[row]->getRangeMetres(), bearing, endLat, endLong); list.push_back(QVariant::fromValue(*new QGeoCoordinate(endLat, endLong, Units::feetToMetres(m_vors[row]->m_elevation)))); @@ -484,36 +505,49 @@ QVariant VORModel::data(const QModelIndex &index, int role) const return QVariantList(); } else if (role == VORModel::selectedRole) + { return QVariant::fromValue(m_selected[row]); + } + return QVariant(); } bool VORModel::setData(const QModelIndex &index, const QVariant& value, int role) { int row = index.row(); - if ((row < 0) || (row >= m_vors.count())) + + if ((row < 0) || (row >= m_vors.count())) { return false; + } + if (role == VORModel::selectedRole) { bool selected = value.toBool(); VORGUI *vorGUI; - if (selected == true) + + if (selected) { vorGUI = new VORGUI(m_vors[row], m_gui); m_vorGUIs[row] = vorGUI; } else + { vorGUI = m_vorGUIs[row]; + } + m_gui->selectVOR(vorGUI, selected); m_selected[row] = selected; emit dataChanged(index, index); + if (!selected) { delete vorGUI; m_vorGUIs[row] = nullptr; } + return true; } + return true; } @@ -533,28 +567,33 @@ bool VORModel::findIntersection(float &lat, float &lon) { lat1 = m_vors[i]->m_latitude; lon1 = m_vors[i]->m_longitude; - if (m_gui->m_settings.m_magDecAdjust && !m_vors[i]->m_alignedTrueNorth) + + if (m_gui->m_settings.m_magDecAdjust && !m_vors[i]->m_alignedTrueNorth) { bearing1 = m_radials[i] - m_vors[i]->m_magneticDeclination; - else + } else { bearing1 = m_radials[i]; + } + valid1 = true; } else { lat2 = m_vors[i]->m_latitude; lon2 = m_vors[i]->m_longitude; - if (m_gui->m_settings.m_magDecAdjust && !m_vors[i]->m_alignedTrueNorth) + + if (m_gui->m_settings.m_magDecAdjust && !m_vors[i]->m_alignedTrueNorth) { bearing2 = m_radials[i] - m_vors[i]->m_magneticDeclination; - else + } else { bearing2 = m_radials[i]; + } + valid2 = true; break; } } } - if (valid1 && valid2) - { + if (valid1 && valid2) { return calcIntersectionPoint(lat1, lon1, bearing1, lat2, lon2, bearing2, lat, lon); } } @@ -571,7 +610,7 @@ void VORLocalizerGUI::resizeTable() ui->vorData->setRowCount(row + 1); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAME, new QTableWidgetItem("White Sulphur Springs")); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_FREQUENCY, new QTableWidgetItem("Freq (MHz) ")); - ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_OFFSET, new QTableWidgetItem("Offset (kHz) ")); + ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAVID, new QTableWidgetItem("99999999")); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_IDENT, new QTableWidgetItem("Ident ")); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_MORSE, new QTableWidgetItem(Morse::toSpacedUnicode(morse))); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_RADIAL, new QTableWidgetItem("Radial (o) ")); @@ -588,7 +627,6 @@ void VORLocalizerGUI::resizeTable() void VORLocalizerGUI::vorData_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) { (void) oldVisualIndex; - m_settings.m_columnIndexes[logicalIndex] = newVisualIndex; } @@ -596,7 +634,6 @@ void VORLocalizerGUI::vorData_sectionMoved(int logicalIndex, int oldVisualIndex, void VORLocalizerGUI::vorData_sectionResized(int logicalIndex, int oldSize, int newSize) { (void) oldSize; - m_settings.m_columnSizes[logicalIndex] = newSize; } @@ -610,9 +647,9 @@ void VORLocalizerGUI::columnSelectMenu(QPoint pos) void VORLocalizerGUI::columnSelectMenuChecked(bool checked) { (void) checked; - QAction* action = qobject_cast(sender()); - if (action != nullptr) + + if (action) { int idx = action->data().toInt(nullptr); ui->vorData->setColumnHidden(idx, !action->isChecked()); @@ -627,6 +664,7 @@ QAction *VORLocalizerGUI::createCheckableItem(QString &text, int idx, bool check action->setChecked(checked); action->setData(QVariant(idx)); connect(action, SIGNAL(triggered()), this, SLOT(columnSelectMenuChecked())); + return action; } @@ -646,7 +684,7 @@ void VORLocalizerGUI::selectVOR(VORGUI *vorGUI, bool selected) ui->vorData->setRowCount(row + 1); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAME, vorGUI->m_nameItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_FREQUENCY, vorGUI->m_frequencyItem); - ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_OFFSET, vorGUI->m_offsetItem); + ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAVID, vorGUI->m_navIdItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_IDENT, vorGUI->m_identItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_MORSE, vorGUI->m_morseItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_RADIAL, vorGUI->m_radialItem); @@ -662,11 +700,11 @@ void VORLocalizerGUI::selectVOR(VORGUI *vorGUI, bool selected) ui->vorData->setSortingEnabled(true); // Add to settings to create corresponding demodulator - VORLocalizerSubChannelSettings *subChannelSettings = new VORLocalizerSubChannelSettings(); - subChannelSettings->m_id = navId; - subChannelSettings->m_frequency = vorGUI->m_navAid->m_frequencykHz * 1000; - subChannelSettings->m_audioMute = false; - m_settings.m_subChannelSettings.insert(navId, subChannelSettings); + m_settings.m_subChannelSettings.insert(navId, VORLocalizerSubChannelSettings{ + navId, + vorGUI->m_navAid->m_frequencykHz * 1000, + false + }); applySettings(); } @@ -678,9 +716,7 @@ void VORLocalizerGUI::selectVOR(VORGUI *vorGUI, bool selected) m_selectedVORs.remove(navId); ui->vorData->removeRow(vorGUI->m_nameItem->row()); // Remove from settings to remove corresponding demodulator - VORLocalizerSubChannelSettings *subChannelSettings = m_settings.m_subChannelSettings.value(navId); m_settings.m_subChannelSettings.remove(navId); - delete subChannelSettings; applySettings(); } @@ -701,10 +737,10 @@ void VORLocalizerGUI::updateVORs() azEl.calculate(); // Only display VOR if in range - if (azEl.getDistance() <= 200000) - { + if (azEl.getDistance() <= 200000) { m_vorModel.addVOR(vor); } + ++i; } } @@ -734,11 +770,14 @@ QByteArray VORLocalizerGUI::serialize() const bool VORLocalizerGUI::deserialize(const QByteArray& data) { - if(m_settings.deserialize(data)) { + if (m_settings.deserialize(data)) + { displaySettings(); applySettings(true); return true; - } else { + } + else + { resetToDefaults(); return false; } @@ -775,50 +814,36 @@ bool VORLocalizerGUI::handleMessage(const Message& message) Real refMagDB = std::round(20.0*std::log10(report.getRefMag())); bool validRadial = report.getValidRadial(); - vorGUI->m_radialItem->setData(Qt::DisplayRole, std::round(report.getRadial())); - if (validRadial) + vorGUI->m_navIdItem->setData(Qt::DisplayRole, subChannelId); + + if (validRadial) { vorGUI->m_radialItem->setForeground(QBrush(Qt::white)); - else + } else { vorGUI->m_radialItem->setForeground(QBrush(Qt::red)); + } vorGUI->m_refMagItem->setData(Qt::DisplayRole, refMagDB); - if (report.getValidRefMag()) + + if (report.getValidRefMag()) { vorGUI->m_refMagItem->setForeground(QBrush(Qt::white)); - else + } else { vorGUI->m_refMagItem->setForeground(QBrush(Qt::red)); + } vorGUI->m_varMagItem->setData(Qt::DisplayRole, varMagDB); - if (report.getValidVarMag()) + + if (report.getValidVarMag()) { vorGUI->m_varMagItem->setForeground(QBrush(Qt::white)); - else + } else { vorGUI->m_varMagItem->setForeground(QBrush(Qt::red)); + } // Update radial on map m_vorModel.setRadial(subChannelId, validRadial, report.getRadial()); return true; } - else if (VORLocalizerReport::MsgReportFreqOffset::match(message)) - { - VORLocalizerReport::MsgReportFreqOffset& report = (VORLocalizerReport::MsgReportFreqOffset&) message; - int subChannelId = report.getSubChannelId(); - - VORGUI *vorGUI = m_selectedVORs.value(subChannelId); - - vorGUI->m_offsetItem->setData(Qt::DisplayRole, report.getFreqOffset() / 1000.0); - if (report.getOutOfBand()) - { - vorGUI->m_offsetItem->setForeground(QBrush(Qt::red)); - // Clear other fields as data is now invalid - vorGUI->m_radialItem->setText(""); - vorGUI->m_refMagItem->setText(""); - vorGUI->m_varMagItem->setText(""); - m_vorModel.setRadial(subChannelId, false, -1.0f); - } - else - vorGUI->m_offsetItem->setForeground(QBrush(Qt::white)); - } else if (VORLocalizerReport::MsgReportIdent::match(message)) { VORLocalizerReport::MsgReportIdent& report = (VORLocalizerReport::MsgReportIdent&) message; @@ -835,6 +860,7 @@ bool VORLocalizerGUI::handleMessage(const Message& message) { vorGUI->m_rxIdentItem->setText(identString); vorGUI->m_rxMorseItem->setText(Morse::toSpacedUnicode(ident)); + if (vorGUI->m_navAid->m_ident == identString) { // Set colour to green if matching expected ident @@ -868,11 +894,29 @@ bool VORLocalizerGUI::handleMessage(const Message& message) ui->channels->clear(); for (; it != channels.end(); ++it) { - ui->channels->addItem(tr("%1:%2").arg(it->m_deviceSetIndex).arg(it->m_channelIndex)); + ui->channels->addItem(tr("R%1:%2").arg(it->m_deviceSetIndex).arg(it->m_channelIndex)); } return true; } + else if (VORLocalizerReport::MsgReportServiceddVORs::match(message)) + { + VORLocalizerReport::MsgReportServiceddVORs& report = (VORLocalizerReport::MsgReportServiceddVORs&) message; + std::vector& servicedVORNavIds = report.getNavIds(); + + for (auto vorGUI : m_selectedVORs) { + vorGUI->m_frequencyItem->setForeground(QBrush(Qt::white)); + } + + for (auto navId : servicedVORNavIds) + { + if (m_selectedVORs.contains(navId)) + { + VORGUI *vorGUI = m_selectedVORs[navId]; + vorGUI->m_frequencyItem->setForeground(QBrush(Qt::green)); + } + } + } return false; } @@ -883,8 +927,7 @@ void VORLocalizerGUI::handleInputMessages() while ((message = getInputMessageQueue()->pop()) != 0) { - if (handleMessage(*message)) - { + if (handleMessage(*message)) { delete message; } } @@ -893,31 +936,41 @@ void VORLocalizerGUI::handleInputMessages() qint64 VORLocalizerGUI::fileAgeInDays(QString filename) { QFile file(filename); + if (file.exists()) { QDateTime modified = file.fileTime(QFileDevice::FileModificationTime); - if (modified.isValid()) + + if (modified.isValid()) { return modified.daysTo(QDateTime::currentDateTime()); - else + } else { return -1; + } } + return -1; } bool VORLocalizerGUI::confirmDownload(QString filename) { qint64 age = fileAgeInDays(filename); + if ((age == -1) || (age > 100)) + { return true; + } else { QMessageBox::StandardButton reply; - if (age == 0) + + if (age == 0) { reply = QMessageBox::question(this, "Confirm download", "This file was last downloaded today. Are you sure you wish to redownload it?", QMessageBox::Yes|QMessageBox::No); - else if (age == 1) + } else if (age == 1) { reply = QMessageBox::question(this, "Confirm download", "This file was last downloaded yesterday. Are you sure you wish to redownload it?", QMessageBox::Yes|QMessageBox::No); - else + } else { reply = QMessageBox::question(this, "Confirm download", QString("This file was last downloaded %1 days ago. Are you sure you wish to redownload this file?").arg(age), QMessageBox::Yes|QMessageBox::No); + } + return reply == QMessageBox::Yes; } } @@ -932,18 +985,20 @@ QString VORLocalizerGUI::getDataDir() QString VORLocalizerGUI::getOpenAIPVORDBFilename(int i) { - if (countryCodes[i] != nullptr) + if (countryCodes[i] != nullptr) { return getDataDir() + "/" + countryCodes[i] + "_nav.aip"; - else + } else { return ""; + } } QString VORLocalizerGUI::getOpenAIPVORDBURL(int i) { - if (countryCodes[i] != nullptr) + if (countryCodes[i] != nullptr) { return QString(OPENAIP_NAVAIDS_URL).arg(countryCodes[i]); - else + } else { return ""; + } } QString VORLocalizerGUI::getVORDBFilename() @@ -964,14 +1019,18 @@ void VORLocalizerGUI::downloadFinished(const QString& filename, bool success) if (filename == getVORDBFilename()) { m_vors = NavAid::readNavAidsDB(filename); - if (m_vors != nullptr) + + if (m_vors != nullptr) { updateVORs(); + } + m_progressDialog->close(); m_progressDialog = nullptr; } else if (filename == getOpenAIPVORDBFilename(m_countryIndex)) { m_countryIndex++; + if (countryCodes[m_countryIndex] != nullptr) { QString vorDBFile = getOpenAIPVORDBFilename(m_countryIndex); @@ -984,8 +1043,11 @@ void VORLocalizerGUI::downloadFinished(const QString& filename, bool success) else { readNavAids(); - if (m_vors != nullptr) + + if (m_vors) { updateVORs(); + } + m_progressDialog->close(); m_progressDialog = nullptr; } @@ -1021,6 +1083,7 @@ void VORLocalizerGUI::on_getOurAirportsVORDB_clicked() if (m_progressDialog == nullptr) { QString vorDBFile = getVORDBFilename(); + if (confirmDownload(vorDBFile)) { // Download OurAirports navaid database to disk @@ -1039,10 +1102,11 @@ void VORLocalizerGUI::on_getOurAirportsVORDB_clicked() void VORLocalizerGUI::on_getOpenAIPVORDB_clicked() { // Don't try to download while already in progress - if (m_progressDialog == nullptr) + if (!m_progressDialog) { m_countryIndex = 0; QString vorDBFile = getOpenAIPVORDBFilename(m_countryIndex); + if (confirmDownload(vorDBFile)) { // Download OpenAIP XML to disk @@ -1071,13 +1135,27 @@ void VORLocalizerGUI::readNavAids() } } -void VORLocalizerGUI::on_magDecAdjust_clicked(bool checked) +void VORLocalizerGUI::on_magDecAdjust_toggled(bool checked) { m_settings.m_magDecAdjust = checked; m_vorModel.allVORUpdated(); applySettings(); } +void VORLocalizerGUI::on_rrTime_valueChanged(int value) +{ + m_settings.m_rrTime = value; + ui->rrTimeText->setText(tr("%1s").arg(m_settings.m_rrTime)); + applySettings(); +} + +void VORLocalizerGUI::on_centerShift_valueChanged(int value) +{ + m_settings.m_centerShift = value * 1000; + ui->centerShiftText->setText(tr("%1k").arg(value)); + applySettings(); +} + void VORLocalizerGUI::on_channelsRefresh_clicked() { if (m_doApplySettings) @@ -1170,6 +1248,7 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe // Centre map at My Position QQuickItem *item = ui->map->rootObject(); QObject *object = item->findChild("map"); + if (object) { QGeoCoordinate coords = object->property("center").value(); @@ -1177,8 +1256,10 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe coords.setLongitude(stationLongitude); object->setProperty("center", QVariant::fromValue(coords)); } + // Move antenna icon to My Position to start with QObject *stationObject = item->findChild("station"); + if (stationObject) { QGeoCoordinate coords = stationObject->property("coordinate").value(); @@ -1191,6 +1272,7 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe // Read in VOR information if it exists bool useOurAirports = false; + if (useOurAirports) { m_vors = NavAid::readNavAidsDB(getVORDBFilename()); @@ -1201,7 +1283,8 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe readNavAids(); ui->getOurAirportsVORDB->setVisible(false); } - if (m_vors != nullptr) { + + if (m_vors) { updateVORs(); } @@ -1213,11 +1296,13 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe ui->vorData->setSortingEnabled(true); // Add context menu to allow hiding/showing of columns menu = new QMenu(ui->vorData); + for (int i = 0; i < ui->vorData->horizontalHeader()->count(); i++) { QString text = ui->vorData->horizontalHeaderItem(i)->text(); menu->addAction(createCheckableItem(text, i, true)); } + ui->vorData->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->vorData->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(columnSelectMenu(QPoint))); // Get signals when columns change @@ -1273,6 +1358,11 @@ void VORLocalizerGUI::displaySettings() header->moveSection(header->visualIndex(i), m_settings.m_columnIndexes[i]); } + ui->rrTimeText->setText(tr("%1s").arg(m_settings.m_rrTime)); + ui->rrTime->setValue(m_settings.m_rrTime); + ui->centerShiftText->setText(tr("%1k").arg(m_settings.m_centerShift/1000)); + ui->centerShift->setValue(m_settings.m_centerShift/1000); + blockApplySettings(false); } @@ -1325,6 +1415,7 @@ void VORLocalizerGUI::tick() // Move antenna icon to estimated position QQuickItem *item = ui->map->rootObject(); QObject *stationObject = item->findChild("station"); + if(stationObject != NULL) { QGeoCoordinate coords = stationObject->property("coordinate").value(); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h index 68af9396c..eeea8e5b4 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.h +++ b/plugins/feature/vorlocalizer/vorlocalizergui.h @@ -62,7 +62,7 @@ public: QTableWidgetItem *m_nameItem; QTableWidgetItem *m_frequencyItem; - QTableWidgetItem *m_offsetItem; + QTableWidgetItem *m_navIdItem; QTableWidgetItem *m_identItem; QTableWidgetItem *m_morseItem; QTableWidgetItem *m_radialItem; @@ -269,7 +269,9 @@ private slots: void on_startStop_toggled(bool checked); void on_getOurAirportsVORDB_clicked(); void on_getOpenAIPVORDB_clicked(); - void on_magDecAdjust_clicked(bool checked); + void on_magDecAdjust_toggled(bool checked); + void on_rrTime_valueChanged(int value); + void on_centerShift_valueChanged(int value); void on_channelsRefresh_clicked(); void vorData_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); void vorData_sectionResized(int logicalIndex, int oldSize, int newSize); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.ui b/plugins/feature/vorlocalizer/vorlocalizergui.ui index 978e91a6a..e22dd69a8 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.ui +++ b/plugins/feature/vorlocalizer/vorlocalizergui.ui @@ -120,7 +120,7 @@ - + Draw radials adjusted for magnetic declination @@ -139,6 +139,124 @@ + + + + RR + + + + + + + + 24 + 24 + + + + Round robin turn time (s) + + + 10 + + + 60 + + + 5 + + + 5 + + + 20 + + + + + + + + 30 + 0 + + + + + 30 + 16777215 + + + + Sound volume (%) + + + 20s + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + Sh + + + + + + + + 24 + 24 + + + + Center frequency shift (kHz) + + + -40 + + + 40 + + + 1 + + + 1 + + + 0 + + + + + + + + 30 + 0 + + + + 20k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -159,7 +277,7 @@ - VORs + Chan @@ -264,7 +382,7 @@ - Offset (kHz) + Nav Id Offset of the VOR's frequency from the current center frequency. Red indicates out of range. diff --git a/plugins/feature/vorlocalizer/vorlocalizerreport.cpp b/plugins/feature/vorlocalizer/vorlocalizerreport.cpp index d2fd7572e..23ceb00aa 100644 --- a/plugins/feature/vorlocalizer/vorlocalizerreport.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizerreport.cpp @@ -17,7 +17,7 @@ #include "vorlocalizerreport.h" -MESSAGE_CLASS_DEFINITION(VORLocalizerReport::MsgReportFreqOffset, Message) MESSAGE_CLASS_DEFINITION(VORLocalizerReport::MsgReportRadial, Message) MESSAGE_CLASS_DEFINITION(VORLocalizerReport::MsgReportIdent, Message) MESSAGE_CLASS_DEFINITION(VORLocalizerReport::MsgReportChannels, Message) +MESSAGE_CLASS_DEFINITION(VORLocalizerReport::MsgReportServiceddVORs, Message) diff --git a/plugins/feature/vorlocalizer/vorlocalizerreport.h b/plugins/feature/vorlocalizer/vorlocalizerreport.h index d9abe4d5e..e95254b33 100644 --- a/plugins/feature/vorlocalizer/vorlocalizerreport.h +++ b/plugins/feature/vorlocalizer/vorlocalizerreport.h @@ -20,6 +20,7 @@ #define INCLUDE_VORLOCALIZERREPORT_H #include +#include #include "util/message.h" @@ -90,33 +91,6 @@ public: } }; - class MsgReportFreqOffset : public Message { - MESSAGE_CLASS_DECLARATION - - public: - int getSubChannelId() const { return m_subChannelId; } - int getFreqOffset() const { return m_freqOffset; } - bool getOutOfBand() const { return m_outOfBand; } - - static MsgReportFreqOffset* create(int subChannelId, int freqOffset, bool outOfBand) - { - return new MsgReportFreqOffset(subChannelId, freqOffset, outOfBand); - } - - private: - int m_subChannelId; - int m_freqOffset; - bool m_outOfBand; - - MsgReportFreqOffset(int subChannelId, int freqOffset, bool outOfBand) : - Message(), - m_subChannelId(subChannelId), - m_freqOffset(freqOffset), - m_outOfBand(outOfBand) - { - } - }; - class MsgReportIdent : public Message { MESSAGE_CLASS_DECLARATION @@ -164,6 +138,26 @@ public: {} }; + class MsgReportServiceddVORs : public Message { + MESSAGE_CLASS_DECLARATION + + public: + std::vector& getNavIds() { return m_navIds; } + QHash& getSinglePlans() { return m_singlePlans; } + + static MsgReportServiceddVORs* create() { + return new MsgReportServiceddVORs(); + } + + private: + std::vector m_navIds; + QHash m_singlePlans; + + MsgReportServiceddVORs() : + Message() + {} + }; + public: VORLocalizerReport() {} ~VORLocalizerReport() {} diff --git a/plugins/feature/vorlocalizer/vorlocalizersettings.cpp b/plugins/feature/vorlocalizer/vorlocalizersettings.cpp index 5664a4f04..a39c52785 100644 --- a/plugins/feature/vorlocalizer/vorlocalizersettings.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizersettings.cpp @@ -33,6 +33,8 @@ void VORLocalizerSettings::resetToDefaults() m_rgbColor = QColor(255, 255, 0).rgb(); m_title = "VOR Localizer"; m_magDecAdjust = true; + m_rrTime = 20; + m_centerShift = 20000; m_useReverseAPI = false; m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; @@ -54,6 +56,8 @@ QByteArray VORLocalizerSettings::serialize() const s.writeU32(7, m_rgbColor); s.writeString(9, m_title); s.writeBool(10, m_magDecAdjust); + s.writeS32(11, m_rrTime); + s.writeS32(12, m_centerShift); s.writeBool(14, m_useReverseAPI); s.writeString(15, m_reverseAPIAddress); s.writeU32(16, m_reverseAPIPort); @@ -91,6 +95,8 @@ bool VORLocalizerSettings::deserialize(const QByteArray& data) d.readU32(7, &m_rgbColor); d.readString(9, &m_title, "VOR Localizer"); d.readBool(10, &m_magDecAdjust, true); + d.readS32(11, &m_rrTime, 20); + d.readS32(12, &m_centerShift, 20000); d.readBool(14, &m_useReverseAPI, false); d.readString(15, &m_reverseAPIAddress, "127.0.0.1"); d.readU32(16, &utmp, 0); @@ -123,4 +129,14 @@ bool VORLocalizerSettings::deserialize(const QByteArray& data) } } +bool VORLocalizerSettings::VORChannel::operator<(const VORChannel& other) const +{ + if (m_frequency != other.m_frequency) { + return m_frequency < other.m_frequency; + } + if (m_subChannelId != other.m_subChannelId) { + return m_subChannelId < other.m_subChannelId; + } + return false; +} diff --git a/plugins/feature/vorlocalizer/vorlocalizersettings.h b/plugins/feature/vorlocalizer/vorlocalizersettings.h index f08f6bf2a..0557374c9 100644 --- a/plugins/feature/vorlocalizer/vorlocalizersettings.h +++ b/plugins/feature/vorlocalizer/vorlocalizersettings.h @@ -23,6 +23,7 @@ #include class Serializable; +class ChannelAPI; // Number of columns in the table @@ -34,15 +35,38 @@ struct VORLocalizerSubChannelSettings { struct VORLocalizerSettings { - struct VORDemodChannels + struct VORChannel + { + int m_subChannelId; //!< Unique VOR identifier (from database) + int m_frequency; //!< Frequency the VOR is on + bool m_audioMute; //!< Mute the audio from this VOR + + VORChannel() = default; + VORChannel(const VORChannel&) = default; + VORChannel& operator=(const VORChannel&) = default; + + bool operator<(const VORChannel& other) const; + }; + + struct AvailableChannel { int m_deviceSetIndex; int m_channelIndex; + ChannelAPI *m_channelAPI; + quint64 m_deviceCenterFrequency; + int m_basebandSampleRate; + int m_navId; + + AvailableChannel() = default; + AvailableChannel(const AvailableChannel&) = default; + AvailableChannel& operator=(const AvailableChannel&) = default; }; quint32 m_rgbColor; QString m_title; bool m_magDecAdjust; //!< Adjust for magnetic declination when drawing radials on the map + int m_rrTime; //!< Round robin turn time in seconds + int m_centerShift; //!< Center frequency shift to apply to move away from DC bool m_useReverseAPI; QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; @@ -53,7 +77,7 @@ struct VORLocalizerSettings static const int VORDEMOD_COLUMNS = 11; static const int VOR_COL_NAME = 0; static const int VOR_COL_FREQUENCY = 1; - static const int VOR_COL_OFFSET = 2; + static const int VOR_COL_NAVID = 2; static const int VOR_COL_IDENT = 3; static const int VOR_COL_MORSE = 4; static const int VOR_COL_RX_IDENT = 5; @@ -66,7 +90,7 @@ struct VORLocalizerSettings int m_columnIndexes[VORDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[VORDEMOD_COLUMNS]; //!< Size of the coumns in the table - QHash m_subChannelSettings; + QHash m_subChannelSettings; VORLocalizerSettings(); void resetToDefaults(); diff --git a/plugins/feature/vorlocalizer/vorlocalizerworker.cpp b/plugins/feature/vorlocalizer/vorlocalizerworker.cpp index adc2bf9e0..2cb7ac6e2 100644 --- a/plugins/feature/vorlocalizer/vorlocalizerworker.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizerworker.cpp @@ -18,12 +18,15 @@ #include #include "SWGDeviceState.h" +#include "SWGDeviceSettings.h" +#include "SWGChannelSettings.h" #include "SWGSuccessResponse.h" #include "SWGErrorResponse.h" -#include "webapi/webapiadapterinterface.h" #include "device/deviceset.h" #include "channel/channelapi.h" +#include "webapi/webapiadapterinterface.h" +#include "webapi/webapiutils.h" #include "maincore.h" #include "vorlocalizerreport.h" @@ -36,7 +39,8 @@ class DSPDeviceSourceEngine; VorLocalizerWorker::VorLocalizerWorker(WebAPIAdapterInterface *webAPIAdapterInterface) : m_webAPIAdapterInterface(webAPIAdapterInterface), - m_msgQueueToGUI(nullptr), + m_msgQueueToFeature(nullptr), + m_availableChannels(nullptr), m_running(false), m_mutex(QMutex::Recursive) { @@ -59,6 +63,8 @@ bool VorLocalizerWorker::startWork() { QMutexLocker mutexLocker(&m_mutex); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + connect(&m_rrTimer, SIGNAL(timeout()), this, SLOT(rrNextTurn())); + m_rrTimer.start(m_settings.m_rrTime * 1000); m_running = true; return m_running; } @@ -66,6 +72,8 @@ bool VorLocalizerWorker::startWork() void VorLocalizerWorker::stopWork() { QMutexLocker mutexLocker(&m_mutex); + m_rrTimer.stop(); + disconnect(&m_rrTimer, SIGNAL(timeout()), this, SLOT(rrNextTurn())); disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); m_running = false; } @@ -98,6 +106,7 @@ bool VorLocalizerWorker::handleMessage(const Message& cmd) { qDebug() << "VorLocalizerWorker::handleMessage: MsgRefreshChannels"; updateChannels(); + return true; } else @@ -124,28 +133,53 @@ void VorLocalizerWorker::applySettings(const VORLocalizerSettings& settings, boo } // Add new sub channels - QHash::const_iterator itr = settings.m_subChannelSettings.begin(); + QHash::const_iterator itr = settings.m_subChannelSettings.begin(); + while (itr != settings.m_subChannelSettings.end()) { - VORLocalizerSubChannelSettings *subChannelSettings = itr.value(); + const VORLocalizerSubChannelSettings& subChannelSettings = itr.value(); + qDebug() << "VorLocalizerWorker::applySettings: subchannel " << subChannelSettings.m_id; int j = 0; for (; j < m_vorChannels.size(); j++) { - if (subChannelSettings->m_id == m_vorChannels[j].m_subChannelId) + if (subChannelSettings.m_id == m_vorChannels[j].m_subChannelId) + { + qDebug() << "VorLocalizerWorker::applySettings: subchannel " + << subChannelSettings.m_id + << "already present"; break; + } } if (j == m_vorChannels.size()) { // Add a sub-channel sink - qDebug() << "VorLocalizerWorker::applySettings: Adding sink " << subChannelSettings->m_id; + qDebug() << "VorLocalizerWorker::applySettings: Adding subchannel " << subChannelSettings.m_id; addVORChannel(subChannelSettings); } ++itr; } + for (auto subChannelSetting : settings.m_subChannelSettings) + { + int navId = subChannelSetting.m_id; + + if (m_settings.m_subChannelSettings.contains(navId)) + { + if (subChannelSetting.m_audioMute != m_settings.m_subChannelSettings[navId].m_audioMute) + { + qDebug() << "VorLocalizerWorker::applySettings: audioMute:" << subChannelSetting.m_audioMute; + setAudioMute(navId, subChannelSetting.m_audioMute); + } + } + } + + if ((settings.m_rrTime != m_settings.m_rrTime) || force) { + m_rrTimer.start(settings.m_rrTime * 1000); + } + m_settings = settings; } @@ -159,6 +193,8 @@ void VorLocalizerWorker::updateHardware() void VorLocalizerWorker::removeVORChannel(int navId) { + qDebug("VorLocalizerWorker::removeVORChannel: %d", navId); + for (int i = 0; i < m_vorChannels.size(); i++) { if (m_vorChannels[i].m_subChannelId == navId) @@ -167,57 +203,538 @@ void VorLocalizerWorker::removeVORChannel(int navId) break; } } + + updateChannels(); } -void VorLocalizerWorker::addVORChannel(const VORLocalizerSubChannelSettings *subChannelSettings) +void VorLocalizerWorker::addVORChannel(const VORLocalizerSubChannelSettings& subChannelSettings) { - VORChannel vorChannel = VORChannel{subChannelSettings->m_id, subChannelSettings->m_frequency, subChannelSettings->m_audioMute}; + qDebug("VorLocalizerWorker::addVORChannel: %d at %d Hz", + subChannelSettings.m_id, subChannelSettings.m_frequency); + + VORLocalizerSettings::VORChannel vorChannel = + VORLocalizerSettings::VORChannel{ + subChannelSettings.m_id, + subChannelSettings.m_frequency, + subChannelSettings.m_audioMute + }; m_vorChannels.push_back(vorChannel); + updateChannels(); } void VorLocalizerWorker::updateChannels() { - MainCore *mainCore = MainCore::instance(); - std::vector& deviceSets = mainCore->getDeviceSets(); - std::vector::const_iterator it = deviceSets.begin(); - m_availableChannels.clear(); + qDebug() << "VorLocalizerWorker::updateChannels: " + << "#VORs:" << m_vorChannels.size() + << "#Chans:" << m_availableChannels->size(); - int deviceIndex = 0; + if ((m_vorChannels.size() == 0) || (m_availableChannels->size() == 0)) { + return; + } - for (; it != deviceSets.end(); ++it, deviceIndex++) + QMutexLocker mlock(&m_mutex); + std::sort(m_vorChannels.begin(), m_vorChannels.end()); + std::vector devicesChannels; + getChannelsByDevice(m_availableChannels, devicesChannels); + QList unallocatedVORs(m_vorChannels); + m_rrPlans.clear(); + int deviceCount = 0; + + for (auto deviceChannel : devicesChannels) { - DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine; + unsigned int nbChannels = unallocatedVORs.size() < (int) deviceChannel.m_channels.size() ? + unallocatedVORs.size() : + deviceChannel.m_channels.size(); + std::vector vorRanges; - if (deviceSourceEngine) + while (nbChannels != 0) { - for (int chi = 0; chi < (*it)->getNumberOfChannels(); chi++) - { - ChannelAPI *channel = (*it)->getChannelAt(chi); + getVORRanges(unallocatedVORs, nbChannels, vorRanges); + filterVORRanges(vorRanges, deviceChannel.m_bandwidth); - if (channel->getURI() == "sdrangel.channel.vordemodsc") + if (vorRanges.size() != 0) { + break; + } + + nbChannels--; + } + + std::vector> vorLists; + + for (auto vorRange : vorRanges) + { + QList vorList; + + for (auto index : vorRange.m_vorIndices) { + vorList.append(VORLocalizerSettings::VORChannel(unallocatedVORs[index])); + } + + vorLists.push_back(vorList); + } + + // make one round robin turn for each VOR list for this device + std::vector rrDevicePlans; + + for (auto vorList : vorLists) + { + RRTurnPlan turnPlan(deviceChannel); + int fMin = vorList.front().m_frequency; + int fMax = vorList.back().m_frequency; + int devFreq = (fMin + fMax) / 2; + turnPlan.m_device.m_frequency = devFreq; + int iCh = 0; + + // qDebug() << "RR build plan " + // << "device:" << turnPlan.m_device.m_deviceIndex + // << "freq:" << turnPlan.m_device.m_frequency; + + for (auto vorChannel : vorList) + { + RRChannel& channel = turnPlan.m_channels[iCh]; + channel.m_frequencyShift = vorChannel.m_frequency - devFreq; + channel.m_navId = vorChannel.m_subChannelId; + // qDebug() << "VOR channel" << vorChannel.m_subChannelId + // << "freq:" << vorChannel.m_frequency + // << "channel:" << channel.m_channelIndex + // << "shift:" << channel.m_frequencyShift; + // remove VOR from the unallocated list + QList::iterator it = unallocatedVORs.begin(); + while (it != unallocatedVORs.end()) { - AvailableChannel availableChannel = AvailableChannel{deviceIndex, chi, channel}; - m_availableChannels.push_back(availableChannel); + if (it->m_subChannelId == vorChannel.m_subChannelId) { + it = unallocatedVORs.erase(it); + } else { + ++it; + } } + + iCh++; + } + + rrDevicePlans.push_back(turnPlan); + } + + m_rrPlans.push_back(rrDevicePlans); + deviceCount++; + } + + qDebug() << "VorLocalizerWorker::updateChannels: unallocatedVORs size:" << unallocatedVORs.size(); + + // Fallback for unallocated VORs: add single channel plans for all unallocated VORs + if ((unallocatedVORs.size() != 0) && (devicesChannels.size() != 0) && m_rrPlans.size() != 0) + { + VorLocalizerWorker::RRTurnPlan& deviceChannel = devicesChannels.front(); + std::vector vorRanges; + getVORRanges(unallocatedVORs, 1, vorRanges); + std::vector& rrPlan = m_rrPlans.front(); + std::vector> vorLists; + + for (auto vorRange : vorRanges) + { + QList vorList; + + for (auto index : vorRange.m_vorIndices) { + vorList.append(VORLocalizerSettings::VORChannel(unallocatedVORs[index])); + } + + vorLists.push_back(vorList); + } + + for (auto vorList : vorLists) + { + RRTurnPlan turnPlan(deviceChannel); + int fMin = vorList.front().m_frequency; + int fMax = vorList.back().m_frequency; + int devFreq = (fMin + fMax) / 2; + turnPlan.m_device.m_frequency = devFreq; + int iCh = 0; + + // qDebug() << "RR build plan " + // << "device:" << turnPlan.m_device.m_deviceIndex + // << "freq:" << turnPlan.m_device.m_frequency; + + for (auto vorChannel : vorList) + { + RRChannel& channel = turnPlan.m_channels[iCh]; + channel.m_frequencyShift = vorChannel.m_frequency - devFreq; + channel.m_navId = vorChannel.m_subChannelId; + // qDebug() << "VOR channel" << vorChannel.m_subChannelId + // << "freq:" << vorChannel.m_frequency + // << "channel:" << channel.m_channelIndex + // << "shift:" << channel.m_frequencyShift; + iCh++; + } + + rrPlan.push_back(turnPlan); + } + } + + for (auto rrPlans : m_rrPlans) + { + qDebug() << "VorLocalizerWorker::updateChannels: RR plans for one device"; + + for (auto rrPlan : rrPlans) + { + qDebug() << "VorLocalizerWorker::updateChannels: RR plan: " + << "device:" << rrPlan.m_device.m_deviceIndex + << "frequency:" << rrPlan.m_device.m_frequency; + + for (auto rrChannel : rrPlan.m_channels) + { + qDebug() << "VorLocalizerWorker::updateChannels: RR channel: " + << "channel:" << rrChannel.m_channelAPI + << "index:" << rrChannel.m_channelIndex + << "shift:" << rrChannel.m_frequencyShift + << "navId:" << rrChannel.m_navId; } } } - if (m_msgQueueToGUI) + m_rrTurnCounters.resize(deviceCount); + std::fill(m_rrTurnCounters.begin(), m_rrTurnCounters.end(), 0); + rrNextTurn(); +} + +void VorLocalizerWorker::allocateChannel(ChannelAPI *channel, int vorFrequency, int vorNavId, int channelShift) +{ + VORLocalizerSettings::AvailableChannel& availableChannel = m_availableChannels->operator[](channel); + qDebug() << "VorLocalizerWorker::allocateChannel:" + << " vorNavId:" << vorNavId + << " vorFrequency:" << vorFrequency + << " channelShift:" << channelShift + << " deviceIndex:" << availableChannel.m_deviceSetIndex + << " channelIndex:" << availableChannel.m_channelIndex; + double deviceFrequency = vorFrequency - channelShift; + setDeviceFrequency(availableChannel.m_deviceSetIndex, deviceFrequency); + setChannelShift(availableChannel.m_deviceSetIndex, availableChannel.m_channelIndex, channelShift, vorNavId); + availableChannel.m_navId = vorNavId; +} + +void VorLocalizerWorker::setDeviceFrequency(int deviceIndex, double targetFrequency) +{ + SWGSDRangel::SWGDeviceSettings deviceSettingsResponse; + SWGSDRangel::SWGErrorResponse errorResponse; + int httpRC; + + // Get current device center frequency + httpRC = m_webAPIAdapterInterface->devicesetDeviceSettingsGet( + deviceIndex, + deviceSettingsResponse, + errorResponse + ); + + if (httpRC/100 != 2) { - VORLocalizerReport::MsgReportChannels *msg = VORLocalizerReport::MsgReportChannels::create(); - std::vector& msgChannels = msg->getChannels(); + qWarning("VorLocalizerWorker::setDeviceFrequency: get device frequency error %d: %s", + httpRC, qPrintable(*errorResponse.getMessage())); + } - for (int i = 0; i < m_availableChannels.size(); i++) - { - VORLocalizerReport::MsgReportChannels::Channel msgChannel = - VORLocalizerReport::MsgReportChannels::Channel{ - m_availableChannels[i].m_deviceSetIndex, - m_availableChannels[i].m_channelIndex - }; - msgChannels.push_back(msgChannel); - } + QJsonObject *jsonObj = deviceSettingsResponse.asJsonObject(); - m_msgQueueToGUI->push(msg); + // Update centerFrequency + WebAPIUtils::setSubObjectDouble(*jsonObj, "centerFrequency", targetFrequency); + QStringList deviceSettingsKeys; + deviceSettingsKeys.append("centerFrequency"); + deviceSettingsResponse.init(); + deviceSettingsResponse.fromJsonObject(*jsonObj); + SWGSDRangel::SWGErrorResponse errorResponse2; + + httpRC = m_webAPIAdapterInterface->devicesetDeviceSettingsPutPatch( + deviceIndex, + false, // PATCH + deviceSettingsKeys, + deviceSettingsResponse, + errorResponse2 + ); + + if (httpRC/100 == 2) + { + qDebug("VorLocalizerWorker::setDeviceFrequency: set device frequency %f OK", targetFrequency); + } + else + { + qWarning("VorLocalizerWorker::setDeviceFrequency: set device frequency error %d: %s", + httpRC, qPrintable(*errorResponse2.getMessage())); + } +} + +void VorLocalizerWorker::setChannelShift(int deviceIndex, int channelIndex, double targetOffset, int vorNavId) +{ + SWGSDRangel::SWGChannelSettings channelSettingsResponse; + SWGSDRangel::SWGErrorResponse errorResponse; + int httpRC; + + // Get channel settings containg inputFrequencyOffset, so we can patch them + httpRC = m_webAPIAdapterInterface->devicesetChannelSettingsGet( + deviceIndex, + channelIndex, + channelSettingsResponse, + errorResponse + ); + + if (httpRC/100 != 2) + { + qWarning("VorLocalizerWorker::setChannelShift: get channel offset frequency error %d: %s", + httpRC, qPrintable(*errorResponse.getMessage())); + } + + QJsonObject *jsonObj = channelSettingsResponse.asJsonObject(); + + if (!WebAPIUtils::setSubObjectDouble(*jsonObj, "inputFrequencyOffset", targetOffset)) + { + qWarning("VorLocalizerWorker::setChannelShift: No inputFrequencyOffset key in channel settings"); + return; + } + + if (!WebAPIUtils::setSubObjectInt(*jsonObj, "navId", vorNavId)) + { + qWarning("VorLocalizerWorker::setChannelShift: No navId key in channel settings"); + return; + } + + QStringList channelSettingsKeys; + + if (m_settings.m_subChannelSettings.contains(vorNavId)) + { + if (!WebAPIUtils::setSubObjectInt(*jsonObj, "audioMute", m_settings.m_subChannelSettings[vorNavId].m_audioMute ? 1 : 0)) { + qWarning("VorLocalizerWorker::setChannelShift: No audioMute key in channel settings"); + } else { + channelSettingsKeys.append("audioMute"); + } + } + + channelSettingsKeys.append("inputFrequencyOffset"); + channelSettingsKeys.append("navId"); + channelSettingsResponse.init(); + channelSettingsResponse.fromJsonObject(*jsonObj); + + httpRC = m_webAPIAdapterInterface->devicesetChannelSettingsPutPatch( + deviceIndex, + channelIndex, + false, // PATCH + channelSettingsKeys, + channelSettingsResponse, + errorResponse + ); + + if (httpRC/100 == 2) + { + qDebug("VorLocalizerWorker::setChannelShift: inputFrequencyOffset: %f navId: %d OK", targetOffset, vorNavId); + } + else + { + qWarning("VorLocalizerWorker::setChannelShift: set inputFrequencyOffset and navId error %d: %s", + httpRC, qPrintable(*errorResponse.getMessage())); + } +} + +void VorLocalizerWorker::setAudioMute(int vorNavId, bool audioMute) +{ + QMutexLocker mlock(&m_mutex); + + if (!m_channelAllocations.contains(vorNavId)) { + return; + } + + SWGSDRangel::SWGChannelSettings channelSettingsResponse; + SWGSDRangel::SWGErrorResponse errorResponse; + int httpRC; + int deviceIndex = m_channelAllocations[vorNavId].m_deviceIndex; + int channelIndex = m_channelAllocations[vorNavId].m_channelIndex; + + // Get channel settings containg inputFrequencyOffset, so we can patch them + httpRC = m_webAPIAdapterInterface->devicesetChannelSettingsGet( + deviceIndex, + channelIndex, + channelSettingsResponse, + errorResponse + ); + + if (httpRC/100 != 2) + { + qWarning("VorLocalizerWorker::setChannelShift: get channel offset frequency error %d: %s", + httpRC, qPrintable(*errorResponse.getMessage())); + } + + QJsonObject *jsonObj = channelSettingsResponse.asJsonObject(); + + if (!WebAPIUtils::setSubObjectInt(*jsonObj, "audioMute", audioMute ? 1 : 0)) + { + qWarning("VorLocalizerWorker::setAudioMute: No audioMute key in channel settings"); + return; + } + + QStringList channelSettingsKeys; + channelSettingsKeys.append("audioMute"); + channelSettingsResponse.init(); + channelSettingsResponse.fromJsonObject(*jsonObj); + + httpRC = m_webAPIAdapterInterface->devicesetChannelSettingsPutPatch( + deviceIndex, + channelIndex, + false, // PATCH + channelSettingsKeys, + channelSettingsResponse, + errorResponse + ); + + if (httpRC/100 == 2) + { + qDebug("VorLocalizerWorker::setAudioMute: navId: %d audioMute: %d OK", vorNavId, audioMute ? 1 : 0); + } + else + { + qWarning("VorLocalizerWorker::setAudioMute: navId: %d set audioMute error %d: %s", + vorNavId, httpRC, qPrintable(*errorResponse.getMessage())); + } +} + +void VorLocalizerWorker::generateIndexCombinations(int length, int subLength, std::vector>& indexCombinations) +{ + indexCombinations.clear(); + std::vector sublist(subLength); + std::vector::iterator first = sublist.begin(), last = sublist.end(); + std::iota(first, last, 0); + indexCombinations.push_back(sublist); + + while ((*first) != length - subLength) + { + std::vector::iterator mt = last; + + while (*(--mt) == length-(last-mt)); + (*mt)++; + while (++mt != last) *mt = *(mt-1)+1; + + indexCombinations.push_back(std::vector(first, last)); + } +} + +void VorLocalizerWorker::getVORRanges(const QList& vors, int subLength, std::vector& vorRanges) +{ + std::vector> indexCombinations; + generateIndexCombinations(vors.size(), subLength, indexCombinations); + vorRanges.clear(); + + for (auto indexCombination : indexCombinations) + { + int fMax = vors.at(indexCombination.back()).m_frequency; + int fMin = vors.at(indexCombination.front()).m_frequency; + vorRanges.push_back(VORRange{indexCombination, fMax - fMin}); + } +} + +void VorLocalizerWorker::filterVORRanges(std::vector& vorRanges, int thresholdBW) +{ + std::vector originalVORRanges(vorRanges.size()); + std::copy(vorRanges.begin(), vorRanges.end(), originalVORRanges.begin()); + vorRanges.clear(); + + for (auto vorRange : originalVORRanges) + { + if (vorRange.m_frequencyRange < thresholdBW) { + vorRanges.push_back(vorRange); + } + } +} + +void VorLocalizerWorker::getChannelsByDevice( + const QHash *availableChannels, + std::vector& devicesChannels +) +{ + struct + { + bool operator()(const RRTurnPlan& a, const RRTurnPlan& b) + { + unsigned int nbChannelsA = a.m_channels.size(); + unsigned int nbChannelsB = a.m_channels.size(); + + if (nbChannelsA == nbChannelsB) { + return a.m_bandwidth > b.m_bandwidth; + } else { + return nbChannelsA > nbChannelsB; + } + } + } rrTurnPlanGreater; + + QHash::const_iterator itr = availableChannels->begin(); + QMap devicesChannelsMap; + + for (; itr != availableChannels->end(); ++itr) + { + devicesChannelsMap[itr->m_deviceSetIndex].m_device.m_deviceIndex = itr->m_deviceSetIndex; + devicesChannelsMap[itr->m_deviceSetIndex].m_bandwidth = itr->m_basebandSampleRate; + devicesChannelsMap[itr->m_deviceSetIndex].m_channels.push_back(RRChannel{itr->m_channelAPI, itr->m_channelIndex, 0, -1}); + } + + QMap::const_iterator itm = devicesChannelsMap.begin(); + devicesChannels.clear(); + + for (; itm != devicesChannelsMap.end(); ++itm) { + devicesChannels.push_back(*itm); + } + + std::sort(devicesChannels.begin(), devicesChannels.end(), rrTurnPlanGreater); +} + +void VorLocalizerWorker::rrNextTurn() +{ + QMutexLocker mlock(&m_mutex); + int iDevPlan = 0; + VORLocalizerReport::MsgReportServiceddVORs *msg = VORLocalizerReport::MsgReportServiceddVORs::create(); + m_channelAllocations.clear(); + + for (auto rrPlan : m_rrPlans) + { + unsigned int turnCount = m_rrTurnCounters[iDevPlan]; + int deviceIndex = rrPlan[turnCount].m_device.m_deviceIndex; + int deviceFrequency = rrPlan[turnCount].m_device.m_frequency - m_settings.m_centerShift; + qDebug() << "VorLocalizerWorker::rrNextTurn: " + << "turn:" << turnCount + << "device:" << deviceIndex + << "frequency:" << deviceFrequency - m_settings.m_centerShift; + setDeviceFrequency(deviceIndex, deviceFrequency); + + for (auto channel : rrPlan[turnCount].m_channels) + { + qDebug() << "VorLocalizerWorker::rrNextTurn: " + << "device:" << deviceIndex + << "channel:" << channel.m_channelIndex + << "shift:" << channel.m_frequencyShift + m_settings.m_centerShift + << "navId:" << channel.m_navId; + setChannelShift( + deviceIndex, + channel.m_channelIndex, + channel.m_frequencyShift + m_settings.m_centerShift, + channel.m_navId + ); + m_channelAllocations[channel.m_navId] = ChannelAllocation{ + channel.m_navId, + deviceIndex, + channel.m_channelIndex + }; + + if(m_availableChannels->contains(channel.m_channelAPI)) + { + VORLocalizerSettings::AvailableChannel& availableChannel = m_availableChannels->operator[](channel.m_channelAPI); + availableChannel.m_navId = channel.m_navId; + } + + msg->getNavIds().push_back(channel.m_navId); + msg->getSinglePlans()[channel.m_navId] = (rrPlan.size() == 1); + } + + turnCount++; + + if (turnCount == rrPlan.size()) { + turnCount = 0; + } + + m_rrTurnCounters[iDevPlan] = turnCount; + iDevPlan++; + } + + if (m_msgQueueToFeature) { + m_msgQueueToFeature->push(msg); } } diff --git a/plugins/feature/vorlocalizer/vorlocalizerworker.h b/plugins/feature/vorlocalizer/vorlocalizerworker.h index f898cd346..32dfa414d 100644 --- a/plugins/feature/vorlocalizer/vorlocalizerworker.h +++ b/plugins/feature/vorlocalizer/vorlocalizerworker.h @@ -28,6 +28,7 @@ class WebAPIAdapterInterface; class ChannelAPI; +class Feature; class VorLocalizerWorker : public QObject { @@ -77,42 +78,98 @@ public: void stopWork(); bool isRunning() const { return m_running; } MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - void setMessageQueueToGUI(MessageQueue *messageQueue) { m_msgQueueToGUI = messageQueue; } + void setMessageQueueToFeature(MessageQueue *messageQueue) { m_msgQueueToFeature = messageQueue; } + void setAvailableChannels(QHash *avaialbleChannels) { + m_availableChannels = avaialbleChannels; + } private: - struct VORChannel + struct VORRange { - int m_subChannelId; //!< Unique VOR identifier (from database) - int m_frequency; //!< Frequency the VOR is on - bool m_audioMute; //!< Mute the audio from this VOR + std::vector m_vorIndices; + int m_frequencyRange; + + VORRange() = default; + VORRange(const VORRange&) = default; + VORRange& operator=(const VORRange&) = default; }; - struct AvailableChannel + struct RRDevice + { + int m_deviceIndex; + int m_frequency; + + RRDevice() = default; + RRDevice(const RRDevice&) = default; + RRDevice& operator=(const RRDevice&) = default; + }; + + struct RRChannel { - int m_deviceSetIndex; - int m_channelIndex; ChannelAPI *m_channelAPI; + int m_channelIndex; + int m_frequencyShift; + int m_navId; + + RRChannel() = default; + RRChannel(const RRChannel&) = default; + RRChannel& operator=(const RRChannel&) = default; + }; + + struct RRTurnPlan + { + RRDevice m_device; + int m_bandwidth; + std::vector m_channels; + + RRTurnPlan() = default; + RRTurnPlan(const RRTurnPlan&) = default; + RRTurnPlan& operator=(const RRTurnPlan&) = default; + }; + + struct ChannelAllocation + { + int m_navId; + int m_deviceIndex; + int m_channelIndex; + + ChannelAllocation() = default; + ChannelAllocation(const ChannelAllocation&) = default; + ChannelAllocation& operator=(const ChannelAllocation&) = default; }; WebAPIAdapterInterface *m_webAPIAdapterInterface; MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication - MessageQueue *m_msgQueueToGUI; //!< Queue to report state to GUI + MessageQueue *m_msgQueueToFeature; //!< Queue to report state to GUI VORLocalizerSettings m_settings; - QList m_vorChannels; - QList m_availableChannels; + QList m_vorChannels; + QHash m_channelAllocations; + QHash *m_availableChannels; bool m_running; QTimer m_updateTimer; QMutex m_mutex; + QTimer m_rrTimer; + std::vector> m_rrPlans; //!< Round robin plans for each device + std::vector m_rrTurnCounters; //!< Round robin turn count for each device bool handleMessage(const Message& cmd); void applySettings(const VORLocalizerSettings& settings, bool force = false); - void updateChannels(); void removeVORChannel(int navId); - void addVORChannel(const VORLocalizerSubChannelSettings *subChannelSettings); + void addVORChannel(const VORLocalizerSubChannelSettings& subChannelSettings); + void updateChannels(); //!< (re)allocate channels to service VORs + void allocateChannel(ChannelAPI *channel, int vorFrequency, int vorNavId, int channelShift); + void setDeviceFrequency(int deviceIndex, double targetFrequency); + void setChannelShift(int deviceIndex, int channelIndex, double targetOffset, int vorNavId); + void setAudioMute(int vorNavId, bool audioMute); + static void generateIndexCombinations(int length, int subLength, std::vector>& indexCombinations); + static void getVORRanges(const QList& vors, int subLength, std::vector& vorRanges); + static void filterVORRanges(std::vector& vorRanges, int thresholdBW); + static void getChannelsByDevice(const QHash *availableChannels, std::vector& m_deviceChannels); private slots: void handleInputMessages(); void updateHardware(); + void rrNextTurn(); }; #endif // INCLUDE_FEATURE_VORLOCALIZERWORKER_H_ diff --git a/sdrbase/pipes/messagepipes.cpp b/sdrbase/pipes/messagepipes.cpp index 18799eed9..3aaae269b 100644 --- a/sdrbase/pipes/messagepipes.cpp +++ b/sdrbase/pipes/messagepipes.cpp @@ -46,7 +46,7 @@ MessagePipes::~MessagePipes() } } -void MessagePipes::registerChannelToFeature(const ChannelAPI *source, const Feature *feature, const QString& type) +MessageQueue *MessagePipes::registerChannelToFeature(const ChannelAPI *source, const Feature *feature, const QString& type) { int typeId; @@ -68,8 +68,11 @@ void MessagePipes::registerChannelToFeature(const ChannelAPI *source, const Feat m_featureRegistrations.insert(regKey, QList()); } - m_messageRegistrations[regKey].append(new MessageQueue()); + MessageQueue *messageQueue = new MessageQueue(); + m_messageRegistrations[regKey].append(messageQueue); m_featureRegistrations[regKey].append(feature); + + return messageQueue; } QList* MessagePipes::getMessageQueues(const ChannelAPI *source, const QString& type) diff --git a/sdrbase/pipes/messagepipes.h b/sdrbase/pipes/messagepipes.h index 70bea01d0..0f97b75fc 100644 --- a/sdrbase/pipes/messagepipes.h +++ b/sdrbase/pipes/messagepipes.h @@ -45,7 +45,7 @@ public: MessagePipes& operator=(const MessagePipes&) = delete; ~MessagePipes(); - void registerChannelToFeature(const ChannelAPI *source, const Feature *feature, const QString& type); + MessageQueue *registerChannelToFeature(const ChannelAPI *source, const Feature *feature, const QString& type); QList* getMessageQueues(const ChannelAPI *source, const QString& type); private: diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index f9373dbff..9a69e8788 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -9484,20 +9484,36 @@ margin-bottom: 20px; "type" : "number", "format" : "float" }, + "navId" : { + "type" : "integer", + "description" : "VOR unique identifier when set by VOR localizer feature" + }, "radial" : { "type" : "number", "format" : "float", - "description" : "current detected radial" + "description" : "current detected radial (degrees)" }, "refMag" : { "type" : "number", "format" : "float", - "description" : "current reference signal magnitude" + "description" : "current reference signal magnitude (dB)" }, "varMag" : { "type" : "number", "format" : "float", - "description" : "current variable signal magnitude" + "description" : "current variable signal magnitude (dB)" + }, + "validRadial" : { + "type" : "integer", + "description" : "Radial validity estimation\n * 0 - Radial is invalid\n * 1 - Radial is valid\n" + }, + "validRefMag" : { + "type" : "integer", + "description" : "Reference signal magnitude validity\n * 0 - Magnitude below threshold\n * 1 - Magnitude above threshold\n" + }, + "validVarMag" : { + "type" : "integer", + "description" : "Variable signal magnitude validity\n * 0 - Magnitude below threshold\n * 1 - Magnitude above threshold\n" }, "morseIdent" : { "type" : "string", @@ -9645,6 +9661,14 @@ margin-bottom: 20px; "magDecAdjust" : { "type" : "integer", "description" : "Adjust radial lines on map for magnetic declination of VOR" + }, + "rrTime" : { + "type" : "integer", + "description" : "Round robin turn time in seconds" + }, + "centerShift" : { + "type" : "integer", + "description" : "Shift of center frequency in Hz" } }, "description" : "VORLocalizer" @@ -44850,7 +44874,7 @@ except ApiException as e:
- Generated 2020-11-29T21:00:18.945+01:00 + Generated 2020-12-06T23:43:21.772+01:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/VORDemodSC.yaml b/sdrbase/resources/webapi/doc/swagger/include/VORDemodSC.yaml index e9c103d0b..2d7b1a9aa 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/VORDemodSC.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/VORDemodSC.yaml @@ -55,18 +55,39 @@ VORDemodSCReport: volume: type: number format: float + navId: + description: VOR unique identifier when set by VOR localizer feature + type: integer radial: - description: current detected radial + description: current detected radial (degrees) type: number format: float refMag: - description: current reference signal magnitude + description: current reference signal magnitude (dB) type: number format: float varMag: - description: current variable signal magnitude + description: current variable signal magnitude (dB) type: number format: float + validRadial: + type: integer + description: > + Radial validity estimation + * 0 - Radial is invalid + * 1 - Radial is valid + validRefMag: + type: integer + description: > + Reference signal magnitude validity + * 0 - Magnitude below threshold + * 1 - Magnitude above threshold + validVarMag: + type: integer + description: > + Variable signal magnitude validity + * 0 - Magnitude below threshold + * 1 - Magnitude above threshold morseIdent: description: current identification morse code transcript type: string diff --git a/sdrbase/resources/webapi/doc/swagger/include/VORLocalizer.yaml b/sdrbase/resources/webapi/doc/swagger/include/VORLocalizer.yaml index 5b101cf60..fe1c2b669 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/VORLocalizer.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/VORLocalizer.yaml @@ -19,3 +19,9 @@ VORLocalizerSettings: magDecAdjust: description: Adjust radial lines on map for magnetic declination of VOR type: integer + rrTime: + description: Round robin turn time in seconds + type: integer + centerShift: + description: Shift of center frequency in Hz + type: integer diff --git a/sdrbase/util/average.h b/sdrbase/util/average.h new file mode 100644 index 000000000..d95edc615 --- /dev/null +++ b/sdrbase/util/average.h @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// // +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////////// + +#ifndef _UTIL_AVERAGE_H_ +#define _UTIL_AVERAGE_H_ + +#include + +template +class AverageUtil +{ + public: + AverageUtil() + : m_numSamples(0), m_total(0) + { } + + AverageUtil(T sample) + : m_numSamples(1), m_total(sample) + { } + + void reset() + { + m_numSamples = 0; + m_total = 0; + } + + void operator()(T sample) + { + m_total += sample; + m_numSamples++; + } + + double asDouble() const { return ((double)m_total) / (m_numSamples == 0 ? 1 : m_numSamples); } + float asFloat() const { return ((float)m_total) / (m_numSamples == 0 ? 1 : m_numSamples); } + operator T() const { return m_total / (m_numSamples == 0 ? 1 : m_numSamples); } + T instantAverage() const { return m_total / (m_numSamples == 0 ? 1 : m_numSamples); } + int getNumSamples() const { return m_numSamples; } + + private: + int m_numSamples; + Total m_total; +}; + +#endif /* _UTIL_AVERAGE_H_ */ diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index 255691d8b..a2bd59bf8 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -344,6 +344,51 @@ bool WebAPIUtils::setSubObjectDouble(QJsonObject &json, const QString &key, doub return false; } +// Get integer value from within nested JSON object +bool WebAPIUtils::getSubObjectInt(const QJsonObject &json, const QString &key, int &value) +{ + for (QJsonObject::const_iterator it = json.begin(); it != json.end(); it++) + { + QJsonValue jsonValue = it.value(); + + if (jsonValue.isObject()) + { + QJsonObject subObject = jsonValue.toObject(); + + if (subObject.contains(key)) + { + value = subObject[key].toInt(); + return true; + } + } + } + + return false; +} + +// Set integer value withing nested JSON object +bool WebAPIUtils::setSubObjectInt(QJsonObject &json, const QString &key, int value) +{ + for (QJsonObject::iterator it = json.begin(); it != json.end(); it++) + { + QJsonValue jsonValue = it.value(); + + if (jsonValue.isObject()) + { + QJsonObject subObject = jsonValue.toObject(); + + if (subObject.contains(key)) + { + subObject[key] = value; + it.value() = subObject; + return true; + } + } + } + + return false; +} + // look for value in key=value bool WebAPIUtils::extractValue(const QJsonObject &json, const QString &key, QJsonValue &value) { diff --git a/sdrbase/webapi/webapiutils.h b/sdrbase/webapi/webapiutils.h index 32ae7591e..2c6c28714 100644 --- a/sdrbase/webapi/webapiutils.h +++ b/sdrbase/webapi/webapiutils.h @@ -47,6 +47,8 @@ public: static bool getObjectObjects(const QJsonObject &json, const QString &key, QList &objects); static bool getSubObjectDouble(const QJsonObject &json, const QString &key, double &value); static bool setSubObjectDouble(QJsonObject &json, const QString &key, double value); + static bool getSubObjectInt(const QJsonObject &json, const QString &key, int &value); + static bool setSubObjectInt(QJsonObject &json, const QString &key, int value); static bool extractValue(const QJsonObject &json, const QString &key, QJsonValue &value); static bool extractArray(const QJsonObject &json, const QString &key, QJsonArray &value); static bool extractObject(const QJsonObject &json, const QString &key, QJsonObject &value); diff --git a/swagger/sdrangel/api/swagger/include/VORDemodSC.yaml b/swagger/sdrangel/api/swagger/include/VORDemodSC.yaml index e9c103d0b..2d7b1a9aa 100644 --- a/swagger/sdrangel/api/swagger/include/VORDemodSC.yaml +++ b/swagger/sdrangel/api/swagger/include/VORDemodSC.yaml @@ -55,18 +55,39 @@ VORDemodSCReport: volume: type: number format: float + navId: + description: VOR unique identifier when set by VOR localizer feature + type: integer radial: - description: current detected radial + description: current detected radial (degrees) type: number format: float refMag: - description: current reference signal magnitude + description: current reference signal magnitude (dB) type: number format: float varMag: - description: current variable signal magnitude + description: current variable signal magnitude (dB) type: number format: float + validRadial: + type: integer + description: > + Radial validity estimation + * 0 - Radial is invalid + * 1 - Radial is valid + validRefMag: + type: integer + description: > + Reference signal magnitude validity + * 0 - Magnitude below threshold + * 1 - Magnitude above threshold + validVarMag: + type: integer + description: > + Variable signal magnitude validity + * 0 - Magnitude below threshold + * 1 - Magnitude above threshold morseIdent: description: current identification morse code transcript type: string diff --git a/swagger/sdrangel/api/swagger/include/VORLocalizer.yaml b/swagger/sdrangel/api/swagger/include/VORLocalizer.yaml index 5b101cf60..fe1c2b669 100644 --- a/swagger/sdrangel/api/swagger/include/VORLocalizer.yaml +++ b/swagger/sdrangel/api/swagger/include/VORLocalizer.yaml @@ -19,3 +19,9 @@ VORLocalizerSettings: magDecAdjust: description: Adjust radial lines on map for magnetic declination of VOR type: integer + rrTime: + description: Round robin turn time in seconds + type: integer + centerShift: + description: Shift of center frequency in Hz + type: integer diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index f9373dbff..9a69e8788 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -9484,20 +9484,36 @@ margin-bottom: 20px; "type" : "number", "format" : "float" }, + "navId" : { + "type" : "integer", + "description" : "VOR unique identifier when set by VOR localizer feature" + }, "radial" : { "type" : "number", "format" : "float", - "description" : "current detected radial" + "description" : "current detected radial (degrees)" }, "refMag" : { "type" : "number", "format" : "float", - "description" : "current reference signal magnitude" + "description" : "current reference signal magnitude (dB)" }, "varMag" : { "type" : "number", "format" : "float", - "description" : "current variable signal magnitude" + "description" : "current variable signal magnitude (dB)" + }, + "validRadial" : { + "type" : "integer", + "description" : "Radial validity estimation\n * 0 - Radial is invalid\n * 1 - Radial is valid\n" + }, + "validRefMag" : { + "type" : "integer", + "description" : "Reference signal magnitude validity\n * 0 - Magnitude below threshold\n * 1 - Magnitude above threshold\n" + }, + "validVarMag" : { + "type" : "integer", + "description" : "Variable signal magnitude validity\n * 0 - Magnitude below threshold\n * 1 - Magnitude above threshold\n" }, "morseIdent" : { "type" : "string", @@ -9645,6 +9661,14 @@ margin-bottom: 20px; "magDecAdjust" : { "type" : "integer", "description" : "Adjust radial lines on map for magnetic declination of VOR" + }, + "rrTime" : { + "type" : "integer", + "description" : "Round robin turn time in seconds" + }, + "centerShift" : { + "type" : "integer", + "description" : "Shift of center frequency in Hz" } }, "description" : "VORLocalizer" @@ -44850,7 +44874,7 @@ except ApiException as e:
- Generated 2020-11-29T21:00:18.945+01:00 + Generated 2020-12-06T23:43:21.772+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.cpp index 02c73786a..b59d20e7b 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.cpp @@ -36,12 +36,20 @@ SWGVORDemodSCReport::SWGVORDemodSCReport() { m_audio_sample_rate_isSet = false; volume = 0.0f; m_volume_isSet = false; + nav_id = 0; + m_nav_id_isSet = false; radial = 0.0f; m_radial_isSet = false; ref_mag = 0.0f; m_ref_mag_isSet = false; var_mag = 0.0f; m_var_mag_isSet = false; + valid_radial = 0; + m_valid_radial_isSet = false; + valid_ref_mag = 0; + m_valid_ref_mag_isSet = false; + valid_var_mag = 0; + m_valid_var_mag_isSet = false; morse_ident = nullptr; m_morse_ident_isSet = false; } @@ -60,12 +68,20 @@ SWGVORDemodSCReport::init() { m_audio_sample_rate_isSet = false; volume = 0.0f; m_volume_isSet = false; + nav_id = 0; + m_nav_id_isSet = false; radial = 0.0f; m_radial_isSet = false; ref_mag = 0.0f; m_ref_mag_isSet = false; var_mag = 0.0f; m_var_mag_isSet = false; + valid_radial = 0; + m_valid_radial_isSet = false; + valid_ref_mag = 0; + m_valid_ref_mag_isSet = false; + valid_var_mag = 0; + m_valid_var_mag_isSet = false; morse_ident = new QString(""); m_morse_ident_isSet = false; } @@ -79,6 +95,10 @@ SWGVORDemodSCReport::cleanup() { + + + + if(morse_ident != nullptr) { delete morse_ident; } @@ -103,12 +123,20 @@ SWGVORDemodSCReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); + ::SWGSDRangel::setValue(&nav_id, pJson["navId"], "qint32", ""); + ::SWGSDRangel::setValue(&radial, pJson["radial"], "float", ""); ::SWGSDRangel::setValue(&ref_mag, pJson["refMag"], "float", ""); ::SWGSDRangel::setValue(&var_mag, pJson["varMag"], "float", ""); + ::SWGSDRangel::setValue(&valid_radial, pJson["validRadial"], "qint32", ""); + + ::SWGSDRangel::setValue(&valid_ref_mag, pJson["validRefMag"], "qint32", ""); + + ::SWGSDRangel::setValue(&valid_var_mag, pJson["validVarMag"], "qint32", ""); + ::SWGSDRangel::setValue(&morse_ident, pJson["morseIdent"], "QString", "QString"); } @@ -139,6 +167,9 @@ SWGVORDemodSCReport::asJsonObject() { if(m_volume_isSet){ obj->insert("volume", QJsonValue(volume)); } + if(m_nav_id_isSet){ + obj->insert("navId", QJsonValue(nav_id)); + } if(m_radial_isSet){ obj->insert("radial", QJsonValue(radial)); } @@ -148,6 +179,15 @@ SWGVORDemodSCReport::asJsonObject() { if(m_var_mag_isSet){ obj->insert("varMag", QJsonValue(var_mag)); } + if(m_valid_radial_isSet){ + obj->insert("validRadial", QJsonValue(valid_radial)); + } + if(m_valid_ref_mag_isSet){ + obj->insert("validRefMag", QJsonValue(valid_ref_mag)); + } + if(m_valid_var_mag_isSet){ + obj->insert("validVarMag", QJsonValue(valid_var_mag)); + } if(morse_ident != nullptr && *morse_ident != QString("")){ toJsonValue(QString("morseIdent"), morse_ident, obj, QString("QString")); } @@ -195,6 +235,16 @@ SWGVORDemodSCReport::setVolume(float volume) { this->m_volume_isSet = true; } +qint32 +SWGVORDemodSCReport::getNavId() { + return nav_id; +} +void +SWGVORDemodSCReport::setNavId(qint32 nav_id) { + this->nav_id = nav_id; + this->m_nav_id_isSet = true; +} + float SWGVORDemodSCReport::getRadial() { return radial; @@ -225,6 +275,36 @@ SWGVORDemodSCReport::setVarMag(float var_mag) { this->m_var_mag_isSet = true; } +qint32 +SWGVORDemodSCReport::getValidRadial() { + return valid_radial; +} +void +SWGVORDemodSCReport::setValidRadial(qint32 valid_radial) { + this->valid_radial = valid_radial; + this->m_valid_radial_isSet = true; +} + +qint32 +SWGVORDemodSCReport::getValidRefMag() { + return valid_ref_mag; +} +void +SWGVORDemodSCReport::setValidRefMag(qint32 valid_ref_mag) { + this->valid_ref_mag = valid_ref_mag; + this->m_valid_ref_mag_isSet = true; +} + +qint32 +SWGVORDemodSCReport::getValidVarMag() { + return valid_var_mag; +} +void +SWGVORDemodSCReport::setValidVarMag(qint32 valid_var_mag) { + this->valid_var_mag = valid_var_mag; + this->m_valid_var_mag_isSet = true; +} + QString* SWGVORDemodSCReport::getMorseIdent() { return morse_ident; @@ -252,6 +332,9 @@ SWGVORDemodSCReport::isSet(){ if(m_volume_isSet){ isObjectUpdated = true; break; } + if(m_nav_id_isSet){ + isObjectUpdated = true; break; + } if(m_radial_isSet){ isObjectUpdated = true; break; } @@ -261,6 +344,15 @@ SWGVORDemodSCReport::isSet(){ if(m_var_mag_isSet){ isObjectUpdated = true; break; } + if(m_valid_radial_isSet){ + isObjectUpdated = true; break; + } + if(m_valid_ref_mag_isSet){ + isObjectUpdated = true; break; + } + if(m_valid_var_mag_isSet){ + isObjectUpdated = true; break; + } if(morse_ident && *morse_ident != QString("")){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h index e990cd685..e0afd6b71 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h @@ -54,6 +54,9 @@ public: float getVolume(); void setVolume(float volume); + qint32 getNavId(); + void setNavId(qint32 nav_id); + float getRadial(); void setRadial(float radial); @@ -63,6 +66,15 @@ public: float getVarMag(); void setVarMag(float var_mag); + qint32 getValidRadial(); + void setValidRadial(qint32 valid_radial); + + qint32 getValidRefMag(); + void setValidRefMag(qint32 valid_ref_mag); + + qint32 getValidVarMag(); + void setValidVarMag(qint32 valid_var_mag); + QString* getMorseIdent(); void setMorseIdent(QString* morse_ident); @@ -82,6 +94,9 @@ private: float volume; bool m_volume_isSet; + qint32 nav_id; + bool m_nav_id_isSet; + float radial; bool m_radial_isSet; @@ -91,6 +106,15 @@ private: float var_mag; bool m_var_mag_isSet; + qint32 valid_radial; + bool m_valid_radial_isSet; + + qint32 valid_ref_mag; + bool m_valid_ref_mag_isSet; + + qint32 valid_var_mag; + bool m_valid_var_mag_isSet; + QString* morse_ident; bool m_morse_ident_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings.cpp index 19e992b06..4ea3cb05e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings.cpp @@ -44,6 +44,10 @@ SWGVORLocalizerSettings::SWGVORLocalizerSettings() { m_reverse_api_feature_index_isSet = false; mag_dec_adjust = 0; m_mag_dec_adjust_isSet = false; + rr_time = 0; + m_rr_time_isSet = false; + center_shift = 0; + m_center_shift_isSet = false; } SWGVORLocalizerSettings::~SWGVORLocalizerSettings() { @@ -68,6 +72,10 @@ SWGVORLocalizerSettings::init() { m_reverse_api_feature_index_isSet = false; mag_dec_adjust = 0; m_mag_dec_adjust_isSet = false; + rr_time = 0; + m_rr_time_isSet = false; + center_shift = 0; + m_center_shift_isSet = false; } void @@ -84,6 +92,8 @@ SWGVORLocalizerSettings::cleanup() { + + } SWGVORLocalizerSettings* @@ -113,6 +123,10 @@ SWGVORLocalizerSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&mag_dec_adjust, pJson["magDecAdjust"], "qint32", ""); + ::SWGSDRangel::setValue(&rr_time, pJson["rrTime"], "qint32", ""); + + ::SWGSDRangel::setValue(¢er_shift, pJson["centerShift"], "qint32", ""); + } QString @@ -153,6 +167,12 @@ SWGVORLocalizerSettings::asJsonObject() { if(m_mag_dec_adjust_isSet){ obj->insert("magDecAdjust", QJsonValue(mag_dec_adjust)); } + if(m_rr_time_isSet){ + obj->insert("rrTime", QJsonValue(rr_time)); + } + if(m_center_shift_isSet){ + obj->insert("centerShift", QJsonValue(center_shift)); + } return obj; } @@ -237,6 +257,26 @@ SWGVORLocalizerSettings::setMagDecAdjust(qint32 mag_dec_adjust) { this->m_mag_dec_adjust_isSet = true; } +qint32 +SWGVORLocalizerSettings::getRrTime() { + return rr_time; +} +void +SWGVORLocalizerSettings::setRrTime(qint32 rr_time) { + this->rr_time = rr_time; + this->m_rr_time_isSet = true; +} + +qint32 +SWGVORLocalizerSettings::getCenterShift() { + return center_shift; +} +void +SWGVORLocalizerSettings::setCenterShift(qint32 center_shift) { + this->center_shift = center_shift; + this->m_center_shift_isSet = true; +} + bool SWGVORLocalizerSettings::isSet(){ @@ -266,6 +306,12 @@ SWGVORLocalizerSettings::isSet(){ if(m_mag_dec_adjust_isSet){ isObjectUpdated = true; break; } + if(m_rr_time_isSet){ + isObjectUpdated = true; break; + } + if(m_center_shift_isSet){ + isObjectUpdated = true; break; + } }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings.h b/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings.h index 9ddd7f205..2e3617a17 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings.h @@ -66,6 +66,12 @@ public: qint32 getMagDecAdjust(); void setMagDecAdjust(qint32 mag_dec_adjust); + qint32 getRrTime(); + void setRrTime(qint32 rr_time); + + qint32 getCenterShift(); + void setCenterShift(qint32 center_shift); + virtual bool isSet() override; @@ -94,6 +100,12 @@ private: qint32 mag_dec_adjust; bool m_mag_dec_adjust_isSet; + qint32 rr_time; + bool m_rr_time_isSet; + + qint32 center_shift; + bool m_center_shift_isSet; + }; }