mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 13:11:20 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			757 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			757 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
// Copyright (C) 2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com>         //
 | 
						|
// Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com>                //
 | 
						|
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai>                      //
 | 
						|
//                                                                               //
 | 
						|
// 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/>.          //
 | 
						|
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#include <QDebug>
 | 
						|
 | 
						|
#include "SWGChannelSettings.h"
 | 
						|
#include "SWGSuccessResponse.h"
 | 
						|
#include "SWGErrorResponse.h"
 | 
						|
 | 
						|
#include "device/deviceset.h"
 | 
						|
#include "device/deviceapi.h"
 | 
						|
#include "dsp/devicesamplesource.h"
 | 
						|
#include "dsp/devicesamplesink.h"
 | 
						|
#include "channel/channelapi.h"
 | 
						|
#include "channel/channelwebapiutils.h"
 | 
						|
#include "webapi/webapiadapterinterface.h"
 | 
						|
#include "webapi/webapiutils.h"
 | 
						|
#include "maincore.h"
 | 
						|
 | 
						|
#include "vorlocalizerreport.h"
 | 
						|
#include "vorlocalizerworker.h"
 | 
						|
 | 
						|
MESSAGE_CLASS_DEFINITION(VorLocalizerWorker::MsgConfigureVORLocalizerWorker, Message)
 | 
						|
MESSAGE_CLASS_DEFINITION(VorLocalizerWorker::MsgRefreshChannels, Message)
 | 
						|
 | 
						|
class DSPDeviceSourceEngine;
 | 
						|
 | 
						|
VorLocalizerWorker::VorLocalizerWorker(WebAPIAdapterInterface *webAPIAdapterInterface) :
 | 
						|
    m_webAPIAdapterInterface(webAPIAdapterInterface),
 | 
						|
    m_msgQueueToFeature(nullptr),
 | 
						|
    m_availableChannels(nullptr),
 | 
						|
    m_updateTimer(this),
 | 
						|
    m_rrTimer(this)
 | 
						|
{
 | 
						|
    qDebug("VorLocalizerWorker::VorLocalizerWorker");
 | 
						|
	connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
 | 
						|
}
 | 
						|
 | 
						|
VorLocalizerWorker::~VorLocalizerWorker()
 | 
						|
{
 | 
						|
    m_inputMessageQueue.clear();
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::reset()
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    m_inputMessageQueue.clear();
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::startWork()
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
						|
    connect(&m_rrTimer, SIGNAL(timeout()), this, SLOT(rrNextTurn()));
 | 
						|
    connect(thread(), SIGNAL(started()), this, SLOT(started()));
 | 
						|
    connect(thread(), SIGNAL(finished()), this, SLOT(finished()));
 | 
						|
}
 | 
						|
 | 
						|
// startWork() is called from main thread. Timers/sockets need to be started on worker thread
 | 
						|
void VorLocalizerWorker::started()
 | 
						|
{
 | 
						|
    m_rrTimer.start(m_settings.m_rrTime * 1000);
 | 
						|
    disconnect(thread(), SIGNAL(started()), this, SLOT(started()));
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::stopWork()
 | 
						|
{
 | 
						|
    QMutexLocker mutexLocker(&m_mutex);
 | 
						|
    disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::finished()
 | 
						|
{
 | 
						|
    m_rrTimer.stop();
 | 
						|
    disconnect(&m_rrTimer, SIGNAL(timeout()), this, SLOT(rrNextTurn()));
 | 
						|
    disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::handleInputMessages()
 | 
						|
{
 | 
						|
	Message* message;
 | 
						|
 | 
						|
	while ((message = m_inputMessageQueue.pop()) != nullptr)
 | 
						|
	{
 | 
						|
		if (handleMessage(*message)) {
 | 
						|
			delete message;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
bool VorLocalizerWorker::handleMessage(const Message& cmd)
 | 
						|
{
 | 
						|
    if (MsgConfigureVORLocalizerWorker::match(cmd))
 | 
						|
    {
 | 
						|
        QMutexLocker mutexLocker(&m_mutex);
 | 
						|
        MsgConfigureVORLocalizerWorker& cfg = (MsgConfigureVORLocalizerWorker&) cmd;
 | 
						|
        qDebug() << "VorLocalizerWorker::handleMessage: MsgConfigureVORLocalizerWorker";
 | 
						|
 | 
						|
        applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (MsgRefreshChannels::match(cmd))
 | 
						|
    {
 | 
						|
        qDebug() << "VorLocalizerWorker::handleMessage: MsgRefreshChannels";
 | 
						|
        updateChannels();
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::applySettings(const VORLocalizerSettings& settings, const QList<QString>& settingsKeys, bool force)
 | 
						|
{
 | 
						|
    qDebug() << "VorLocalizerWorker::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
 | 
						|
 | 
						|
    // Remove sub-channels no longer needed
 | 
						|
    for (int i = 0; i < m_vorChannels.size(); i++)
 | 
						|
    {
 | 
						|
        if (!settings.m_subChannelSettings.contains(m_vorChannels[i].m_subChannelId))
 | 
						|
        {
 | 
						|
            qDebug() << "VorLocalizerWorker::applySettings: Removing sink " << m_vorChannels[i].m_subChannelId;
 | 
						|
            removeVORChannel(m_vorChannels[i].m_subChannelId);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Add new sub channels
 | 
						|
    QHash<int, VORLocalizerSubChannelSettings>::const_iterator itr = settings.m_subChannelSettings.begin();
 | 
						|
 | 
						|
    while (itr != settings.m_subChannelSettings.end())
 | 
						|
    {
 | 
						|
        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)
 | 
						|
            {
 | 
						|
                qDebug() << "VorLocalizerWorker::applySettings: subchannel "
 | 
						|
                    << subChannelSettings.m_id
 | 
						|
                    << "already present";
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (j == m_vorChannels.size())
 | 
						|
        {
 | 
						|
            // Add a sub-channel sink
 | 
						|
            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 (settingsKeys.contains("rrTime") || force) {
 | 
						|
        m_rrTimer.start(settings.m_rrTime * 1000);
 | 
						|
    }
 | 
						|
 | 
						|
    if (force) {
 | 
						|
        m_settings = settings;
 | 
						|
    } else {
 | 
						|
        m_settings.applySettings(settingsKeys, settings);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::updateHardware()
 | 
						|
{
 | 
						|
    SWGSDRangel::SWGSuccessResponse response;
 | 
						|
    SWGSDRangel::SWGErrorResponse error;
 | 
						|
    m_updateTimer.stop();
 | 
						|
    m_mutex.unlock();
 | 
						|
}
 | 
						|
 | 
						|
quint64 VorLocalizerWorker::getDeviceCenterFrequency(int deviceIndex)
 | 
						|
{
 | 
						|
    std::vector<DeviceSet*> deviceSets = MainCore::instance()->getDeviceSets();
 | 
						|
    if (deviceIndex < (int) deviceSets.size())
 | 
						|
    {
 | 
						|
        DeviceSet *deviceSet = deviceSets[deviceIndex];
 | 
						|
        if (deviceSet->m_deviceSourceEngine)
 | 
						|
        {
 | 
						|
            DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource();
 | 
						|
            return source->getCenterFrequency();
 | 
						|
        }
 | 
						|
        else if (deviceSet->m_deviceSinkEngine)
 | 
						|
        {
 | 
						|
            DeviceSampleSink *sink = deviceSet->m_deviceAPI->getSampleSink();
 | 
						|
            return sink->getCenterFrequency();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int VorLocalizerWorker::getDeviceSampleRate(int deviceIndex)
 | 
						|
{
 | 
						|
    std::vector<DeviceSet*> deviceSets = MainCore::instance()->getDeviceSets();
 | 
						|
    if (deviceIndex < (int) deviceSets.size())
 | 
						|
    {
 | 
						|
        DeviceSet *deviceSet = deviceSets[deviceIndex];
 | 
						|
        if (deviceSet->m_deviceSourceEngine)
 | 
						|
        {
 | 
						|
            DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource();
 | 
						|
            return source->getSampleRate();
 | 
						|
        }
 | 
						|
        else if (deviceSet->m_deviceSinkEngine)
 | 
						|
        {
 | 
						|
            DeviceSampleSink *sink = deviceSet->m_deviceAPI->getSampleSink();
 | 
						|
            return sink->getSampleRate();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
// Does this device have a center frequency setting (FileInput doesn't)
 | 
						|
bool VorLocalizerWorker::hasCenterFrequencySetting(int deviceIndex)
 | 
						|
{
 | 
						|
    double deviceFrequency;
 | 
						|
    return !ChannelWebAPIUtils::getCenterFrequency(deviceIndex, deviceFrequency);
 | 
						|
}
 | 
						|
 | 
						|
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)
 | 
						|
        {
 | 
						|
            m_vorChannels.removeAt(i);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    updateChannels();
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::addVORChannel(const VORLocalizerSubChannelSettings& subChannelSettings)
 | 
						|
{
 | 
						|
    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()
 | 
						|
{
 | 
						|
    qDebug() << "VorLocalizerWorker::updateChannels: "
 | 
						|
        << "#VORs:" << m_vorChannels.size()
 | 
						|
        << "#Chans:" << m_availableChannels->size();
 | 
						|
 | 
						|
    if ((m_vorChannels.size() == 0) || (m_availableChannels->size() == 0)) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    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)
 | 
						|
    {
 | 
						|
        unsigned int nbChannels = unallocatedVORs.size() < (int) deviceChannel.m_channels.size() ?
 | 
						|
            unallocatedVORs.size() :
 | 
						|
            deviceChannel.m_channels.size();
 | 
						|
        std::vector<VORRange> vorRanges;
 | 
						|
 | 
						|
        while (nbChannels != 0)
 | 
						|
        {
 | 
						|
            getVORRanges(unallocatedVORs, nbChannels, vorRanges);
 | 
						|
            filterVORRanges(vorRanges, deviceChannel.m_bandwidth);
 | 
						|
 | 
						|
            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;
 | 
						|
            if (turnPlan.m_fixedCenterFrequency) {
 | 
						|
                devFreq = getDeviceCenterFrequency(turnPlan.m_device.m_deviceIndex);
 | 
						|
            } else {
 | 
						|
                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())
 | 
						|
                {
 | 
						|
                    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;
 | 
						|
            if (turnPlan.m_fixedCenterFrequency) {
 | 
						|
                devFreq = getDeviceCenterFrequency(turnPlan.m_device.m_deviceIndex);
 | 
						|
            } else {
 | 
						|
                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: %p index: %d shift: %d navId: %d",
 | 
						|
                    rrChannel.m_channelAPI, rrChannel.m_channelIndex, rrChannel.m_frequencyShift, rrChannel.m_navId);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    m_rrTurnCounters.resize(deviceCount);
 | 
						|
    std::fill(m_rrTurnCounters.begin(), m_rrTurnCounters.end(), 0);
 | 
						|
    rrNextTurn();
 | 
						|
}
 | 
						|
 | 
						|
void VorLocalizerWorker::setChannelShift(int deviceIndex, int channelIndex, double targetOffset, int vorNavId)
 | 
						|
{
 | 
						|
    SWGSDRangel::SWGChannelSettings channelSettingsResponse;
 | 
						|
    SWGSDRangel::SWGErrorResponse errorResponse;
 | 
						|
    int httpRC;
 | 
						|
 | 
						|
    // Get channel settings containing 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 containing 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 = getDeviceSampleRate(itr->m_deviceSetIndex); // Get b/w of device, not channel, as the latter may be decimated
 | 
						|
        devicesChannelsMap[itr->m_deviceSetIndex].m_channels.push_back(RRChannel{itr->m_channelAPI, itr->m_channelIndex, 0, -1});
 | 
						|
    }
 | 
						|
 | 
						|
    QMap<int, RRTurnPlan>::iterator itm = devicesChannelsMap.begin();
 | 
						|
    devicesChannels.clear();
 | 
						|
 | 
						|
    for (; itm != devicesChannelsMap.end(); ++itm)
 | 
						|
    {
 | 
						|
        itm->m_fixedCenterFrequency = hasCenterFrequencySetting(itm->m_device.m_deviceIndex);
 | 
						|
        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;
 | 
						|
 | 
						|
        if (!rrPlan[turnCount].m_fixedCenterFrequency) {
 | 
						|
            ChannelWebAPIUtils::setCenterFrequency(deviceIndex, deviceFrequency);
 | 
						|
        }
 | 
						|
 | 
						|
        for (auto channel : rrPlan[turnCount].m_channels)
 | 
						|
        {
 | 
						|
            int shift = channel.m_frequencyShift;
 | 
						|
            if (!rrPlan[turnCount].m_fixedCenterFrequency) {
 | 
						|
                shift += m_settings.m_centerShift;
 | 
						|
            }
 | 
						|
 | 
						|
            qDebug() << "VorLocalizerWorker::rrNextTurn: "
 | 
						|
                << "device:" << deviceIndex
 | 
						|
                << "channel:" << channel.m_channelIndex
 | 
						|
                << "shift:" << shift
 | 
						|
                << "navId:" << channel.m_navId;
 | 
						|
            setChannelShift(
 | 
						|
                deviceIndex,
 | 
						|
                channel.m_channelIndex,
 | 
						|
                shift,
 | 
						|
                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);
 | 
						|
    }
 | 
						|
}
 |