1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-19 06:41:47 -05:00
sdrangel/plugins/channelrx/freqscanner/freqscanner.cpp
srcejon a398381aaf Allow ADS-B to send airport ATC frequencies to Frequency Scanner.
Add ATC callsigns.
Add ATC mode, displaying basic info for all aircraft.
Add airport range rings.
Change Device setting to be an AM Demod setting, so AM demod isn't at DC.
Add basic aircraft data to ADSB Web API report.
2023-10-26 16:31:37 +01:00

1120 lines
40 KiB
C++

///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// 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 "freqscanner.h"
#include <QTime>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QThread>
#include <QRegExp>
#include <stdio.h>
#include <complex.h>
#include "SWGChannelSettings.h"
#include "SWGWorkspaceInfo.h"
#include "SWGFreqScannerSettings.h"
#include "SWGChannelReport.h"
#include "device/deviceset.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/morsedemod.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "settings/serializable.h"
#include "util/db.h"
#include "channel/channelwebapiutils.h"
#include "maincore.h"
#include "dsp/spectrumvis.h"
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgConfigureFreqScanner, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportChannels, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgStartScan, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgStopScan, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgScanComplete, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgScanResult, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgStatus, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportActiveFrequency, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportActivePower, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportScanning, Message)
MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportScanRange, Message)
const char * const FreqScanner::m_channelIdURI = "sdrangel.channel.freqscanner";
const char * const FreqScanner::m_channelId = "FreqScanner";
FreqScanner::FreqScanner(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_thread(nullptr),
m_basebandSink(nullptr),
m_running(false),
m_basebandSampleRate(0),
m_scanDeviceSetIndex(-1),
m_scanChannelIndex(-1),
m_state(IDLE),
m_timeoutTimer(this)
{
setObjectName(m_channelId);
applySettings(m_settings, QStringList(), true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&FreqScanner::networkManagerFinished
);
QObject::connect(
this,
&ChannelAPI::indexInDeviceSetChanged,
this,
&FreqScanner::handleIndexInDeviceSetChanged
);
start();
scanAvailableChannels();
QObject::connect(
MainCore::instance(),
&MainCore::channelAdded,
this,
&FreqScanner::handleChannelAdded
);
QObject::connect(
MainCore::instance(),
&MainCore::channelRemoved,
this,
&FreqScanner::handleChannelRemoved
);
m_timeoutTimer.callOnTimeout(this, &FreqScanner::timeout);
}
FreqScanner::~FreqScanner()
{
qDebug("FreqScanner::~FreqScanner");
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&FreqScanner::networkManagerFinished
);
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
stop();
}
void FreqScanner::setDeviceAPI(DeviceAPI *deviceAPI)
{
if (deviceAPI != m_deviceAPI)
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
m_deviceAPI = deviceAPI;
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
}
}
uint32_t FreqScanner::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void FreqScanner::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
(void) firstOfBurst;
if (m_running) {
m_basebandSink->feed(begin, end);
}
}
void FreqScanner::start()
{
QMutexLocker m_lock(&m_mutex);
if (m_running) {
return;
}
qDebug("FreqScanner::start");
m_thread = new QThread();
m_basebandSink = new FreqScannerBaseband(this);
m_basebandSink->setFifoLabel(QString("%1 [%2:%3]")
.arg(m_channelId)
.arg(m_deviceAPI->getDeviceSetIndex())
.arg(getIndexInDeviceSet())
);
m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
m_basebandSink->setChannel(this);
m_basebandSink->moveToThread(m_thread);
QObject::connect(
m_thread,
&QThread::finished,
m_basebandSink,
&QObject::deleteLater
);
QObject::connect(
m_thread,
&QThread::finished,
m_thread,
&QThread::deleteLater
);
m_thread->start();
DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency);
m_basebandSink->getInputMessageQueue()->push(dspMsg);
FreqScannerBaseband::MsgConfigureFreqScannerBaseband *msg = FreqScannerBaseband::MsgConfigureFreqScannerBaseband::create(m_settings, QStringList(), true);
m_basebandSink->getInputMessageQueue()->push(msg);
m_running = true;
}
void FreqScanner::stop()
{
QMutexLocker m_lock(&m_mutex);
if (!m_running) {
return;
}
qDebug("FreqScanner::stop");
m_running = false;
m_thread->exit();
m_thread->wait();
}
bool FreqScanner::handleMessage(const Message& cmd)
{
if (MsgConfigureFreqScanner::match(cmd))
{
MsgConfigureFreqScanner& cfg = (MsgConfigureFreqScanner&) cmd;
qDebug() << "FreqScanner::handleMessage: MsgConfigureFreqScanner";
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
int newSampleRate = notif.getSampleRate();
if ((newSampleRate != m_basebandSampleRate) && (m_state != IDLE))
{
// Restart scan if sample rate changes
startScan();
}
m_basebandSampleRate = newSampleRate;
m_centerFrequency = notif.getCenterFrequency();
qDebug() << "FreqScanner::handleMessage: DSPSignalNotification";
// Forward to the sink
if (m_running)
{
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
m_basebandSink->getInputMessageQueue()->push(rep);
}
// Forward to GUI if any
if (m_guiMessageQueue) {
m_guiMessageQueue->push(new DSPSignalNotification(notif));
}
return true;
}
else if (MsgStartScan::match(cmd))
{
startScan();
return true;
}
else if (MsgStopScan::match(cmd))
{
stopScan();
return true;
}
else if (MsgScanResult::match(cmd))
{
MsgScanResult& result = (MsgScanResult&)cmd;
const QList<MsgScanResult::ScanResult>& results = result.getScanResults();
processScanResults(result.getFFTStartTime(), results);
return true;
}
else
{
return false;
}
}
void FreqScanner::startScan()
{
// Start scan
m_state = START_SCAN;
}
void FreqScanner::stopScan()
{
// Stop scan
m_state = IDLE;
m_timeoutTimer.stop();
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgStatus::create(""));
}
}
void FreqScanner::setDeviceCenterFrequency(qint64 frequency)
{
// For RTL SDR, ChannelWebAPIUtils::setCenterFrequency takes ~50ms, which means tuneTime can be 0
if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), frequency)) {
qWarning() << "Freq Scanner failed to set frequency" << frequency;
}
m_minFFTStartTime = QDateTime::currentDateTime().addMSecs(m_settings.m_tuneTime);
}
void FreqScanner::initScan()
{
ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, true);
if (m_centerFrequency != m_stepStartFrequency) {
setDeviceCenterFrequency(m_stepStartFrequency);
}
m_scanResults.clear();
if (m_guiMessageQueue) {
m_guiMessageQueue->push(FreqScanner::MsgReportScanning::create());
}
m_state = SCAN_FOR_MAX_POWER;
}
void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<MsgScanResult::ScanResult>& results)
{
switch (m_state)
{
case IDLE:
break;
case START_SCAN:
{
// Create ordered list of frequencies to scan
QList<qint64> frequencies;
for (int i = 0; i < m_settings.m_frequencies.size(); i++)
{
if (m_settings.m_enabled[i]) {
frequencies.append(m_settings.m_frequencies[i]);
}
}
std::sort(frequencies.begin(), frequencies.end());
if ((frequencies.size() > 0) && (m_settings.m_channelBandwidth > 0) && (m_basebandSampleRate > 0))
{
// Calculate how many channels can be scanned in one go
int fftSize;
int binsPerChannel;
calcScannerSampleRate(m_settings.m_channelBandwidth, m_basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel);
// Align first frequency so we cover as many channels as possible, while channel guard band
// Can we adjust this to avoid DC bin?
m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_scannerSampleRate * 0.125f;
m_stepStopFrequency = frequencies.back();
// If all frequencies fit within usable bandwidth, we can have the first frequency more central
int totalBW = frequencies.back() - frequencies.front() + 2 * m_settings.m_channelBandwidth;
if (totalBW < m_scannerSampleRate * 0.75f)
{
int spareBWEachSide = (m_scannerSampleRate - totalBW) / 2;
int spareChannelsEachSide = spareBWEachSide / m_settings.m_channelBandwidth;
int offset = spareChannelsEachSide * m_settings.m_channelBandwidth;
m_stepStartFrequency -= offset;
}
initScan();
}
}
break;
case SCAN_FOR_MAX_POWER:
if (fftStartTime >= m_minFFTStartTime)
{
if (results.size() > 0) {
m_scanResults.append(results);
}
// Calculate next center frequency
bool complete = false; // Have all frequencies been scanned?
bool freqInRange = false;
qint64 nextCenterFrequency = m_centerFrequency;
float usableBW = m_scannerSampleRate * 0.75f;
do
{
if (nextCenterFrequency + usableBW / 2 > m_stepStopFrequency)
{
nextCenterFrequency = m_stepStartFrequency;
complete = true;
}
else
{
nextCenterFrequency += usableBW;
complete = false;
}
// Are any frequencies in this new range?
for (int i = 0; i < m_settings.m_frequencies.size(); i++)
{
if (m_settings.m_enabled[i]
&& (m_settings.m_frequencies[i] >= nextCenterFrequency - usableBW / 2)
&& (m_settings.m_frequencies[i] < nextCenterFrequency + usableBW / 2))
{
freqInRange = true;
break;
}
}
}
while (!complete && !freqInRange);
if (complete)
{
if (m_scanResults.size() > 0)
{
// Send scan results to GUI for display in table
if (m_guiMessageQueue)
{
FreqScanner::MsgScanResult* msg = FreqScanner::MsgScanResult::create(QDateTime());
QList<FreqScanner::MsgScanResult::ScanResult>& guiResults = msg->getScanResults();
guiResults.append(m_scanResults);
m_guiMessageQueue->push(msg);
}
int frequency = m_scanResults[0].m_frequency;
Real maxPower = m_scanResults[0].m_power;
if (m_settings.m_priority == FreqScannerSettings::MAX_POWER)
{
// Find frequency with max power
for (int i = 1; i < m_scanResults.size(); i++)
{
if (m_scanResults[i].m_power > maxPower)
{
frequency = m_scanResults[i].m_frequency;
maxPower = m_scanResults[i].m_power;
}
}
}
else
{
// Find first frequency in list above threshold
for (int j = 0; j < m_settings.m_frequencies.size(); j++)
{
for (int i = 0; i < m_scanResults.size(); i++)
{
if (m_scanResults[i].m_frequency == m_settings.m_frequencies[j])
{
if (m_scanResults[i].m_power >= m_settings.m_threshold)
{
frequency = m_scanResults[i].m_frequency;
maxPower = m_scanResults[i].m_power;
goto found_freq;
}
}
}
}
found_freq: ;
}
if (m_settings.m_mode != FreqScannerSettings::SCAN_ONLY)
{
// Is power above threshold
if (maxPower >= m_settings.m_threshold)
{
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgReportActiveFrequency::create(frequency));
}
// Tune device/channel to frequency
int offset;
if ((frequency < m_centerFrequency - usableBW / 2) || (frequency >= m_centerFrequency + usableBW / 2))
{
nextCenterFrequency = frequency;
offset = 0;
}
else
{
nextCenterFrequency = m_centerFrequency;
offset = frequency - m_centerFrequency;
}
// Ensure we have minimum offset from DC
if (offset >= 0)
{
while (offset < m_settings.m_channelFrequencyOffset)
{
nextCenterFrequency -= m_settings.m_channelBandwidth;
offset += m_settings.m_channelBandwidth;
}
}
else
{
while (abs(offset) < m_settings.m_channelFrequencyOffset)
{
nextCenterFrequency += m_settings.m_channelBandwidth;
offset -= m_settings.m_channelBandwidth;
}
}
//qDebug() << "Tuning to active freq:" << frequency << "m_centerFrequency" << m_centerFrequency << "nextCenterFrequency" << nextCenterFrequency << "offset: " << offset << "deviceset: R" << m_scanDeviceSetIndex << ":" << m_scanChannelIndex;
ChannelWebAPIUtils::setFrequencyOffset(m_scanDeviceSetIndex, m_scanChannelIndex, offset);
// Unmute the channel
ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, false);
m_activeFrequency = frequency;
if (m_settings.m_mode == FreqScannerSettings::SINGLE)
{
// Scan complete
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgScanComplete::create());
}
m_state = IDLE;
}
else
{
// Wait for transmission to finish
m_state = WAIT_FOR_END_TX;
}
}
else
{
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgStatus::create(QString("Scanning: No active channels - Max power %1 dB").arg(maxPower, 0, 'f', 1)));
}
}
}
}
}
if (nextCenterFrequency != m_centerFrequency) {
setDeviceCenterFrequency(nextCenterFrequency);
}
if (complete) {
m_scanResults.clear();
}
}
break;
case WAIT_FOR_END_TX:
for (int i = 0; i < results.size(); i++)
{
if (results[i].m_frequency == m_activeFrequency)
{
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgReportActivePower::create(results[i].m_power));
}
// Wait until power drops below threshold
if (results[i].m_power < m_settings.m_threshold)
{
m_timeoutTimer.setSingleShot(true);
m_timeoutTimer.start((int)(m_settings.m_retransmitTime * 1000.0));
m_state = WAIT_FOR_RETRANSMISSION;
break;
}
}
}
break;
case WAIT_FOR_RETRANSMISSION:
for (int i = 0; i < results.size(); i++)
{
if (results[i].m_frequency == m_activeFrequency)
{
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgReportActivePower::create(results[i].m_power));
}
// Check if power has returned to being above threshold
if (results[i].m_power >= m_settings.m_threshold)
{
m_timeoutTimer.stop();
m_state = WAIT_FOR_END_TX;
}
}
}
break;
}
}
void FreqScanner::timeout()
{
// Power hasn't returned above threshold - Restart scan
initScan();
}
void FreqScanner::calcScannerSampleRate(int channelBW, int basebandSampleRate, int& scannerSampleRate, int& fftSize, int& binsPerChannel)
{
const int maxFFTSize = 16384;
const int minBinsPerChannel = 8;
// Base FFT size on that used for main spectrum
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
DeviceSet* deviceSet = deviceSets[m_deviceAPI->getDeviceSetIndex()];
const SpectrumSettings& spectrumSettings = deviceSet->m_spectrumVis->getSettings();
fftSize = spectrumSettings.m_fftSize;
// But ensure we have several bins per channel
// Adjust sample rate, to ensure we don't get massive FFT size
scannerSampleRate = basebandSampleRate;
while (fftSize / (scannerSampleRate / channelBW) < minBinsPerChannel)
{
if (fftSize == maxFFTSize) {
scannerSampleRate /= 2;
} else {
fftSize *= 2;
}
}
binsPerChannel = fftSize / (scannerSampleRate / (float)channelBW);
}
void FreqScanner::setCenterFrequency(qint64 frequency)
{
FreqScannerSettings settings = m_settings;
settings.m_inputFrequencyOffset = frequency;
applySettings(settings, {"inputFrequencyOffset"}, false);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureFreqScanner *msgToGUI = MsgConfigureFreqScanner::create(settings, {"inputFrequencyOffset"}, false);
m_guiMessageQueue->push(msgToGUI);
}
}
void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force)
{
qDebug() << "FreqScanner::applySettings:"
<< settings.getDebugString(settingsKeys, force)
<< " force: " << force;
if (settingsKeys.contains("streamIndex"))
{
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex);
m_deviceAPI->addChannelSink(this, settings.m_streamIndex);
m_deviceAPI->addChannelSinkAPI(this);
}
}
if (settingsKeys.contains("channel") || force)
{
const QRegExp re("R([0-9]+):([0-9]+)");
if (re.indexIn(settings.m_channel) >= 0)
{
m_scanDeviceSetIndex = re.capturedTexts()[1].toInt();
m_scanChannelIndex = re.capturedTexts()[2].toInt();
}
else
{
qDebug() << "FreqScanner::applySettings: Failed to parse channel" << settings.m_channel;
}
}
if (m_running)
{
FreqScannerBaseband::MsgConfigureFreqScannerBaseband *msg = FreqScannerBaseband::MsgConfigureFreqScannerBaseband::create(settings, settingsKeys, force);
m_basebandSink->getInputMessageQueue()->push(msg);
}
if (settingsKeys.contains("useReverseAPI"))
{
bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) ||
settingsKeys.contains("reverseAPIAddress") ||
settingsKeys.contains("reverseAPIPort") ||
settingsKeys.contains("reverseAPIDeviceIndex") ||
settingsKeys.contains("reverseAPIChannelIndex");
webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force);
}
if (settingsKeys.contains("frequencies")
|| settingsKeys.contains("priority")
|| settingsKeys.contains("measurement")
|| settingsKeys.contains("mode")
|| settingsKeys.contains("channelBandwidth")
|| force)
{
// Restart scan if any settings change
if (m_state != IDLE) {
startScan();
}
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
QByteArray FreqScanner::serialize() const
{
return m_settings.serialize();
}
bool FreqScanner::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureFreqScanner *msg = MsgConfigureFreqScanner::create(m_settings, QStringList(), true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureFreqScanner *msg = MsgConfigureFreqScanner::create(m_settings, QStringList(), true);
m_inputMessageQueue.push(msg);
return false;
}
}
int FreqScanner::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings());
response.getFreqScannerSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int FreqScanner::webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIndex(m_settings.m_workspaceIndex);
return 200;
}
int FreqScanner::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
FreqScannerSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureFreqScanner *msg = MsgConfigureFreqScanner::create(settings, channelSettingsKeys, force);
m_inputMessageQueue.push(msg);
qDebug("FreqScanner::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureFreqScanner *msgToGUI = MsgConfigureFreqScanner::create(settings, channelSettingsKeys, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
int FreqScanner::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setFreqScannerReport(new SWGSDRangel::SWGFreqScannerReport());
response.getFreqScannerReport()->init();
webapiFormatChannelReport(response);
return 200;
}
void FreqScanner::webapiUpdateChannelSettings(
FreqScannerSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("channelFrequencyOffset")) {
settings.m_channelFrequencyOffset = response.getFreqScannerSettings()->getChannelFrequencyOffset();
}
if (channelSettingsKeys.contains("channelBandwidth")) {
settings.m_channelBandwidth = response.getFreqScannerSettings()->getChannelBandwidth();
}
if (channelSettingsKeys.contains("threshold")) {
settings.m_threshold = response.getFreqScannerSettings()->getThreshold();
}
if (channelSettingsKeys.contains("frequencies"))
{
settings.m_frequencies.clear();
settings.m_enabled.clear();
settings.m_notes.clear();
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = response.getFreqScannerSettings()->getFrequencies();
if (frequencies)
{
for (const auto frequency : *frequencies)
{
settings.m_frequencies.append(frequency->getFrequency());
settings.m_enabled.append((bool)frequency->getEnabled());
if (frequency->getNotes()) {
settings.m_notes.append(*frequency->getNotes());
} else {
settings.m_notes.append("");
}
}
}
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getFreqScannerSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getFreqScannerSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getFreqScannerSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getFreqScannerSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getFreqScannerSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getFreqScannerSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getFreqScannerSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getFreqScannerSettings()->getReverseApiChannelIndex();
}
if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) {
settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getFreqScannerSettings()->getChannelMarker());
}
if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(channelSettingsKeys, response.getFreqScannerSettings()->getRollupState());
}
}
QList<SWGSDRangel::SWGFreqScannerFrequency *> *FreqScanner::createFrequencyList(const FreqScannerSettings& settings)
{
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = new QList<SWGSDRangel::SWGFreqScannerFrequency *>();
for (int i = 0; i < settings.m_frequencies.size(); i++)
{
SWGSDRangel::SWGFreqScannerFrequency *frequency = new SWGSDRangel::SWGFreqScannerFrequency();
frequency->init();
frequency->setFrequency(settings.m_frequencies[i]);
frequency->setEnabled(settings.m_enabled[i]);
if (!settings.m_notes[i].isEmpty()) {
frequency->setNotes(new QString(settings.m_notes[i]));
}
frequencies->append(frequency);
}
return frequencies;
}
void FreqScanner::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const FreqScannerSettings& settings)
{
response.getFreqScannerSettings()->setChannelFrequencyOffset(settings.m_channelFrequencyOffset);
response.getFreqScannerSettings()->setChannelBandwidth(settings.m_channelBandwidth);
response.getFreqScannerSettings()->setThreshold(settings.m_threshold);
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = createFrequencyList(settings);
if (response.getFreqScannerSettings()->getFrequencies()) {
*response.getFreqScannerSettings()->getFrequencies() = *frequencies;
} else {
response.getFreqScannerSettings()->setFrequencies(frequencies);
}
response.getFreqScannerSettings()->setRgbColor(settings.m_rgbColor);
if (response.getFreqScannerSettings()->getTitle()) {
*response.getFreqScannerSettings()->getTitle() = settings.m_title;
} else {
response.getFreqScannerSettings()->setTitle(new QString(settings.m_title));
}
response.getFreqScannerSettings()->setStreamIndex(settings.m_streamIndex);
response.getFreqScannerSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getFreqScannerSettings()->getReverseApiAddress()) {
*response.getFreqScannerSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getFreqScannerSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getFreqScannerSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getFreqScannerSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getFreqScannerSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
if (settings.m_channelMarker)
{
if (response.getFreqScannerSettings()->getChannelMarker())
{
settings.m_channelMarker->formatTo(response.getFreqScannerSettings()->getChannelMarker());
}
else
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
response.getFreqScannerSettings()->setChannelMarker(swgChannelMarker);
}
}
if (settings.m_rollupState)
{
if (response.getFreqScannerSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getFreqScannerSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getFreqScannerSettings()->setRollupState(swgRollupState);
}
}
}
void FreqScanner::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
response.getFreqScannerReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate());
}
void FreqScanner::webapiReverseSendSettings(const QStringList& channelSettingsKeys, const FreqScannerSettings& settings, bool force)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex)
.arg(settings.m_reverseAPIChannelIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgChannelSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgChannelSettings;
}
void FreqScanner::webapiFormatChannelSettings(
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const FreqScannerSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString("FreqScanner"));
swgChannelSettings->setFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings());
SWGSDRangel::SWGFreqScannerSettings *swgFreqScannerSettings = swgChannelSettings->getFreqScannerSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("channelFrequencyOffset") || force) {
swgFreqScannerSettings->setChannelFrequencyOffset(settings.m_channelFrequencyOffset);
}
if (channelSettingsKeys.contains("channelBandwidth") || force) {
swgFreqScannerSettings->setChannelBandwidth(settings.m_channelBandwidth);
}
if (channelSettingsKeys.contains("threshold") || force) {
swgFreqScannerSettings->setThreshold(settings.m_threshold);
}
if (channelSettingsKeys.contains("frequencies") || force) {
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = createFrequencyList(settings);
if (swgFreqScannerSettings->getFrequencies()) {
*swgFreqScannerSettings->getFrequencies() = *frequencies;
} else {
swgFreqScannerSettings->setFrequencies(frequencies);
}
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgFreqScannerSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgFreqScannerSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgFreqScannerSettings->setStreamIndex(settings.m_streamIndex);
}
if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force))
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
swgFreqScannerSettings->setChannelMarker(swgChannelMarker);
}
if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force))
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
swgFreqScannerSettings->setRollupState(swgRollupState);
}
}
void FreqScanner::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "FreqScanner::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("FreqScanner::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
void FreqScanner::handleIndexInDeviceSetChanged(int index)
{
if (!m_running || (index < 0)) {
return;
}
QString fifoLabel = QString("%1 [%2:%3]")
.arg(m_channelId)
.arg(m_deviceAPI->getDeviceSetIndex())
.arg(index);
m_basebandSink->setFifoLabel(fifoLabel);
}
void FreqScanner::scanAvailableChannels()
{
MainCore* mainCore = MainCore::instance();
std::vector<DeviceSet*>& deviceSets = mainCore->getDeviceSets();
m_availableChannels.clear();
for (const auto& deviceSet : deviceSets)
{
DSPDeviceSourceEngine* deviceSourceEngine = deviceSet->m_deviceSourceEngine;
if (deviceSourceEngine)
{
for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++)
{
ChannelAPI* channel = deviceSet->getChannelAt(chi);
FreqScannerSettings::AvailableChannel availableChannel =
FreqScannerSettings::AvailableChannel{ channel->getDeviceSetIndex(), channel->getIndexInDeviceSet()};
m_availableChannels[channel] = availableChannel;
}
}
}
notifyUpdateChannels();
}
void FreqScanner::handleChannelAdded(int deviceSetIndex, ChannelAPI* channel)
{
qDebug("FreqScanner::handleChannelAdded: deviceSetIndex: %d:%d channel: %s (%p)",
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
DeviceSet* deviceSet = deviceSets[deviceSetIndex];
DSPDeviceSourceEngine* deviceSourceEngine = deviceSet->m_deviceSourceEngine;
if (deviceSourceEngine)
{
FreqScannerSettings::AvailableChannel availableChannel =
FreqScannerSettings::AvailableChannel{ deviceSetIndex, channel->getIndexInDeviceSet()};
m_availableChannels[channel] = availableChannel;
}
notifyUpdateChannels();
}
void FreqScanner::handleChannelRemoved(int deviceSetIndex, ChannelAPI* channel)
{
qDebug("FreqScanner::handleChannelRemoved: deviceSetIndex: %d:%d channel: %s (%p)",
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
DeviceSet* deviceSet = deviceSets[deviceSetIndex];
DSPDeviceSourceEngine* deviceSourceEngine = deviceSet->m_deviceSourceEngine;
if (deviceSourceEngine) {
m_availableChannels.remove(channel);
}
notifyUpdateChannels();
}
void FreqScanner::notifyUpdateChannels()
{
if (getMessageQueueToGUI())
{
MsgReportChannels* msgToGUI = MsgReportChannels::create();
QList<FreqScannerSettings::AvailableChannel>& msgChannels = msgToGUI->getChannels();
QHash<ChannelAPI*, FreqScannerSettings::AvailableChannel>::iterator it = m_availableChannels.begin();
for (; it != m_availableChannels.end(); ++it)
{
FreqScannerSettings::AvailableChannel msgChannel =
FreqScannerSettings::AvailableChannel{
it->m_deviceSetIndex,
it->m_channelIndex
};
msgChannels.push_back(msgChannel);
}
getMessageQueueToGUI()->push(msgToGUI);
}
}