mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	VOR single channel: first working release of VOR localizer feature plugin
This commit is contained in:
		
							parent
							
								
									ffe515fb63
								
							
						
					
					
						commit
						6c02a78d62
					
				| @ -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<QString>& channelSettingsKeys, const | ||||
| 
 | ||||
| void VORDemodSC::sendChannelReport(QList<MessageQueue*> *messageQueues) | ||||
| { | ||||
|     SWGSDRangel::SWGChannelReport *swgChannelReport = new SWGSDRangel::SWGChannelReport(); | ||||
|     webapiFormatChannelReport(*swgChannelReport); | ||||
|     QList<MessageQueue*>::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); | ||||
|     } | ||||
|  | ||||
| @ -303,6 +303,9 @@ | ||||
|         <property name="maximum"> | ||||
|          <number>100</number> | ||||
|         </property> | ||||
|         <property name="pageStep"> | ||||
|          <number>1</number> | ||||
|         </property> | ||||
|         <property name="value"> | ||||
|          <number>0</number> | ||||
|         </property> | ||||
| @ -367,6 +370,9 @@ | ||||
|         <property name="maximum"> | ||||
|          <number>0</number> | ||||
|         </property> | ||||
|         <property name="pageStep"> | ||||
|          <number>1</number> | ||||
|         </property> | ||||
|         <property name="value"> | ||||
|          <number>-40</number> | ||||
|         </property> | ||||
| @ -416,6 +422,9 @@ | ||||
|         <property name="maximum"> | ||||
|          <number>100</number> | ||||
|         </property> | ||||
|         <property name="pageStep"> | ||||
|          <number>1</number> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
| @ -655,7 +664,6 @@ | ||||
|  </tabstops> | ||||
|  <resources> | ||||
|   <include location="../../../sdrgui/resources/res.qrc"/> | ||||
|   <include location="map.qrc"/> | ||||
|   <include location="icons.qrc"/> | ||||
|  </resources> | ||||
|  <connections/> | ||||
|  | ||||
| @ -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()) | ||||
|                 { | ||||
|  | ||||
| @ -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<float, double>(), | ||||
|                     AverageUtil<float, double>(), | ||||
|                     AverageUtil<float, double>(), | ||||
|                     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<int>& vorNavIds = report.getNavIds(); | ||||
|         m_vorSinglePlans = report.getSinglePlans(); | ||||
| 
 | ||||
|         for (std::vector<int>::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<QString> 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<DeviceSet*>& deviceSets = mainCore->getDeviceSets(); | ||||
|     std::vector<DeviceSet*>::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<VORLocalizerReport::MsgReportChannels::Channel>& msgChannels = msgToGUI->getChannels(); | ||||
|         QHash<ChannelAPI*, VORLocalizerSettings::AvailableChannel>::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<QString>& 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<float, double> m_radialAvg; | ||||
|         AverageUtil<float, double> m_refMagAvg; | ||||
|         AverageUtil<float, double> 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<ChannelAPI*, VORLocalizerSettings::AvailableChannel> m_availableChannels; | ||||
|     QHash<int, VORChannelReport> m_vorChannelReports; | ||||
|     QHash<int, bool> 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<QString>& featureSettingsKeys, const VORLocalizerSettings& settings, bool force); | ||||
| 
 | ||||
| private slots: | ||||
|     void networkManagerFinished(QNetworkReply *reply); | ||||
|     void handleChannelMessageQueue(MessageQueue* messageQueue); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_FEATURE_VORLOCALIZER_H_
 | ||||
|  | ||||
| @ -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<float>::epsilon()) | ||||
| 
 | ||||
|     if (abs(delta12) < std::numeric_limits<float>::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<QAction*>(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<int>& 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<QObject*>("map"); | ||||
| 
 | ||||
|     if (object) | ||||
|     { | ||||
|         QGeoCoordinate coords = object->property("center").value<QGeoCoordinate>(); | ||||
| @ -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<QObject*>("station"); | ||||
| 
 | ||||
|     if (stationObject) | ||||
|     { | ||||
|         QGeoCoordinate coords = stationObject->property("coordinate").value<QGeoCoordinate>(); | ||||
| @ -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<QObject*>("station"); | ||||
| 
 | ||||
|             if(stationObject != NULL) | ||||
|             { | ||||
|                 QGeoCoordinate coords = stationObject->property("coordinate").value<QGeoCoordinate>(); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -120,7 +120,7 @@ | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QPushButton" name="magDecAdjust"> | ||||
|        <widget class="ButtonSwitch" name="magDecAdjust"> | ||||
|         <property name="toolTip"> | ||||
|          <string>Draw radials adjusted for magnetic declination</string> | ||||
|         </property> | ||||
| @ -139,6 +139,124 @@ | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="rrTimeLabel"> | ||||
|         <property name="text"> | ||||
|          <string>RR</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QDial" name="rrTime"> | ||||
|         <property name="maximumSize"> | ||||
|          <size> | ||||
|           <width>24</width> | ||||
|           <height>24</height> | ||||
|          </size> | ||||
|         </property> | ||||
|         <property name="toolTip"> | ||||
|          <string>Round robin turn time (s)</string> | ||||
|         </property> | ||||
|         <property name="minimum"> | ||||
|          <number>10</number> | ||||
|         </property> | ||||
|         <property name="maximum"> | ||||
|          <number>60</number> | ||||
|         </property> | ||||
|         <property name="singleStep"> | ||||
|          <number>5</number> | ||||
|         </property> | ||||
|         <property name="pageStep"> | ||||
|          <number>5</number> | ||||
|         </property> | ||||
|         <property name="value"> | ||||
|          <number>20</number> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="rrTimeText"> | ||||
|         <property name="minimumSize"> | ||||
|          <size> | ||||
|           <width>30</width> | ||||
|           <height>0</height> | ||||
|          </size> | ||||
|         </property> | ||||
|         <property name="maximumSize"> | ||||
|          <size> | ||||
|           <width>30</width> | ||||
|           <height>16777215</height> | ||||
|          </size> | ||||
|         </property> | ||||
|         <property name="toolTip"> | ||||
|          <string>Sound volume (%)</string> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>20s</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="Line" name="line"> | ||||
|         <property name="orientation"> | ||||
|          <enum>Qt::Vertical</enum> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="centerShiftLabel"> | ||||
|         <property name="text"> | ||||
|          <string>Sh</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QDial" name="centerShift"> | ||||
|         <property name="maximumSize"> | ||||
|          <size> | ||||
|           <width>24</width> | ||||
|           <height>24</height> | ||||
|          </size> | ||||
|         </property> | ||||
|         <property name="toolTip"> | ||||
|          <string>Center frequency shift (kHz)</string> | ||||
|         </property> | ||||
|         <property name="minimum"> | ||||
|          <number>-40</number> | ||||
|         </property> | ||||
|         <property name="maximum"> | ||||
|          <number>40</number> | ||||
|         </property> | ||||
|         <property name="singleStep"> | ||||
|          <number>1</number> | ||||
|         </property> | ||||
|         <property name="pageStep"> | ||||
|          <number>1</number> | ||||
|         </property> | ||||
|         <property name="value"> | ||||
|          <number>0</number> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="centerShiftText"> | ||||
|         <property name="minimumSize"> | ||||
|          <size> | ||||
|           <width>30</width> | ||||
|           <height>0</height> | ||||
|          </size> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>20k</string> | ||||
|         </property> | ||||
|         <property name="alignment"> | ||||
|          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <spacer name="horizontalSpacer_2"> | ||||
|         <property name="orientation"> | ||||
| @ -159,7 +277,7 @@ | ||||
|       <item> | ||||
|        <widget class="QLabel" name="channelsLabel"> | ||||
|         <property name="text"> | ||||
|          <string>VORs</string> | ||||
|          <string>Chan</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
| @ -264,7 +382,7 @@ | ||||
|       </column> | ||||
|       <column> | ||||
|        <property name="text"> | ||||
|         <string>Offset (kHz)</string> | ||||
|         <string>Nav Id</string> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Offset of the VOR's frequency from the current center frequency. Red indicates out of range.</string> | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
| #define INCLUDE_VORLOCALIZERREPORT_H | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include <QHash> | ||||
| 
 | ||||
| #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<int>& getNavIds() { return m_navIds; } | ||||
|         QHash<int, bool>& getSinglePlans() { return m_singlePlans; } | ||||
| 
 | ||||
|         static MsgReportServiceddVORs* create() { | ||||
|             return new MsgReportServiceddVORs(); | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         std::vector<int> m_navIds; | ||||
|         QHash<int, bool> m_singlePlans; | ||||
| 
 | ||||
|         MsgReportServiceddVORs() : | ||||
|             Message() | ||||
|         {} | ||||
|     }; | ||||
| 
 | ||||
| public: | ||||
|     VORLocalizerReport() {} | ||||
|     ~VORLocalizerReport() {} | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
| #include <QHash> | ||||
| 
 | ||||
| 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<int, VORLocalizerSubChannelSettings *> m_subChannelSettings; | ||||
|     QHash<int, VORLocalizerSubChannelSettings> m_subChannelSettings; | ||||
| 
 | ||||
|     VORLocalizerSettings(); | ||||
|     void resetToDefaults(); | ||||
|  | ||||
| @ -18,12 +18,15 @@ | ||||
| #include <QDebug> | ||||
| 
 | ||||
| #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<int, VORLocalizerSubChannelSettings *>::const_iterator itr = settings.m_subChannelSettings.begin(); | ||||
|     QHash<int, VORLocalizerSubChannelSettings>::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<DeviceSet*>& deviceSets = mainCore->getDeviceSets(); | ||||
|     std::vector<DeviceSet*>::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<RRTurnPlan> devicesChannels; | ||||
|     getChannelsByDevice(m_availableChannels, devicesChannels); | ||||
|     QList<VORLocalizerSettings::VORChannel> 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<VORRange> 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<QList<VORLocalizerSettings::VORChannel>> vorLists; | ||||
| 
 | ||||
|         for (auto vorRange : vorRanges) | ||||
|         { | ||||
|             QList<VORLocalizerSettings::VORChannel> 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<RRTurnPlan> 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<VORLocalizerSettings::VORChannel>::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<VORRange> vorRanges; | ||||
|         getVORRanges(unallocatedVORs, 1, vorRanges); | ||||
|         std::vector<VorLocalizerWorker::RRTurnPlan>& rrPlan = m_rrPlans.front(); | ||||
|         std::vector<QList<VORLocalizerSettings::VORChannel>> vorLists; | ||||
| 
 | ||||
|         for (auto vorRange : vorRanges) | ||||
|         { | ||||
|             QList<VORLocalizerSettings::VORChannel> 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<VORLocalizerReport::MsgReportChannels::Channel>& 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<std::vector<int>>& indexCombinations) | ||||
| { | ||||
|     indexCombinations.clear(); | ||||
|     std::vector<int> sublist(subLength); | ||||
|     std::vector<int>::iterator first = sublist.begin(), last = sublist.end(); | ||||
|     std::iota(first, last, 0); | ||||
|     indexCombinations.push_back(sublist); | ||||
| 
 | ||||
|     while ((*first) != length - subLength) | ||||
|     { | ||||
|         std::vector<int>::iterator mt = last; | ||||
| 
 | ||||
|         while (*(--mt) == length-(last-mt)); | ||||
|         (*mt)++; | ||||
|         while (++mt != last) *mt = *(mt-1)+1; | ||||
| 
 | ||||
|         indexCombinations.push_back(std::vector<int>(first, last)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void VorLocalizerWorker::getVORRanges(const QList<VORLocalizerSettings::VORChannel>& vors, int subLength, std::vector<VORRange>& vorRanges) | ||||
| { | ||||
|     std::vector<std::vector<int>> 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<VORRange>& vorRanges, int thresholdBW) | ||||
| { | ||||
|     std::vector<VORRange> 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<ChannelAPI*, VORLocalizerSettings::AvailableChannel> *availableChannels, | ||||
|     std::vector<RRTurnPlan>& 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<ChannelAPI*, VORLocalizerSettings::AvailableChannel>::const_iterator itr = availableChannels->begin(); | ||||
|     QMap<int, RRTurnPlan> 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<int, RRTurnPlan>::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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<ChannelAPI*, VORLocalizerSettings::AvailableChannel> *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<int> 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<RRChannel> 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<VORChannel> m_vorChannels; | ||||
|     QList<AvailableChannel> m_availableChannels; | ||||
|     QList<VORLocalizerSettings::VORChannel> m_vorChannels; | ||||
|     QHash<int, ChannelAllocation> m_channelAllocations; | ||||
|     QHash<ChannelAPI*, VORLocalizerSettings::AvailableChannel> *m_availableChannels; | ||||
|     bool m_running; | ||||
| 	QTimer m_updateTimer; | ||||
|     QMutex m_mutex; | ||||
|     QTimer m_rrTimer; | ||||
|     std::vector<std::vector<RRTurnPlan>> m_rrPlans; //!< Round robin plans for each device
 | ||||
|     std::vector<int> 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<std::vector<int>>& indexCombinations); | ||||
|     static void getVORRanges(const QList<VORLocalizerSettings::VORChannel>& vors, int subLength, std::vector<VORRange>& vorRanges); | ||||
|     static void filterVORRanges(std::vector<VORRange>& vorRanges, int thresholdBW); | ||||
|     static void getChannelsByDevice(const QHash<ChannelAPI*, VORLocalizerSettings::AvailableChannel> *availableChannels, std::vector<RRTurnPlan>& m_deviceChannels); | ||||
| 
 | ||||
| private slots: | ||||
|     void handleInputMessages(); | ||||
| 	void updateHardware(); | ||||
|     void rrNextTurn(); | ||||
| }; | ||||
| 
 | ||||
| #endif // INCLUDE_FEATURE_VORLOCALIZERWORKER_H_
 | ||||
|  | ||||
| @ -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<const Feature*>()); | ||||
| 	} | ||||
| 
 | ||||
| 	m_messageRegistrations[regKey].append(new MessageQueue()); | ||||
| 	MessageQueue *messageQueue = new MessageQueue(); | ||||
| 	m_messageRegistrations[regKey].append(messageQueue); | ||||
| 	m_featureRegistrations[regKey].append(feature); | ||||
| 
 | ||||
| 	return messageQueue; | ||||
| } | ||||
| 
 | ||||
| QList<MessageQueue*>* MessagePipes::getMessageQueues(const ChannelAPI *source, const QString& type) | ||||
|  | ||||
| @ -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<MessageQueue*>* getMessageQueues(const ChannelAPI *source, const QString& type); | ||||
| 
 | ||||
| private: | ||||
|  | ||||
| @ -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: | ||||
|           </div> | ||||
|           <div id="generator"> | ||||
|             <div class="content"> | ||||
|               Generated 2020-11-29T21:00:18.945+01:00 | ||||
|               Generated 2020-12-06T23:43:21.772+01:00 | ||||
|             </div> | ||||
|           </div> | ||||
|       </div> | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										59
									
								
								sdrbase/util/average.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								sdrbase/util/average.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 <http://www.gnu.org/licenses/>.              //
 | ||||
| ///////////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| #ifndef _UTIL_AVERAGE_H_ | ||||
| #define _UTIL_AVERAGE_H_ | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| template <typename T, typename Total> | ||||
| 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_ */ | ||||
| @ -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) | ||||
| { | ||||
|  | ||||
| @ -47,6 +47,8 @@ public: | ||||
|     static bool getObjectObjects(const QJsonObject &json, const QString &key, QList<QJsonObject> &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); | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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: | ||||
|           </div> | ||||
|           <div id="generator"> | ||||
|             <div class="content"> | ||||
|               Generated 2020-11-29T21:00:18.945+01:00 | ||||
|               Generated 2020-12-06T23:43:21.772+01:00 | ||||
|             </div> | ||||
|           </div> | ||||
|       </div> | ||||
|  | ||||
| @ -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; | ||||
|         } | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user