1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-12-22 17:45:48 -05:00

Add ILS demodulator

This commit is contained in:
Jon Beniston 2023-03-21 10:54:42 +00:00
parent d67aac3b27
commit 0bf126baa5
50 changed files with 8666 additions and 5 deletions

View File

@ -83,6 +83,7 @@ option(ENABLE_CHANNELRX_DEMODDSD "Enable channelrx demoddsd plugin" ON)
option(ENABLE_CHANNELRX_DEMODFT8 "Enable channelrx demodft8 plugin" ON)
option(ENABLE_CHANNELRX_DEMODNAVTEX "Enable channelrx demodnavtex plugin" ON)
option(ENABLE_CHANNELRX_DEMODRTTY "Enable channelrx demodrtty plugin" ON)
option(ENABLE_CHANNELRX_DEMODILS "Enable channelrx demodils plugin" ON)
# Channel Tx enablers
option(ENABLE_CHANNELTX "Enable channeltx plugins" ON)

BIN
doc/img/ILSDemod_plugin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -121,6 +121,10 @@ if (ENABLE_CHANNELRX_DEMODRTTY)
add_subdirectory(demodrtty)
endif()
if (ENABLE_CHANNELRX_DEMODILS)
add_subdirectory(demodils)
endif()
if(NOT SERVER_MODE)
add_subdirectory(heatmap)

View File

@ -0,0 +1,63 @@
project(demodils)
set(demodils_SOURCES
ilsdemod.cpp
ilsdemodsettings.cpp
ilsdemodbaseband.cpp
ilsdemodsink.cpp
ilsdemodplugin.cpp
ilsdemodwebapiadapter.cpp
)
set(demodils_HEADERS
ilsdemod.h
ilsdemodsettings.h
ilsdemodbaseband.h
ilsdemodsink.h
ilsdemodplugin.h
ilsdemodwebapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(demodils_SOURCES
${demodils_SOURCES}
ilsdemodgui.cpp
ilsdemodgui.ui
)
set(demodils_HEADERS
${demodils_HEADERS}
ilsdemodgui.h
)
set(TARGET_NAME demodils)
set(TARGET_LIB "Qt::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME demodilssrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${demodils_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

View File

@ -0,0 +1,921 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "ilsdemod.h"
#include <QTime>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QThread>
#include <stdio.h>
#include <complex.h>
#include "SWGChannelSettings.h"
#include "SWGWorkspaceInfo.h"
#include "SWGILSDemodSettings.h"
#include "SWGChannelReport.h"
#include "SWGMapItem.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 "maincore.h"
MESSAGE_CLASS_DEFINITION(ILSDemod::MsgConfigureILSDemod, Message)
MESSAGE_CLASS_DEFINITION(ILSDemod::MsgAngleEstimate, Message)
const char * const ILSDemod::m_channelIdURI = "sdrangel.channel.ilsdemod";
const char * const ILSDemod::m_channelId = "ILSDemod";
ILSDemod::ILSDemod(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_running(false),
m_spectrumVis(SDR_RX_SCALEF),
m_basebandSampleRate(0)
{
setObjectName(m_channelId);
m_basebandSink = new ILSDemodBaseband(this);
m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
m_basebandSink->setChannel(this);
m_basebandSink->moveToThread(&m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&ILSDemod::networkManagerFinished
);
QObject::connect(
this,
&ChannelAPI::indexInDeviceSetChanged,
this,
&ILSDemod::handleIndexInDeviceSetChanged
);
}
ILSDemod::~ILSDemod()
{
qDebug("ILSDemod::~ILSDemod");
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&ILSDemod::networkManagerFinished
);
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
if (m_basebandSink->isRunning()) {
stop();
}
delete m_basebandSink;
}
void ILSDemod::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 ILSDemod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void ILSDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
(void) firstOfBurst;
if (m_running) {
m_basebandSink->feed(begin, end);
}
}
void ILSDemod::start()
{
if (m_running) {
return;
}
qDebug("ILSDemod::start");
m_basebandSink->reset();
m_basebandSink->startWork();
m_basebandSink->setSpectrumSink(&m_spectrumVis);
m_thread.start();
// FIXME: Threading!! Compare to SSB
DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency);
m_basebandSink->getInputMessageQueue()->push(dspMsg);
ILSDemodBaseband::MsgConfigureILSDemodBaseband *msg = ILSDemodBaseband::MsgConfigureILSDemodBaseband::create(m_settings, true);
m_basebandSink->getInputMessageQueue()->push(msg);
m_running = true;
}
void ILSDemod::stop()
{
if (!m_running) {
return;
}
qDebug("ILSDemod::stop");
m_running = false;
m_basebandSink->stopWork();
m_thread.quit();
m_thread.wait();
}
bool ILSDemod::handleMessage(const Message& cmd)
{
if (MsgConfigureILSDemod::match(cmd))
{
MsgConfigureILSDemod& cfg = (MsgConfigureILSDemod&) cmd;
qDebug() << "ILSDemod::handleMessage: MsgConfigureILSDemod";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
m_centerFrequency = notif.getCenterFrequency();
qDebug() << "ILSDemod::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 (MorseDemod::MsgReportIdent::match(cmd))
{
MorseDemod::MsgReportIdent& report = (MorseDemod::MsgReportIdent&) cmd;
// Forward to GUI
if (m_guiMessageQueue)
{
MorseDemod::MsgReportIdent *msg = new MorseDemod::MsgReportIdent(report);
m_guiMessageQueue->push(msg);
}
return true;
}
else if (ILSDemod::MsgAngleEstimate::match(cmd))
{
// Forward to GUI
ILSDemod::MsgAngleEstimate& report = (ILSDemod::MsgAngleEstimate&)cmd;
if (getMessageQueueToGUI())
{
ILSDemod::MsgAngleEstimate *msg = new ILSDemod::MsgAngleEstimate(report);
getMessageQueueToGUI()->push(msg);
}
// Forward via UDP
if (m_settings.m_udpEnabled)
{
QString ddm = QString::number(report.getDDM(), 'f', 3);
QByteArray bytes = ddm.toUtf8();
m_udpSocket.writeDatagram(bytes, bytes.size(),
QHostAddress(m_settings.m_udpAddress), m_settings.m_udpPort);
}
// Write to log file
if (m_logFile.isOpen())
{
float stationLatitude = MainCore::instance()->getSettings().getLatitude();
float stationLongitude = MainCore::instance()->getSettings().getLongitude();
float stationAltitude = MainCore::instance()->getSettings().getAltitude();
QDateTime dateTime = QDateTime::currentDateTime();
m_logStream << dateTime.date().toString()
<< "," << dateTime.time().toString()
<< "," << stationLatitude
<< "," << stationLongitude
<< "," << stationAltitude
<< "," << report.getModDepth90()
<< "," << report.getModDepth150()
<< "," << report.getSDM()
<< "," << report.getDDM()
<< "," << report.getAngle()
<< "," << report.getPowerCarrier()
<< "," << report.getPower90()
<< "," << report.getPower150()
<< "\n";
}
return true;
}
else if (MainCore::MsgChannelDemodQuery::match(cmd))
{
qDebug() << "ILSDemod::handleMessage: MsgChannelDemodQuery";
sendSampleRateToDemodAnalyzer();
return true;
}
else
{
return false;
}
}
ScopeVis *ILSDemod::getScopeSink()
{
return m_basebandSink->getScopeSink();
}
void ILSDemod::setCenterFrequency(qint64 frequency)
{
ILSDemodSettings settings = m_settings;
settings.m_inputFrequencyOffset = frequency;
applySettings(settings, false);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureILSDemod *msgToGUI = MsgConfigureILSDemod::create(settings, false);
m_guiMessageQueue->push(msgToGUI);
}
}
void ILSDemod::applySettings(const ILSDemodSettings& settings, bool force)
{
qDebug() << "ILSDemod::applySettings:"
<< " m_logEnabled: " << settings.m_logEnabled
<< " m_logFilename: " << settings.m_logFilename
<< " m_streamIndex: " << settings.m_streamIndex
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< " force: " << force;
QList<QString> reverseAPIKeys;
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
reverseAPIKeys.append("rfBandwidth");
}
if ((settings.m_mode != m_settings.m_mode) || force) {
reverseAPIKeys.append("mode");
}
if ((settings.m_frequencyIndex != m_settings.m_frequencyIndex) || force) {
reverseAPIKeys.append("frequencyIndex");
}
if ((settings.m_squelch != m_settings.m_squelch) || force) {
reverseAPIKeys.append("squelch");
}
if ((settings.m_volume != m_settings.m_volume) || force) {
reverseAPIKeys.append("volume");
}
if ((settings.m_audioMute != m_settings.m_audioMute) || force) {
reverseAPIKeys.append("audioMute");
}
if ((settings.m_average != m_settings.m_average) || force) {
reverseAPIKeys.append("average");
}
if ((settings.m_ddmUnits != m_settings.m_ddmUnits) || force) {
reverseAPIKeys.append("ddmUnits");
}
if ((settings.m_identThreshold != m_settings.m_identThreshold) || force) {
reverseAPIKeys.append("identThreshold");
}
if ((settings.m_ident != m_settings.m_ident) || force) {
reverseAPIKeys.append("ident");
}
if ((settings.m_runway != m_settings.m_runway) || force) {
reverseAPIKeys.append("runway");
}
if ((settings.m_trueBearing != m_settings.m_trueBearing) || force) {
reverseAPIKeys.append("trueBearing");
}
if ((settings.m_latitude != m_settings.m_latitude) || force) {
reverseAPIKeys.append("latitude");
}
if ((settings.m_longitude != m_settings.m_longitude) || force) {
reverseAPIKeys.append("longitude");
}
if ((settings.m_elevation != m_settings.m_elevation) || force) {
reverseAPIKeys.append("elevation");
}
if ((settings.m_glidePath != m_settings.m_glidePath) || force) {
reverseAPIKeys.append("glidePath");
}
if ((settings.m_refHeight != m_settings.m_refHeight) || force) {
reverseAPIKeys.append("refHeight");
}
if ((settings.m_courseWidth != m_settings.m_courseWidth) || force) {
reverseAPIKeys.append("courseWidth");
}
if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) {
reverseAPIKeys.append("udpEnabled");
}
if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) {
reverseAPIKeys.append("udpAddress");
}
if ((settings.m_udpPort != m_settings.m_udpPort) || force) {
reverseAPIKeys.append("udpPort");
}
if ((settings.m_logFilename != m_settings.m_logFilename) || force) {
reverseAPIKeys.append("logFilename");
}
if ((settings.m_logEnabled != m_settings.m_logEnabled) || force) {
reverseAPIKeys.append("logEnabled");
}
if (m_settings.m_streamIndex != settings.m_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);
}
reverseAPIKeys.append("streamIndex");
}
if (m_running)
{
ILSDemodBaseband::MsgConfigureILSDemodBaseband *msg = ILSDemodBaseband::MsgConfigureILSDemodBaseband::create(settings, force);
m_basebandSink->getInputMessageQueue()->push(msg);
}
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) ||
(m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
if ((settings.m_logEnabled != m_settings.m_logEnabled)
|| (settings.m_logFilename != m_settings.m_logFilename)
|| force)
{
if (m_logFile.isOpen())
{
m_logStream.flush();
m_logFile.close();
}
if (settings.m_logEnabled && !settings.m_logFilename.isEmpty())
{
m_logFile.setFileName(settings.m_logFilename);
if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
{
qDebug() << "ILSDemod::applySettings - Logging to: " << settings.m_logFilename;
m_logStream.setDevice(&m_logFile);
bool newFile = m_logFile.size() == 0;
if (newFile)
{
// Write header
m_logStream << "Date,Time,Latitude,Longitude,Height,MD90,MD150,SDM,DDM,Angle,Carrier(dB),90Hz(dB),150Hz(dB)\n";
}
}
else
{
qDebug() << "ILSDemod::applySettings - Unable to open log file: " << settings.m_logFilename;
}
}
}
m_settings = settings;
}
void ILSDemod::sendSampleRateToDemodAnalyzer()
{
QList<ObjectPipe*> pipes;
MainCore::instance()->getMessagePipes().getMessagePipes(this, "reportdemod", pipes);
if (pipes.size() > 0)
{
for (const auto& pipe : pipes)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(
this,
ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE
);
messageQueue->push(msg);
}
}
}
QByteArray ILSDemod::serialize() const
{
return m_settings.serialize();
}
bool ILSDemod::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureILSDemod *msg = MsgConfigureILSDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureILSDemod *msg = MsgConfigureILSDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
int ILSDemod::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIlsDemodSettings(new SWGSDRangel::SWGILSDemodSettings());
response.getIlsDemodSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int ILSDemod::webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIndex(m_settings.m_workspaceIndex);
return 200;
}
int ILSDemod::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
ILSDemodSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureILSDemod *msg = MsgConfigureILSDemod::create(settings, force);
m_inputMessageQueue.push(msg);
qDebug("ILSDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureILSDemod *msgToGUI = MsgConfigureILSDemod::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
int ILSDemod::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIlsDemodReport(new SWGSDRangel::SWGILSDemodReport());
response.getIlsDemodReport()->init();
webapiFormatChannelReport(response);
return 200;
}
void ILSDemod::webapiUpdateChannelSettings(
ILSDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getIlsDemodSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("rfBandwidth")) {
settings.m_rfBandwidth = response.getIlsDemodSettings()->getRfBandwidth();
}
if (channelSettingsKeys.contains("mode")) {
settings.m_mode = (ILSDemodSettings::Mode) response.getIlsDemodSettings()->getMode();
}
if (channelSettingsKeys.contains("frequencyIndex")) {
settings.m_frequencyIndex = response.getIlsDemodSettings()->getFrequencyIndex();
}
if (channelSettingsKeys.contains("squelch")) {
settings.m_squelch = response.getIlsDemodSettings()->getSquelch();
}
if (channelSettingsKeys.contains("volume")) {
settings.m_volume = response.getIlsDemodSettings()->getVolume();
}
if (channelSettingsKeys.contains("audioMute")) {
settings.m_audioMute = response.getIlsDemodSettings()->getAudioMute();
}
if (channelSettingsKeys.contains("average")) {
settings.m_average = response.getIlsDemodSettings()->getAverage();
}
if (channelSettingsKeys.contains("ddmUnits")) {
settings.m_ddmUnits = (ILSDemodSettings::DDMUnits) response.getIlsDemodSettings()->getDdmUnits();
}
if (channelSettingsKeys.contains("identThreshold")) {
settings.m_identThreshold = response.getIlsDemodSettings()->getIdentThreshold();
}
if (channelSettingsKeys.contains("ident")) {
settings.m_ident = *response.getIlsDemodSettings()->getIdent();
}
if (channelSettingsKeys.contains("runway")) {
settings.m_runway = *response.getIlsDemodSettings()->getRunway();
}
if (channelSettingsKeys.contains("trueBearing")) {
settings.m_trueBearing = response.getIlsDemodSettings()->getTrueBearing();
}
if (channelSettingsKeys.contains("latitude")) {
settings.m_latitude = *response.getIlsDemodSettings()->getLatitude();
}
if (channelSettingsKeys.contains("longitude")) {
settings.m_longitude = *response.getIlsDemodSettings()->getLongitude();
}
if (channelSettingsKeys.contains("elevation")) {
settings.m_elevation = response.getIlsDemodSettings()->getElevation();
}
if (channelSettingsKeys.contains("glidePath")) {
settings.m_glidePath = response.getIlsDemodSettings()->getGlidePath();
}
if (channelSettingsKeys.contains("refHeight")) {
settings.m_refHeight = response.getIlsDemodSettings()->getRefHeight();
}
if (channelSettingsKeys.contains("courseWidth")) {
settings.m_courseWidth = response.getIlsDemodSettings()->getCourseWidth();
}
if (channelSettingsKeys.contains("udpEnabled")) {
settings.m_udpEnabled = response.getIlsDemodSettings()->getUdpEnabled();
}
if (channelSettingsKeys.contains("udpAddress")) {
settings.m_udpAddress = *response.getIlsDemodSettings()->getUdpAddress();
}
if (channelSettingsKeys.contains("udpPort")) {
settings.m_udpPort = response.getIlsDemodSettings()->getUdpPort();
}
if (channelSettingsKeys.contains("logFilename")) {
settings.m_logFilename = *response.getAdsbDemodSettings()->getLogFilename();
}
if (channelSettingsKeys.contains("logEnabled")) {
settings.m_logEnabled = response.getAdsbDemodSettings()->getLogEnabled();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getIlsDemodSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getIlsDemodSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getIlsDemodSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getIlsDemodSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getIlsDemodSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getIlsDemodSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getIlsDemodSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getIlsDemodSettings()->getReverseApiChannelIndex();
}
if (settings.m_scopeGUI && channelSettingsKeys.contains("scopeConfig")) {
settings.m_scopeGUI->updateFrom(channelSettingsKeys, response.getIlsDemodSettings()->getScopeConfig());
}
if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) {
settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getIlsDemodSettings()->getChannelMarker());
}
if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(channelSettingsKeys, response.getIlsDemodSettings()->getRollupState());
}
}
void ILSDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const ILSDemodSettings& settings)
{
response.getIlsDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getIlsDemodSettings()->setRfBandwidth(settings.m_rfBandwidth);
response.getIlsDemodSettings()->setMode((int) settings.m_mode);
response.getIlsDemodSettings()->setFrequencyIndex(settings.m_frequencyIndex);
response.getIlsDemodSettings()->setSquelch(settings.m_squelch);
response.getIlsDemodSettings()->setVolume(settings.m_volume);
response.getIlsDemodSettings()->setAudioMute(settings.m_audioMute);
response.getIlsDemodSettings()->setAverage(settings.m_average);
response.getIlsDemodSettings()->setDdmUnits((int) settings.m_ddmUnits);
response.getIlsDemodSettings()->setIdentThreshold(settings.m_identThreshold);
response.getIlsDemodSettings()->setIdent(new QString(settings.m_ident));
response.getIlsDemodSettings()->setRunway(new QString(settings.m_runway));
response.getIlsDemodSettings()->setTrueBearing(settings.m_trueBearing);
response.getIlsDemodSettings()->setLatitude(new QString(settings.m_latitude));
response.getIlsDemodSettings()->setLatitude(new QString(settings.m_latitude));
response.getIlsDemodSettings()->setElevation(settings.m_elevation);
response.getIlsDemodSettings()->setGlidePath(settings.m_glidePath);
response.getIlsDemodSettings()->setRefHeight(settings.m_refHeight);
response.getIlsDemodSettings()->setCourseWidth(settings.m_courseWidth);
response.getIlsDemodSettings()->setUdpEnabled(settings.m_udpEnabled);
response.getIlsDemodSettings()->setUdpAddress(new QString(settings.m_udpAddress));
response.getIlsDemodSettings()->setUdpPort(settings.m_udpPort);
response.getIlsDemodSettings()->setLogFilename(new QString(settings.m_logFilename));
response.getIlsDemodSettings()->setLogEnabled(settings.m_logEnabled);
response.getIlsDemodSettings()->setRgbColor(settings.m_rgbColor);
if (response.getIlsDemodSettings()->getTitle()) {
*response.getIlsDemodSettings()->getTitle() = settings.m_title;
} else {
response.getIlsDemodSettings()->setTitle(new QString(settings.m_title));
}
response.getIlsDemodSettings()->setStreamIndex(settings.m_streamIndex);
response.getIlsDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getIlsDemodSettings()->getReverseApiAddress()) {
*response.getIlsDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getIlsDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getIlsDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getIlsDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getIlsDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
if (settings.m_scopeGUI)
{
if (response.getIlsDemodSettings()->getScopeConfig())
{
settings.m_scopeGUI->formatTo(response.getIlsDemodSettings()->getScopeConfig());
}
else
{
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
settings.m_scopeGUI->formatTo(swgGLScope);
response.getIlsDemodSettings()->setScopeConfig(swgGLScope);
}
}
if (settings.m_channelMarker)
{
if (response.getIlsDemodSettings()->getChannelMarker())
{
settings.m_channelMarker->formatTo(response.getIlsDemodSettings()->getChannelMarker());
}
else
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
response.getIlsDemodSettings()->setChannelMarker(swgChannelMarker);
}
}
if (settings.m_rollupState)
{
if (response.getIlsDemodSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getIlsDemodSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getIlsDemodSettings()->setRollupState(swgRollupState);
}
}
}
void ILSDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
response.getIlsDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg));
response.getIlsDemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate());
}
void ILSDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const ILSDemodSettings& 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 ILSDemod::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const ILSDemodSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString("ILSDemod"));
swgChannelSettings->setIlsDemodSettings(new SWGSDRangel::SWGILSDemodSettings());
SWGSDRangel::SWGILSDemodSettings *swgILSDemodSettings = swgChannelSettings->getIlsDemodSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgILSDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("rfBandwidth") || force) {
swgILSDemodSettings->setRfBandwidth(settings.m_rfBandwidth);
}
if (channelSettingsKeys.contains("mode") || force) {
swgILSDemodSettings->setMode((int) settings.m_mode);
}
if (channelSettingsKeys.contains("frequencyIndex") || force) {
swgILSDemodSettings->setFrequencyIndex(settings.m_frequencyIndex);
}
if (channelSettingsKeys.contains("squelch") || force) {
swgILSDemodSettings->setSquelch(settings.m_squelch);
}
if (channelSettingsKeys.contains("volume") || force) {
swgILSDemodSettings->setVolume(settings.m_volume);
}
if (channelSettingsKeys.contains("audioMute") || force) {
swgILSDemodSettings->setAudioMute(settings.m_audioMute);
}
if (channelSettingsKeys.contains("average") || force) {
swgILSDemodSettings->setAverage(settings.m_average);
}
if (channelSettingsKeys.contains("ddmUnits") || force) {
swgILSDemodSettings->setDdmUnits((int) settings.m_ddmUnits);
}
if (channelSettingsKeys.contains("identThreshold") || force) {
swgILSDemodSettings->setIdentThreshold(settings.m_identThreshold);
}
if (channelSettingsKeys.contains("ident") || force) {
swgILSDemodSettings->setIdent(new QString(settings.m_ident));
}
if (channelSettingsKeys.contains("runway") || force) {
swgILSDemodSettings->setRunway(new QString(settings.m_runway));
}
if (channelSettingsKeys.contains("trueBearing") || force) {
swgILSDemodSettings->setTrueBearing(settings.m_trueBearing);
}
if (channelSettingsKeys.contains("latitude") || force) {
swgILSDemodSettings->setLatitude(new QString(settings.m_latitude));
}
if (channelSettingsKeys.contains("longitude") || force) {
swgILSDemodSettings->setLongitude(new QString(settings.m_longitude));
}
if (channelSettingsKeys.contains("elevation") || force) {
swgILSDemodSettings->setElevation(settings.m_elevation);
}
if (channelSettingsKeys.contains("glidePath") || force) {
swgILSDemodSettings->setGlidePath(settings.m_glidePath);
}
if (channelSettingsKeys.contains("refHeight") || force) {
swgILSDemodSettings->setRefHeight(settings.m_refHeight);
}
if (channelSettingsKeys.contains("courseWidth") || force) {
swgILSDemodSettings->setCourseWidth(settings.m_courseWidth);
}
if (channelSettingsKeys.contains("udpEnabled") || force) {
swgILSDemodSettings->setUdpEnabled(settings.m_udpEnabled);
}
if (channelSettingsKeys.contains("udpAddress") || force) {
swgILSDemodSettings->setUdpAddress(new QString(settings.m_udpAddress));
}
if (channelSettingsKeys.contains("udpPort") || force) {
swgILSDemodSettings->setUdpPort(settings.m_udpPort);
}
if (channelSettingsKeys.contains("logFilename") || force) {
swgILSDemodSettings->setLogFilename(new QString(settings.m_logFilename));
}
if (channelSettingsKeys.contains("logEnabled") || force) {
swgILSDemodSettings->setLogEnabled(settings.m_logEnabled);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgILSDemodSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgILSDemodSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgILSDemodSettings->setStreamIndex(settings.m_streamIndex);
}
if (settings.m_scopeGUI && (channelSettingsKeys.contains("scopeConfig") || force))
{
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
settings.m_scopeGUI->formatTo(swgGLScope);
swgILSDemodSettings->setScopeConfig(swgGLScope);
}
if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force))
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
swgILSDemodSettings->setChannelMarker(swgChannelMarker);
}
if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force))
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
swgILSDemodSettings->setRollupState(swgRollupState);
}
}
void ILSDemod::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "ILSDemod::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("ILSDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
void ILSDemod::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);
}

View File

@ -0,0 +1,222 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ILSDEMOD_H
#define INCLUDE_ILSDEMOD_H
#include <QNetworkRequest>
#include <QUdpSocket>
#include <QThread>
#include <QFile>
#include <QTextStream>
#include "dsp/basebandsamplesink.h"
#include "dsp/spectrumvis.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "ilsdemodbaseband.h"
#include "ilsdemodsettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
class DeviceAPI;
class ScopeVis;
class ILSDemod : public BasebandSampleSink, public ChannelAPI {
public:
class MsgConfigureILSDemod : public Message {
MESSAGE_CLASS_DECLARATION
public:
const ILSDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureILSDemod* create(const ILSDemodSettings& settings, bool force)
{
return new MsgConfigureILSDemod(settings, force);
}
private:
ILSDemodSettings m_settings;
bool m_force;
MsgConfigureILSDemod(const ILSDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
// Sent from Sink when an estimate is made of the angle
class MsgAngleEstimate : public Message {
MESSAGE_CLASS_DECLARATION
public:
Real getPowerCarrier() const { return m_powerCarrier; }
Real getPower90() const { return m_power90; }
Real getPower150() const { return m_power150; }
Real getModDepth90() const { return m_modDepth90; }
Real getModDepth150() const { return m_modDepth150; }
Real getSDM() const { return m_sdm; }
Real getDDM() const { return m_ddm; }
Real getAngle() const { return m_angle; }
static MsgAngleEstimate* create(Real powerCarrier, Real power90, Real power150, Real modDepth90, Real modDepth150, Real sdm, Real ddm, Real angle)
{
return new MsgAngleEstimate(powerCarrier, power90, power150, modDepth90, modDepth150, sdm, ddm, angle);
}
private:
Real m_powerCarrier;
Real m_power90;
Real m_power150;
Real m_modDepth90;
Real m_modDepth150;
Real m_sdm;
Real m_ddm;
Real m_angle;
MsgAngleEstimate(Real powerCarrier, Real power90, Real power150, Real modDepth90, Real modDepth150, Real sdm, Real ddm, Real angle) :
m_powerCarrier(powerCarrier),
m_power90(power90),
m_power150(power150),
m_modDepth90(modDepth90),
m_modDepth150(modDepth150),
m_sdm(sdm),
m_ddm(ddm),
m_angle(angle)
{}
};
ILSDemod(DeviceAPI *deviceAPI);
virtual ~ILSDemod();
virtual void destroy() { delete this; }
virtual void setDeviceAPI(DeviceAPI *deviceAPI);
virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; }
using BasebandSampleSink::feed;
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
virtual void start();
virtual void stop();
virtual void pushMessage(Message *msg) { m_inputMessageQueue.push(msg); }
virtual QString getSinkName() { return objectName(); }
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual QString getIdentifier() const { return objectName(); }
virtual const QString& getURI() const { return getName(); }
virtual void getTitle(QString& title) { title = m_settings.m_title; }
virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; }
virtual void setCenterFrequency(qint64 frequency);
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int getNbSinkStreams() const { return 1; }
virtual int getNbSourceStreams() const { return 0; }
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
{
(void) streamIndex;
(void) sinkElseSource;
return 0;
}
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage);
static void webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const ILSDemodSettings& settings);
static void webapiUpdateChannelSettings(
ILSDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
SpectrumVis *getSpectrumVis() { return &m_spectrumVis; }
ScopeVis *getScopeSink();
uint32_t getAudioSampleRate() const { return m_running ? m_basebandSink->getAudioSampleRate() : 0; }
bool getSquelchOpen() const { return m_running ? m_basebandSink->getSquelchOpen() : false; }
double getMagSq() const { return m_basebandSink->getMagSq(); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_basebandSink->getMagSqLevels(avg, peak, nbSamples);
}
/* void setMessageQueueToGUI(MessageQueue* queue) override {
ChannelAPI::setMessageQueueToGUI(queue);
m_basebandSink->setMessageQueueToGUI(queue);
}*/
uint32_t getNumberOfDeviceStreams() const;
static const char * const m_channelIdURI;
static const char * const m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread m_thread;
ILSDemodBaseband* m_basebandSink;
bool m_running;
ILSDemodSettings m_settings;
SpectrumVis m_spectrumVis;
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
qint64 m_centerFrequency;
QUdpSocket m_udpSocket;
QFile m_logFile;
QTextStream m_logStream;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
virtual bool handleMessage(const Message& cmd);
void applySettings(const ILSDemodSettings& settings, bool force = false);
void sendSampleRateToDemodAnalyzer();
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const ILSDemodSettings& settings, bool force);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const ILSDemodSettings& settings,
bool force
);
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
private slots:
void networkManagerFinished(QNetworkReply *reply);
void handleIndexInDeviceSetChanged(int index);
};
#endif // INCLUDE_ILSDEMOD_H

View File

@ -0,0 +1,212 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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 <QDebug>
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/downchannelizer.h"
#include "ilsdemodbaseband.h"
MESSAGE_CLASS_DEFINITION(ILSDemodBaseband::MsgConfigureILSDemodBaseband, Message)
ILSDemodBaseband::ILSDemodBaseband(ILSDemod *packetDemod) :
m_sink(packetDemod),
m_running(false)
{
qDebug("ILSDemodBaseband::ILSDemodBaseband");
m_sink.setScopeSink(&m_scopeSink);
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
m_channelizer = new DownChannelizer(&m_sink);
DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue());
m_sink.applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate());
m_channelSampleRate = 0;
}
ILSDemodBaseband::~ILSDemodBaseband()
{
m_inputMessageQueue.clear();
DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo());
delete m_channelizer;
}
void ILSDemodBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
m_sampleFifo.reset();
m_channelSampleRate = 0;
}
void ILSDemodBaseband::startWork()
{
QMutexLocker mutexLocker(&m_mutex);
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&ILSDemodBaseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_running = true;
}
void ILSDemodBaseband::stopWork()
{
QMutexLocker mutexLocker(&m_mutex);
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
QObject::disconnect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&ILSDemodBaseband::handleData
);
m_running = false;
}
void ILSDemodBaseband::setChannel(ChannelAPI *channel)
{
m_sink.setChannel(channel);
}
void ILSDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_sampleFifo.write(begin, end);
}
void ILSDemodBaseband::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0))
{
SampleVector::iterator part1begin;
SampleVector::iterator part1end;
SampleVector::iterator part2begin;
SampleVector::iterator part2end;
std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end);
// first part of FIFO data
if (part1begin != part1end) {
m_channelizer->feed(part1begin, part1end);
}
// second part of FIFO data (used when block wraps around)
if(part2begin != part2end) {
m_channelizer->feed(part2begin, part2end);
}
m_sampleFifo.readCommit((unsigned int) count);
}
}
void ILSDemodBaseband::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool ILSDemodBaseband::handleMessage(const Message& cmd)
{
if (MsgConfigureILSDemodBaseband::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureILSDemodBaseband& cfg = (MsgConfigureILSDemodBaseband&) cmd;
qDebug() << "ILSDemodBaseband::handleMessage: MsgConfigureILSDemodBaseband";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "ILSDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
setBasebandSampleRate(notif.getSampleRate());
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
if (m_channelSampleRate != m_channelizer->getChannelSampleRate())
{
m_sink.applyAudioSampleRate(m_sink.getAudioSampleRate()); // reapply when channel sample rate changes
m_channelSampleRate = m_channelizer->getChannelSampleRate();
}
return true;
}
else
{
return false;
}
}
void ILSDemodBaseband::applySettings(const ILSDemodSettings& settings, bool force)
{
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_channelizer->setChannelization(ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
if (m_channelSampleRate != m_channelizer->getChannelSampleRate())
{
m_sink.applyAudioSampleRate(m_sink.getAudioSampleRate()); // reapply when channel sample rate changes
m_channelSampleRate = m_channelizer->getChannelSampleRate();
}
}
if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force)
{
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName);
//qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex);
audioDeviceManager->removeAudioSink(m_sink.getAudioFifo());
audioDeviceManager->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue(), audioDeviceIndex);
int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex);
if (m_sink.getAudioSampleRate() != audioSampleRate)
{
m_channelizer->setChannelization(audioSampleRate, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
m_sink.applyAudioSampleRate(audioSampleRate);
}
}
m_sink.applySettings(settings, force);
m_settings = settings;
}
int ILSDemodBaseband::getChannelSampleRate() const
{
return m_channelizer->getChannelSampleRate();
}
void ILSDemodBaseband::setBasebandSampleRate(int sampleRate)
{
m_channelizer->setBasebandSampleRate(sampleRate);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}

View File

@ -0,0 +1,110 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ILSDEMODBASEBAND_H
#define INCLUDE_ILSDEMODBASEBAND_H
#include <QObject>
#include <QRecursiveMutex>
#include "dsp/samplesinkfifo.h"
#include "dsp/scopevis.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "ilsdemodsink.h"
class DownChannelizer;
class ChannelAPI;
class ILSDemod;
class ScopeVis;
class SpectrumVis;
class ILSDemodBaseband : public QObject
{
Q_OBJECT
public:
class MsgConfigureILSDemodBaseband : public Message {
MESSAGE_CLASS_DECLARATION
public:
const ILSDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureILSDemodBaseband* create(const ILSDemodSettings& settings, bool force)
{
return new MsgConfigureILSDemodBaseband(settings, force);
}
private:
ILSDemodSettings m_settings;
bool m_force;
MsgConfigureILSDemodBaseband(const ILSDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
ILSDemodBaseband(ILSDemod *packetDemod);
~ILSDemodBaseband();
void reset();
void startWork();
void stopWork();
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_sink.getMagSqLevels(avg, peak, nbSamples);
}
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); }
void setBasebandSampleRate(int sampleRate);
int getChannelSampleRate() const;
void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumVis = spectrumSink; m_sink.setSpectrumSink(spectrumSink); }
ScopeVis *getScopeSink() { return &m_scopeSink; }
void setChannel(ChannelAPI *channel);
bool getSquelchOpen() const { return m_sink.getSquelchOpen(); }
int getAudioSampleRate() const { return m_sink.getAudioSampleRate(); }
double getMagSq() const { return m_sink.getMagSq(); }
bool isRunning() const { return m_running; }
void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); }
void setAudioFifoLabel(const QString& label) { m_sink.setAudioFifoLabel(label); }
private:
SampleSinkFifo m_sampleFifo;
DownChannelizer *m_channelizer;
int m_channelSampleRate;
ILSDemodSink m_sink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
ILSDemodSettings m_settings;
SpectrumVis *m_spectrumVis;
ScopeVis m_scopeSink;
bool m_running;
QRecursiveMutex m_mutex;
bool handleMessage(const Message& cmd);
void calculateOffset(ILSDemodSink *sink);
void applySettings(const ILSDemodSettings& settings, bool force = false);
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_ILSDEMODBASEBAND_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,224 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ILSDEMODGUI_H
#define INCLUDE_ILSDEMODGUI_H
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "dsp/movingaverage.h"
#include "util/messagequeue.h"
#include "settings/rollupstate.h"
#include "ilsdemod.h"
#include "ilsdemodsettings.h"
#include <QGeoCoordinate>
class PluginAPI;
class DeviceUISet;
class BasebandSampleSink;
class ScopeVis;
class SpectrumVis;
class ILSDemod;
class ILSDemodGUI;
namespace Ui {
class ILSDemodGUI;
}
class ILSDemodGUI;
class ILSDemodGUI : public ChannelGUI {
Q_OBJECT
struct ILS {
QString m_airportICAO;
QString m_ident; // ILS identifier
QString m_runway;
int m_frequency; // In Hz
float m_trueBearing; // In degrees
float m_glidePath; // In degrees
double m_latitude; // Position of threshold
double m_longitude;
int m_elevation; // In feet as it is on most charts - FIXME: Meters
float m_refHeight; // ILS reference datum height above threshold
int m_thresholdToLocalizer; // Distance from localizer antenna (GARP) to threshold (LTP)
float m_slope; // In %
};
// Send from G/S channel to LOC channel
class MsgGSAngle : public Message {
MESSAGE_CLASS_DECLARATION
public:
float getAngle() const { return m_angle; }
static MsgGSAngle* create(float angle)
{
return new MsgGSAngle(angle);
}
private:
float m_angle;
MsgGSAngle(float angle) :
m_angle(angle)
{}
};
public:
static ILSDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; };
virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; };
virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; };
virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; };
virtual QString getTitle() const { return m_settings.m_title; };
virtual QColor getTitleColor() const { return m_settings.m_rgbColor; };
virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; }
virtual bool getHidden() const { return m_settings.m_hidden; }
virtual ChannelMarker& getChannelMarker() { return m_channelMarker; }
virtual int getStreamIndex() const { return m_settings.m_streamIndex; }
virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; }
public slots:
void channelMarkerChangedByCursor();
void channelMarkerHighlightedByCursor();
private:
Ui::ILSDemodGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
RollupState m_rollupState;
ILSDemodSettings m_settings;
qint64 m_deviceCenterFrequency;
bool m_doApplySettings;
ScopeVis* m_scopeVis;
SpectrumVis* m_spectrumVis;
ILSDemod* m_ilsDemod;
bool m_squelchOpen;
int m_basebandSampleRate;
uint32_t m_tickCount;
MessageQueue m_inputMessageQueue;
int m_markerNo;
QHash<QString, bool> m_mapMarkers;
QHash<QString, bool> m_mapILS;
bool m_disableDrawILS;
bool m_hasDrawnILS;
bool m_ilsValid;
float m_locLatitude;
float m_locLongitude;
float m_tdLatitude;
float m_tdLongitude;
float m_altitude; // Threshold, in metres
float m_locDistance; // Range of localizer in metres
float m_gsDistance;
float m_locToTouchdown;
float m_locAltitude;
float m_locAngle;
float m_gsAngle;
static const QStringList m_locFrequencies;
static const QStringList m_gsFrequencies;
static const QList<ILSDemodGUI::ILS> m_ils;
explicit ILSDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~ILSDemodGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
bool handleMessage(const Message& message);
void makeUIConnections();
void updateAbsoluteCenterFrequency();
qint64 getFrequency();
QString formatFrequency(int frequency) const;
QString formatDDM(float ddm) const;
QString formatAngleDirection(float angle) const;
void removeFromMap(const QString& name);
void drawILSOnMap();
void drawPath();
void clearILSFromMap();
void addLineToMap(const QString& name, const QString& label, float startLatitude, float startLongitude, float startAltitude, float endLatitude, float endLongitude, float endAltitude);
void addPolygonToMap(const QString& name, const QString& label, const QList<QGeoCoordinate>& coordinates, QRgb color);
void updateGPSAngle();
float calcCourseWidth(int m_thresholdToLocalizer) const;
void leaveEvent(QEvent*);
void enterEvent(EnterEventType*);
bool sendToLOCChannel(float angle);
void closePipes();
void scanAvailableChannels();
void handleChannelAdded(int deviceSetIndex, ChannelAPI *channel);
void handleMessagePipeToBeDeleted(int reason, QObject* object);
void handleChannelMessageQueue(MessageQueue* messageQueue);
QSet<ChannelAPI*> m_availableChannels;
private slots:
void on_deltaFrequency_changed(qint64 value);
void on_rfBW_valueChanged(int index);
void on_mode_currentIndexChanged(int index);
void on_frequency_currentIndexChanged(int index);
void on_average_clicked(bool checked);
void on_thresh_valueChanged(int value);
void on_volume_valueChanged(int value);
void on_squelch_valueChanged(int value);
void on_audioMute_toggled(bool checked);
void on_ddmUnits_currentIndexChanged(int index);
void on_ident_editingFinished();
void on_ident_currentIndexChanged(int index);
void on_runway_editingFinished();
void on_trueBearing_valueChanged(double value);
void on_latitude_editingFinished();
void on_longitude_editingFinished();
void on_elevation_valueChanged(int value);
void on_glidePath_valueChanged(double value);
void on_height_valueChanged(double value);
void on_courseWidth_valueChanged(double value);
void on_slope_valueChanged(double value);
void on_findOnMap_clicked();
void on_clearMarkers_clicked();
void on_addMarker_clicked();
void on_udpEnabled_clicked(bool checked);
void on_udpAddress_editingFinished();
void on_udpPort_editingFinished();
void on_logEnable_clicked(bool checked=false);
void on_logFilename_clicked();
void on_channel1_currentIndexChanged(int index);
void on_channel2_currentIndexChanged(int index);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void audioSelect(const QPoint& p);
void tick();
void preferenceChanged(int elementType);
};
#endif // INCLUDE_ILSDEMODGUI_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 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 <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "ilsdemodgui.h"
#endif
#include "ilsdemod.h"
#include "ilsdemodwebapiadapter.h"
#include "ilsdemodplugin.h"
const PluginDescriptor ILSDemodPlugin::m_pluginDescriptor = {
ILSDemod::m_channelId,
QStringLiteral("ILS Demodulator"),
QStringLiteral("7.12.0"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
ILSDemodPlugin::ILSDemodPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& ILSDemodPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void ILSDemodPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerRxChannel(ILSDemod::m_channelIdURI, ILSDemod::m_channelId, this);
}
void ILSDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
ILSDemod *instance = new ILSDemod(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* ILSDemodPlugin::createRxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSink *rxChannel) const
{
(void) deviceUISet;
(void) rxChannel;
return 0;
}
#else
ChannelGUI* ILSDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return ILSDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif
ChannelWebAPIAdapter* ILSDemodPlugin::createChannelWebAPIAdapter() const
{
return new ILSDemodWebAPIAdapter();
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ILSDEMODPLUGIN_H
#define INCLUDE_ILSDEMODPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class ILSDemodPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.ilsdemod")
public:
explicit ILSDemodPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const;
virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_ILSDEMODPLUGIN_H

View File

@ -0,0 +1,237 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 <QColor>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "ilsdemodsettings.h"
ILSDemodSettings::ILSDemodSettings() :
m_channelMarker(nullptr),
m_spectrumGUI(nullptr),
m_scopeGUI(nullptr),
m_rollupState(nullptr)
{
resetToDefaults();
}
void ILSDemodSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_rfBandwidth = 15000.0f; // 15k to support offset carrier
m_mode = LOC;
m_frequencyIndex = 0;
m_squelch = -60.0;
m_volume = 2.0;
m_audioMute = false;
m_average = false;
m_ddmUnits = FULL_SCALE;
m_identThreshold = 4.0f;
m_ident = "";
m_runway = "";
m_trueBearing = 0.0f;
m_slope = 0.0f;
m_latitude = "";
m_longitude = "";
m_elevation = 0;
m_glidePath = 3.0f;
m_refHeight = 15.25;
m_courseWidth = 4.0f;
m_udpEnabled = false;
m_udpAddress = "127.0.0.1";
m_udpPort = 9999;
m_logFilename = "ils_log.csv";
m_logEnabled = false;
m_scopeCh1 = 0;
m_scopeCh2 = 1;
m_rgbColor = QColor(0, 205, 200).rgb();
m_title = "ILS Demodulator";
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
m_streamIndex = 0;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
m_workspaceIndex = 0;
m_hidden = false;
}
QByteArray ILSDemodSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeFloat(2, m_rfBandwidth);
s.writeS32(3, (int) m_mode);
s.writeS32(4, m_frequencyIndex);
s.writeS32(5, m_squelch);
s.writeFloat(6, m_volume);
s.writeBool(7, m_audioMute);
s.writeBool(8, m_average);
s.writeS32(9, (int) m_ddmUnits);
s.writeFloat(10, m_identThreshold);
s.writeString(11, m_ident);
s.writeString(12, m_runway);
s.writeFloat(13, m_trueBearing);
s.writeFloat(14, m_slope);
s.writeString(15, m_latitude);
s.writeString(16, m_longitude);
s.writeS32(17, m_elevation);
s.writeFloat(18, m_glidePath);
s.writeFloat(19, m_refHeight);
s.writeFloat(20, m_courseWidth);
s.writeBool(21, m_udpEnabled);
s.writeString(22, m_udpAddress);
s.writeU32(23, m_udpPort);
s.writeString(24, m_logFilename);
s.writeBool(25, m_logEnabled);
s.writeS32(26, m_scopeCh1);
s.writeS32(27, m_scopeCh2);
s.writeU32(40, m_rgbColor);
s.writeString(41, m_title);
if (m_channelMarker) {
s.writeBlob(42, m_channelMarker->serialize());
}
s.writeString(43, m_audioDeviceName);
s.writeS32(44, m_streamIndex);
s.writeBool(45, m_useReverseAPI);
s.writeString(46, m_reverseAPIAddress);
s.writeU32(47, m_reverseAPIPort);
s.writeU32(48, m_reverseAPIDeviceIndex);
s.writeU32(49, m_reverseAPIChannelIndex);
if (m_spectrumGUI) {
s.writeBlob(50, m_spectrumGUI->serialize());
}
if (m_scopeGUI) {
s.writeBlob(51, m_scopeGUI->serialize());
}
if (m_rollupState) {
s.writeBlob(52, m_rollupState->serialize());
}
s.writeS32(53, m_workspaceIndex);
s.writeBlob(54, m_geometryBytes);
s.writeBool(55, m_hidden);
return s.final();
}
bool ILSDemodSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
int tmp;
uint32_t utmp;
QString strtmp;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readFloat(2, &m_rfBandwidth, 15000.0f);
d.readS32(3, (int *) &m_mode, (int) LOC);
d.readS32(4, &m_frequencyIndex, 0);
d.readS32(5, &m_squelch, -40);
d.readFloat(6, &m_volume, 2.0f);
d.readBool(7, &m_audioMute, false);
d.readBool(8, &m_average, false);
d.readS32(9, (int *) &m_ddmUnits, (int) FULL_SCALE);
d.readFloat(10, &m_identThreshold, 4.0f);
d.readString(11, &m_ident, "");
d.readString(12, &m_runway, "");
d.readFloat(13, &m_trueBearing, 0.0f);
d.readFloat(14, &m_slope, 0.0f);
d.readString(15, &m_latitude, "");
d.readString(16, &m_longitude, "");
d.readS32(17, &m_elevation, 0);
d.readFloat(18, &m_glidePath, 30.f);
d.readFloat(19, &m_refHeight, 15.25f);
d.readFloat(20, &m_courseWidth, 4.0f);
d.readBool(21, &m_udpEnabled);
d.readString(22, &m_udpAddress);
d.readU32(23, &utmp);
if ((utmp > 1023) && (utmp < 65535)) {
m_udpPort = utmp;
} else {
m_udpPort = 9999;
}
d.readString(24, &m_logFilename, "ils_log.csv");
d.readBool(25, &m_logEnabled, false);
d.readS32(26, &m_scopeCh1, 0);
d.readS32(27, &m_scopeCh2, 0);
d.readU32(40, &m_rgbColor, QColor(0, 205, 200).rgb());
d.readString(41, &m_title, "ILS Demodulator");
if (m_channelMarker)
{
d.readBlob(42, &bytetmp);
m_channelMarker->deserialize(bytetmp);
}
d.readString(43, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
d.readS32(44, &m_streamIndex, 0);
d.readBool(45, &m_useReverseAPI, false);
d.readString(46, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(47, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(48, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(49, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
if (m_spectrumGUI)
{
d.readBlob(50, &bytetmp);
m_spectrumGUI->deserialize(bytetmp);
}
if (m_scopeGUI)
{
d.readBlob(51, &bytetmp);
m_scopeGUI->deserialize(bytetmp);
}
if (m_rollupState)
{
d.readBlob(52, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
d.readS32(28, &m_workspaceIndex, 0);
d.readBlob(29, &m_geometryBytes);
d.readBool(30, &m_hidden, false);
return true;
}
else
{
resetToDefaults();
return false;
}
}

View File

@ -0,0 +1,100 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ILSDEMODSETTINGS_H
#define INCLUDE_ILSDEMODSETTINGS_H
#include <QByteArray>
#include "util/baudot.h"
class Serializable;
struct ILSDemodSettings
{
qint32 m_inputFrequencyOffset;
Real m_rfBandwidth;
enum Mode {
LOC,
GS
} m_mode;
int m_frequencyIndex;
int m_squelch;
Real m_volume;
bool m_audioMute;
bool m_average;
enum DDMUnits {
FULL_SCALE,
PERCENT,
MICROAMPS
} m_ddmUnits;
float m_identThreshold; //!< Linear SNR threshold for Morse demodulator
QString m_ident;
QString m_runway;
float m_trueBearing;
float m_slope; // In %
QString m_latitude; // Of threshold. String, so can support multiple formats
QString m_longitude;
int m_elevation; // Of threshold in feet
float m_glidePath; // In degrees
float m_refHeight; // In metres
float m_courseWidth; // In degrees
bool m_udpEnabled;
QString m_udpAddress;
uint16_t m_udpPort;
QString m_logFilename;
bool m_logEnabled;
int m_scopeCh1;
int m_scopeCh2;
quint32 m_rgbColor;
QString m_title;
Serializable *m_channelMarker;
QString m_audioDeviceName;
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
Serializable *m_spectrumGUI;
Serializable *m_scopeGUI;
Serializable *m_rollupState;
int m_workspaceIndex;
QByteArray m_geometryBytes;
bool m_hidden;
static const int ILSDEMOD_CHANNEL_SAMPLE_RATE = 20480; // 2560*8 - Ident is at 1020. Voice 300/3k. SR chosen so 90/150Hz in middle of bin + 20k b/w for offset-carrier
static const int ILSDEMOD_SPECTRUM_DECIM_LOG2 = 5;
static const int ILSDEMOD_SPECTRUM_SAMPLE_RATE = ILSDEMOD_CHANNEL_SAMPLE_RATE / (1 << ILSDEMOD_SPECTRUM_DECIM_LOG2);
ILSDemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
void setSpectrumGUI(Serializable *spectrumGUI) { m_spectrumGUI = spectrumGUI; }
void setScopeGUI(Serializable *scopeGUI) { m_scopeGUI = scopeGUI; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* INCLUDE_ILSDEMODSETTINGS_H */

View File

@ -0,0 +1,473 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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 <QDebug>
#include <complex.h>
#include "dsp/dspengine.h"
#include "dsp/scopevis.h"
#include "util/stepfunctions.h"
#include "util/db.h"
#include "util/morse.h"
#include "util/units.h"
#include "maincore.h"
#include "ilsdemod.h"
#include "ilsdemodsink.h"
ILSDemodSink::ILSDemodSink(ILSDemod *ilsDemod) :
m_spectrumSink(nullptr),
m_scopeSink(nullptr),
m_ilsDemod(ilsDemod),
m_channel(nullptr),
m_channelSampleRate(ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE),
m_channelFrequencyOffset(0),
m_audioSampleRate(0),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0),
m_messageQueueToChannel(nullptr),
m_fftSequence(-1),
m_fft(nullptr),
m_fftCounter(0),
m_squelchLevel(0.001f),
m_squelchCount(0),
m_squelchOpen(false),
m_squelchDelayLine(9600),
m_volumeAGC(0.003),
m_audioFifo(48000),
m_sampleBufferIndex(0)
{
m_audioBuffer.resize(1<<14);
m_audioBufferFill = 0;
m_magsq = 0.0;
m_sampleBuffer.resize(m_sampleBufferSize);
m_spectrumSampleBuffer.resize(m_sampleBufferSize);
applySettings(m_settings, true);
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
if (m_fftSequence >= 0) {
fftFactory->releaseEngine(m_fftSize, false, m_fftSequence);
}
m_fftSequence = fftFactory->getEngine(m_fftSize, false, &m_fft);
m_fftCounter = 0;
m_fftWindow.create(FFTWindow::Flattop, m_fftSize);
}
ILSDemodSink::~ILSDemodSink()
{
}
void ILSDemodSink::sampleToScope(Complex sample, Real demod)
{
Real r = std::real(sample) * SDR_RX_SCALEF;
Real i = std::imag(sample) * SDR_RX_SCALEF;
m_sampleBuffer[m_sampleBufferIndex] = Sample(r, i);
m_spectrumSampleBuffer[m_sampleBufferIndex] = Sample(demod * SDR_RX_SCALEF, 0);
m_sampleBufferIndex++;
if (m_sampleBufferIndex == m_sampleBufferSize)
{
if (m_scopeSink)
{
std::vector<SampleVector::const_iterator> vbegin;
vbegin.push_back(m_sampleBuffer.begin());
m_scopeSink->feed(vbegin, m_sampleBufferSize);
}
if (m_spectrumSink)
{
m_spectrumSink->feed(m_spectrumSampleBuffer.begin(), m_spectrumSampleBuffer.end(), false);
}
m_sampleBufferIndex = 0;
}
}
void ILSDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
Complex ci;
for (SampleVector::const_iterator it = begin; it != end; ++it)
{
Complex c(it->real(), it->imag());
c *= m_nco.nextIQ();
if (m_interpolatorDistance < 1.0f) // interpolate
{
while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
else // decimate
{
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
}
}
void ILSDemodSink::processOneSample(Complex &ci)
{
Complex ca;
// Calculate average and peak levels for level meter
double magsqRaw = ci.real()*ci.real() + ci.imag()*ci.imag();;
Real magsq = magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED);
m_movingAverage(magsq);
m_magsq = m_movingAverage.asDouble();
m_magsqSum += magsq;
if (magsq > m_magsqPeak)
{
m_magsqPeak = magsq;
}
m_magsqCount++;
ci /= SDR_RX_SCALEF;
// AM demodulation
Complex demod = std::abs(ci);
// Resample as audio
if (m_audioInterpolatorDistance < 1.0f) // interpolate
{
while (!m_audioInterpolator.interpolate(&m_audioInterpolatorDistanceRemain, demod, &ca))
{
processOneAudioSample(ca);
m_audioInterpolatorDistanceRemain += m_audioInterpolatorDistance;
}
}
else // decimate
{
if (m_audioInterpolator.decimate(&m_audioInterpolatorDistanceRemain, demod, &ca))
{
processOneAudioSample(ca);
m_audioInterpolatorDistanceRemain += m_audioInterpolatorDistance;
}
}
// Decimate again for spectral analysis
Complex demodDecim;
if (m_decimator.decimate(demod, demodDecim))
{
// Use FFT to calculate sidebands modulation depth
m_fft->in()[m_fftCounter] = demodDecim;
m_fftCounter++;
if (m_fftCounter == m_fftSize)
{
calcDDM();
m_fftCounter = 0;
// Send results to GUI
if (getMessageQueueToChannel())
{
Real modDepth90, modDepth150, sdm, ddm;
if (m_settings.m_average)
{
modDepth90 = m_modDepth90Average.instantAverage();
modDepth150 = m_modDepth150Average.instantAverage();
sdm = m_sdmAverage.instantAverage();
ddm = m_ddmAverage.instantAverage();
}
else
{
modDepth90 = m_modDepth90;
modDepth150 = m_modDepth150;
sdm = m_sdm;
ddm = m_ddm;
}
Real angle;
if (m_settings.m_mode == ILSDemodSettings::LOC)
{
// For localiser, angle depends on runway length
// At ILS datum (over threshold) (or ILS point B for short runways (<=1200m), which is 1050m from threshold)
// the displacement sensitivity is 0.00145 DDM/metre (3.1.3.7)
// The points at which DDM is 0.155 (i.e a displacement of 0.155/0.00154=~105m) define the course sector (3.1.3.7.3 Note 1)
// And this must be <= 6 degrees (typically between 3-6degrees) (3.1.3.7.1)
// Localilzer to threshold distances (geometric angle)
// EGKK 3150m (3.8deg), EGKB 1840m (6.5deg), EGLL 3960m (3.0deg), EGLC 1570m(27) 1510m(09) (7.6/8deg) EGJJ 1710m (7deg)
// FAS data for EGJJ https://nats-uk.ead-it.com/cms-nats/export/sites/default/en/Publications/AIP/Current-AIRAC/graphics/196515.pdf
// LTP (Landing threshold point) 491231.8010N 0021105.6645W = 49.20883361 -2.18490681
// FPAP 491224.8745N 0021228.7365W = 49.20690958 -2.20798236
// Length offset 136m (distance from near threshold??)
// LTP-FPAP=1690m D=1690+305=1995 (GARP is 305m/1000ft from FPAP)
// EGJJ angle for 1995m = 6deg
angle = ddm / 0.155f * (m_settings.m_courseWidth / 2.0f);
}
else
{
// For glide slope, sector is 0.175 DDM = 0.7 degrees
// Displacement sensitivity 0.0875 at 0.12*theta (0.12*3=0.36deg) (3.1.5.6.2)
// GP coverage is from 0.45*theta to 1.75*theta (5.25-1.35=4.9deg for 3deg GP)
angle = 0.12f * m_settings.m_glidePath * ddm / 0.0875f;
}
ILSDemod::MsgAngleEstimate *msg = ILSDemod::MsgAngleEstimate::create(m_powerCarrier, m_power90, m_power150, modDepth90, modDepth150, sdm, ddm, angle);
getMessageQueueToChannel()->push(msg);
}
}
// Select signals to feed to scope
Complex scopeSample;
switch (m_settings.m_scopeCh1)
{
case 0:
scopeSample.real(ci.real());
break;
case 1:
scopeSample.real(ci.imag());
break;
case 2:
scopeSample.real(demod.real());
break;
}
switch (m_settings.m_scopeCh2)
{
case 0:
scopeSample.imag(ci.real());
break;
case 1:
scopeSample.imag(ci.imag());
break;
case 2:
scopeSample.imag(demod.real());
break;
}
sampleToScope(scopeSample, demod.real());
}
}
void ILSDemodSink::processOneAudioSample(Complex &ci)
{
Real re = ci.real();
Real im = ci.imag();
Real magsq = re*re + im*im;
m_audioMovingAverage(magsq);
double magsqAvg = m_movingAverage.asDouble();
m_squelchDelayLine.write(magsq);
if (magsqAvg < m_squelchLevel)
{
if (m_squelchCount > 0) {
m_squelchCount--;
}
}
else
{
if (m_squelchCount < (unsigned int)m_audioSampleRate / 10) {
m_squelchCount++;
}
}
qint16 sample;
m_squelchOpen = (m_squelchCount >= (unsigned int)m_audioSampleRate / 20);
if (m_squelchOpen && !m_settings.m_audioMute)
{
Real demod;
{
demod = sqrt(m_squelchDelayLine.readBack(m_audioSampleRate/20));
m_volumeAGC.feed(demod);
demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue();
}
demod = m_bandpass.filter(demod);
Real attack = (m_squelchCount - 0.05f * m_audioSampleRate) / (0.05f * m_audioSampleRate);
sample = demod * StepFunctions::smootherstep(attack) * (m_audioSampleRate/24) * m_settings.m_volume;
}
else
{
sample = 0;
}
m_audioBuffer[m_audioBufferFill].l = sample;
m_audioBuffer[m_audioBufferFill].r = sample;
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
{
uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
if (res != m_audioBufferFill)
{
qDebug("ILSDemodSink::processOneAudioSample: %u/%u audio samples written", res, m_audioBufferFill);
m_audioFifo.clear();
}
m_audioBufferFill = 0;
}
m_morseDemod.processOneSample(ci);
}
Real ILSDemodSink::magSq(int bin) const
{
Complex c = m_fft->out()[bin];
Real v = c.real() * c.real() + c.imag() * c.imag();
Real magsq = v / (m_fftSize * m_fftSize);
return magsq;
}
// Calculate the difference in the depth of modulation (DDM)
void ILSDemodSink::calcDDM()
{
// 3.1.3.5.3 - the modulating tones shall be 90 Hz and 150 Hz within plus or minus 2.5 per cent
// At 88/92Hz, some energy is lost in adjacent bin, so we use flat top windowing for accurate
// amplitude measurement, which is what is needed for calculating depth of modulation
m_fftWindow.apply(m_fft->in());
// Perform FFT
m_fft->transform();
// Convert bin to frequency offset
double frequencyResolution = ILSDemodSettings::ILSDEMOD_SPECTRUM_SAMPLE_RATE / (double)m_fftSize;
int bin90 = 90.0 / frequencyResolution;
int bin150 = 150.0 / frequencyResolution;
double mag90, mag150;
double magSqCarrier = magSq(0);
double magCarrier = sqrt(magSqCarrier);
// Add both sidebands
mag90 = sqrt(magSq(bin90)) + sqrt(magSq(m_fftSize-bin90));
mag150 = sqrt(magSq(bin150)) + sqrt(magSq(m_fftSize-bin150));
// Calculate power in dB
m_powerCarrier = CalcDb::dbPower(magSqCarrier);
m_power90 = CalcDb::dbPower(mag90 * mag90);
m_power150 = CalcDb::dbPower(mag150 * mag150);
// Calculate modulation depth as % of carrier
m_modDepth90 = mag90 / magCarrier * 100.0;
m_modDepth150 = mag150 / magCarrier * 100.0;
// Calculate modulation depth difference (https://www.youtube.com/watch?v=71iww_ERoYc)
m_ddm = (m_modDepth90 - m_modDepth150) / 100.0;
// Calculate sum of difference of modulation
m_sdm = (m_modDepth90 + m_modDepth150) / 100.0;
// Calculate moving averages
m_modDepth90Average(m_modDepth90);
m_modDepth150Average(m_modDepth150);
m_sdmAverage(m_sdm);
m_ddmAverage(m_ddm);
}
void ILSDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
qDebug() << "ILSDemodSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
if ((m_channelFrequencyOffset != channelFrequencyOffset) ||
(m_channelSampleRate != channelSampleRate) || force)
{
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
if ((m_channelSampleRate != channelSampleRate) || force)
{
m_interpolator.create(16, channelSampleRate, m_settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) channelSampleRate / (Real) ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void ILSDemodSink::applySettings(const ILSDemodSettings& settings, bool force)
{
qDebug() << "ILSDemodSink::applySettings:"
<< " m_rfBandwidth: " << settings.m_rfBandwidth
<< " m_volume: " << settings.m_volume
<< " m_squelch: " << settings.m_squelch
<< " m_audioMute: " << settings.m_audioMute
<< " m_audioDeviceName: " << settings.m_audioDeviceName
<< " force: " << force;
if ((m_settings.m_squelch != settings.m_squelch) || force) {
m_squelchLevel = CalcDb::powerFromdB(settings.m_squelch);
}
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
{
m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
if ((settings.m_identThreshold != m_settings.m_identThreshold) || force) {
m_morseDemod.applySettings(settings.m_identThreshold);
}
if (force)
{
m_modDepth90Average.reset();
m_modDepth150Average.reset();
m_ddmAverage.reset();
m_decimator.setLog2Decim(ILSDemodSettings::ILSDEMOD_SPECTRUM_DECIM_LOG2);
}
m_settings = settings;
}
void ILSDemodSink::applyAudioSampleRate(int sampleRate)
{
if (sampleRate < 0)
{
qWarning("ILSDemodSink::applyAudioSampleRate: invalid sample rate: %d", sampleRate);
return;
}
qDebug("ILSDemodSink::applyAudioSampleRate: sampleRate: %d channelSampleRate: %d", sampleRate, ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE);
if (sampleRate != m_audioSampleRate)
{
m_audioInterpolator.create(16, ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE, 3500.0f);
m_audioInterpolatorDistanceRemain = 0;
m_audioInterpolatorDistance = (Real) ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE / (Real) sampleRate;
m_bandpass.create(301, sampleRate, 300.0f, 3000.0f);
//m_bandpass.printTaps("audio_bpf");
m_audioFifo.setSize(sampleRate);
m_squelchDelayLine.resize(sampleRate/5);
m_volumeAGC.resizeNew(sampleRate/10, 0.003f);
m_morseDemod.applyChannelSettings(sampleRate);
}
m_audioSampleRate = sampleRate;
}

View File

@ -0,0 +1,170 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ILSDEMODSINK_H
#define INCLUDE_ILSDEMODSINK_H
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "dsp/agc.h"
#include "dsp/firfilter.h"
#include "dsp/fftfactory.h"
#include "dsp/fftengine.h"
#include "dsp/fftwindow.h"
#include "dsp/decimatorc.h"
#include "dsp/morsedemod.h"
#include "audio/audiofifo.h"
#include "util/movingaverage.h"
#include "util/movingmaximum.h"
#include "util/doublebufferfifo.h"
#include "util/messagequeue.h"
#include "ilsdemodsettings.h"
class ChannelAPI;
class ILSDemod;
class ScopeVis;
class SpectrumVis;
class ILSDemodSink : public ChannelSampleSink {
public:
ILSDemodSink(ILSDemod *packetDemod);
~ILSDemodSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void setSpectrumSink(SpectrumVis* spectrumSink) { m_spectrumSink = spectrumSink; }
void setScopeSink(ScopeVis* scopeSink) { m_scopeSink = scopeSink; }
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
void applySettings(const ILSDemodSettings& settings, bool force = false);
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; m_morseDemod.setMessageQueueToChannel(messageQueue); }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
void applyAudioSampleRate(int sampleRate);
int getAudioSampleRate() const { return m_audioSampleRate; }
bool getSquelchOpen() const { return m_squelchOpen; }
AudioFifo *getAudioFifo() { return &m_audioFifo; }
void setAudioFifoLabel(const QString& label) { m_audioFifo.setLabel(label); }
double getMagSq() const { return m_magsq; }
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
{
if (m_magsqCount > 0)
{
m_magsq = m_magsqSum / m_magsqCount;
m_magSqLevelStore.m_magsq = m_magsq;
m_magSqLevelStore.m_magsqPeak = m_magsqPeak;
}
avg = m_magSqLevelStore.m_magsq;
peak = m_magSqLevelStore.m_magsqPeak;
nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount;
m_magsqSum = 0.0f;
m_magsqPeak = 0.0f;
m_magsqCount = 0;
}
private:
struct MagSqLevelsStore
{
MagSqLevelsStore() :
m_magsq(1e-12),
m_magsqPeak(1e-12)
{}
double m_magsq;
double m_magsqPeak;
};
SpectrumVis* m_spectrumSink;
ScopeVis* m_scopeSink; // Scope GUI to display baseband waveform
ILSDemod *m_ilsDemod;
ILSDemodSettings m_settings;
ChannelAPI *m_channel;
int m_channelSampleRate;
int m_channelFrequencyOffset;
int m_audioSampleRate;
NCO m_nco;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
double m_magsq;
double m_magsqSum;
double m_magsqPeak;
int m_magsqCount;
MagSqLevelsStore m_magSqLevelStore;
MessageQueue *m_messageQueueToChannel;
MovingAverageUtil<Real, double, 16> m_movingAverage;
MovingAverageUtil<Real, double, 16> m_audioMovingAverage;
DecimatorC m_decimator;
int m_fftSequence;
FFTEngine *m_fft;
int m_fftCounter;
FFTWindow m_fftWindow;
static const int m_fftSize = 256; // 2.5Hz res (so 90/150Hz are centered in bins - and FT isn't too wide)
Real m_powerCarrier;
Real m_power90;
Real m_power150;
Real m_modDepth90;
Real m_modDepth150;
Real m_sdm;
Real m_ddm;
MovingAverageUtil<Real, Real, 16> m_modDepth90Average; // ~0.5 sec
MovingAverageUtil<Real, Real, 16> m_modDepth150Average;
MovingAverageUtil<Real, Real, 16> m_sdmAverage;
MovingAverageUtil<Real, Real, 16> m_ddmAverage;
Real m_squelchLevel;
uint32_t m_squelchCount;
bool m_squelchOpen;
DoubleBufferFIFO<Real> m_squelchDelayLine;
SimpleAGC<4800> m_volumeAGC;
Bandpass<Real> m_bandpass;
Interpolator m_audioInterpolator;
Real m_audioInterpolatorDistance;
Real m_audioInterpolatorDistanceRemain;
AudioVector m_audioBuffer;
AudioFifo m_audioFifo;
uint32_t m_audioBufferFill;
SampleVector m_sampleBuffer;
static const int m_sampleBufferSize = ILSDemodSettings::ILSDEMOD_CHANNEL_SAMPLE_RATE / 20;
int m_sampleBufferIndex;
SampleVector m_spectrumSampleBuffer;
MorseDemod m_morseDemod;
void processOneSample(Complex &ci);
void processOneAudioSample(Complex &ci);
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
void sampleToScope(Complex sample, Real demod);
void calcDDM();
Real magSq(int bin) const;
};
#endif // INCLUDE_ILSDEMODSINK_H

View File

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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 "SWGChannelSettings.h"
#include "ilsdemod.h"
#include "ilsdemodwebapiadapter.h"
ILSDemodWebAPIAdapter::ILSDemodWebAPIAdapter()
{}
ILSDemodWebAPIAdapter::~ILSDemodWebAPIAdapter()
{}
int ILSDemodWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIlsDemodSettings(new SWGSDRangel::SWGILSDemodSettings());
response.getIlsDemodSettings()->init();
ILSDemod::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int ILSDemodWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force;
(void) errorMessage;
ILSDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ILSDEMOD_WEBAPIADAPTER_H
#define INCLUDE_ILSDEMOD_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "ilsdemodsettings.h"
/**
* Standalone API adapter only for the settings
*/
class ILSDemodWebAPIAdapter : public ChannelWebAPIAdapter {
public:
ILSDemodWebAPIAdapter();
virtual ~ILSDemodWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
private:
ILSDemodSettings m_settings;
};
#endif // INCLUDE_ILSDEMOD_WEBAPIADAPTER_H

View File

@ -0,0 +1,243 @@
<h1>ILS Demodulator Plugin</h1>
<h2>Introduction</h2>
This plugin can be used to demodulate ILS (Instrument Landing System) signals. These are the signals
used by aircraft to perform precision approaches and auto-lands. Details of the demodulated signal are displayed,
such as the DDM (Difference in Depth of Modulation), as well as a visual representation of course line & glide path deviation on a
CDI (Course Deviation Indicator), similar to that used in aircraft.
The ILS localizer course and glide path can be displayed in 3D on the [Map](../../feature/map/readme.md) feature.
![ILS Demodulator on Map](../../../doc/img/ILSDemod_plugin_map.png)
Two independent signals are transmitted as part of ILS on different frequencies: The localizer (LOC) signal (at 108-112MHz) that gives guidance in the horizontal plane
and the glide slope (G/S) signal (at 329-335MHz) that gives guidance in the vertical plane.
Each signal contains 90Hz and 150Hz tones. A phased antenna array is used so that the relative strength of the tones to the carrier varies throughout space.
The tones will be equal (more specifically, the difference in depth of modulation (DDM) will be 0), along the localizer course line or glide path.
When approaching the localizer, the 90Hz tone will be stronger to the left and the 150Hz tone will be stronger to the right. Similarly, the 90Hz tone
will be stronger above the glide path, with the 150Hz being stronger below.
As the LOC and G/S signals are so far apart in frequency, in order to receive both simultaneously, two SDRs, each with their own ILS Demodulator, are required.
If you only have one SDR, you can demodulate either signal independently.
<h2>Interface</h2>
The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md)
![ILS Demodulator plugin GUI](../../../doc/img/ILSDemod_plugin.png)
<h3>1: Frequency shift from center frequency of reception</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2.
<h3>2: Channel power</h3>
Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band.
<h3>3: Level meter in dB</h3>
- top bar (green): average value
- bottom bar (blue green): instantaneous peak value
- tip vertical bar (bright green): peak hold value
<h3>4: Mode</h3>
Specifies whether the ILS Localizer or Glideslope signal is to be demodulated. The localizer provides horizontal guidance and the glideslope vertical.
<h3>5: Frequency</h3>
Specifies the ILS frequency. This will be in the range 108-112 MHz. When a frequency is selected, the device will be tuned to the corresponding frequency.
Localizers use the same frequency as listed on aviation charts for the ILS.
Glideslopes using a paired frequency in the range 329-335MHz range, which is typically not displayed on charts.
<h3>6: RF Bandwidth</h3>
This specifies the bandwidth of a filter that is applied to the input signal to limit the RF bandwidth.
This should be set wide enough to contain the ILS and audio signal.
In some countries, offset carrier can be used, where the same signal is transmitted at multiple offsets. In this case, the
bandwidth should be set wide enough to cover all signals (E.g. ~16kHz).
<h3>7: Ident</h3>
Specifies the identifer for the ILS. This is typically 3 or 4 characters. The drop-down contains a number of identifiers for ILSs at
airports within the South East of the UK. Selecting one of these will automatically fill in the other fields with details of the ILS.
The ILS identifier is broadcast as Morse code at an offset of 1020Hz from the ILS carrier. This is demodulated and displayed below the CDI.
<h3>8: Latitude</h3>
Specifies the latitude of the runway threshold, in decimal degrees (North positive).
<h3>9: Longitude</h3>
Specifies the longitude of the runway threshold, in decimal degrees (East positive).
<h3>10: Elevation</h3>
Specifies the runway threshold elevation in feet. The correct elevation value may differ from the terrain height in the 3D map, depending on which terrain model is used (as set in the 3D map settings), so you may wish
to adjust this until the localizer is nicely displayed on the runway in the 3D map.
<h3>11: Runway</h3>
Specifies the airport ICAO and runway name. (E.g. EGKK 08R).
<h3>12: Bearing</h3>
Specifies the runway bearing in degrees true. This can be calculated from the runway course given on charts by adding the magnetic declination.
<h3>13: Slope</h3>
Specifies the runway slope in %.
<h3>14: Glide Path Angle</h3>
Specifies the glide path angle in degrees. For most ILS approaches, this is 3.0 degrees, but there are some exceptions, such as EGLC which is 5.5 degrees.
<h3>15: RD Height</h3>
Specifies the ILS Reference Datum Height (RDH) above the runway threshold in metres. (Also known as the Threshold Crossing Height (TCH)).
This is typically 15m (50ft) +/-3m (10ft), or 12m (40m) for short runways (<1200m).
<h3>16: Course Width</h3>
Specifies the localizer course width in degrees. This is typically between 3 and 6 degrees, with shorter runways having wider course widths.
<h3>17: UDP</h3>
When checked, the calculated DDM value is forwarded to the specified UDP address (18) and port (19) as a UTF-8 string.
<h3>18: UDP address</h3>
IP address of the host to forward DDM data to via UDP.
<h3>19: UDP port</h3>
UDP port number to forward DDM data to.
<h3>20: Average</h3>
When checked, a moving average filter is applied to the ILS data.
<h3>21: MT - Morse Threshold</h3>
This is the Morse code ident threshold, expressed as a linear signal to noise (SNR) ratio. This is effectively the signal level required for the Morse demodulator to detect a dot or dash. Setting this to low values will allow the Morse demodulator to detect weak signals, but it also increases the likelihood that noise will incorrectly be interpreted as a signal, resulting in invalid ident being reported.
<h3>22: Volume</h3>
This is the volume of the audio signal from 0.0 (mute) to 10.0 (maximum). It can be varied continuously in 0.1 steps using the dial button.
<h3>23: Squelch Threshold</h3>
This is the squelch threshold in dB. The average total power received in the signal bandwidth before demodulation is compared to this value and the squelch input is open above this value. It can be varied continuously in 0.1 dB steps from 0.0 to -100.0 dB using the dial button.
<h3>24: Start/stop Logging Data to .csv File</h3>
When checked, writes demodulated data to the .csv file specified by (25).
<h3>25: .csv Log Filename</h3>
Click to specify the name of the .csv file which data will be logged to.
<h3>26: Add Marker</h3>
Adds a marker to the map at the current GPS position, displaying current ILS data.
<h3>27: Clear Markers</h3>
Clears all markers from the map.
<h3>28: ILS Data</h3>
The ILS data area shows details of the demodulated signal.
<h3>29: DM90Hz</h3>
Displays the depth of modulation of the 90Hz tone as a percentage of the carrier.
<h3>30: DM150Hz</h3>
Displays the depth of modulation of the 150Hz tone as percentage of the carrier.
<h3>31: SDM</h3>
Displays the Sun of the Depth of Modulation of the 90 and 150Hz tones. For LOC, this should be 40%. For G/S it should be 80%.
<h3>32: DDM</h3>
Displays the Difference in the Depth of Modulation of the 90 and 150Hz tones (DDM=(DM90Hz-DM150Hz)/100). When the difference is 0,
the aircraft (or receiving antenna) will be aligned on the course line or glide path. For LOC, a positive DDM indicates the aircraft
is to the left of the course line, or for G/S, above the glide path.
<h3>33: Deviation ILS</h3>
Displays an estimate of the deviation angle based on the calculated DDM. Note that this angle may be very inaccurate for |ddm|>0.155, as outside of this value DDM is not linear with angle.
<h3>34: Deviation GPS</h3>
Displays a deviation angle calculated from GPS position, to be used as a reference in a comparison with the deviation angle computed from the ILS signals (33).
<h3>35: CDI</h3>
The Course Deviation Indicator plots course / glide path deviation in a way similar to that displayed in aircraft.
Full scale deviation is 2.5 degrees (centre to edge) for LOC and 0.35 degrees for G/S.
"LOC" will be displayed in green above the CDI when the localizer is captured (|DDM| < 0.175).
"G/S" will be displayed in green above the CDI when the glide slope is capture (|DDM| < 0.175).
The decoded Morse code identifier will be displayed underneath the CDI in both Morse and letters.
If will be displayed in white if it matches the specified identifer (7) or red if not.
<h3>36: Demodulated Spectrum</h3>
The spectrum displays the demodulated AM spectrum, which should show the carrier, 90Hz and 150Hz sidebands.
<h2>Setting up an ILS</h2>
First, find the approach charts (plates) for the runway/airport, or AIP (Aeronautical Information Publication) with the ILS of interest:
* UK - [NATS AIP](https://nats-uk.ead-it.com/cms-nats/opencms/en/Publications/AIP/)
* Europe - [EUROCONTROL AIP](https://www.ead.eurocontrol.int/cms-eadbasic/opencms/en/login/ead-basic/)
* USA - [FAA DTPP](https://www.faa.gov/air_traffic/flight_info/aeronav/digital_products/dtpp/)
* Flight simmers may have [Navigraph Charts](https://navigraph.com/)
This will contain the ILS identifier (green box), that should be entered in (7) and the frequency (red box) (5). It should also specify the glide path angle (blue box), to be entered in (14). That is typically 3 degrees.
The airport ICAO (purple box) and runway (yellow box) can be entered in (11).
![ILS approach chart](../../../doc/img/ILSDemod_plugin_chart.png)
Next, we need to enter the latitude (8), longitude (9) and elevation (10) of the runway threshold. This is available on some charts (orange box), but not usually accurately enough to line up perfectly on the 3D map.
For this, it's best to use the 3D map, and double click at the start of the threshold to set a marker, which will display the coordinates.
![Runway threshold coordinates](../../../doc/img/ILSDemod_plugin_threshold.png)
The runway bearing should then be set (12) in degrees true. This is the runway course + magnetic declination. The easiest way to set this is just to enter the runway course from the chart (brown box), then visually
adjust the setting until the centre of the localizer (the course line) lines up with the runway centre line markings. Likewise, if necessary, the runway slope (13) can be set visually if needed.
![Localizer runway alignment](../../../doc/img/ILSDemod_plugin_alignment.png)
The ILS Reference Datum Height (RDH) to be set in (15) can often be found in the AIP, and is typically 15m (50ft).
The course width (16) is ocassionaly specified in the AIP.
If not in the AIP, it may be possible to calculate it from an SBAS FAS Data Block if available:
* Calculate the distance between LTP (Landing Threshold Point) and FPAP (Fight Path Alignment Point) from
the coordinates and add 305m to calculate the distance, D, between LTP and GARP (GNSS Azimuth Reference Point).
* With W as the Course Width at the LTP in metres (which is typically 105m),
* Calculate course width angle as 2 * atan(W/D).
Alternatively, the course width angle can also be estimated by measuring the distance D above as the distance from
the the threshold to the localizer antenna, using a tool such as Google Maps (Right click on the map at the threshold and click Measure Distance
then left click on the localizer antenna).
![Threshold to localizer measurement](../../../doc/img/ILSDemod_plugin_thr_to_loc.png)
If D is less than 2000m, the calculated angle will be greater than 6 degrees. In this case, 6 degrees should be used as the
course angle, as this is the specified maximum angle.
It should be noted that the GARP and localizer antenna aren't always coincident.
Finally, you can measure the GPS Deviation angle at the point at which DDM is 0.155, and then the course width is twice that.
(Please feel free to send me your settings so I can add them to the builtin database.)

View File

@ -118,6 +118,7 @@ set(sdrbase_SOURCES
dsp/hbfilterchainconverter.cpp
dsp/hbfiltertraits.cpp
dsp/mimochannel.cpp
dsp/morsedemod.cpp
dsp/nco.cpp
dsp/ncof.cpp
dsp/phaselock.cpp
@ -336,6 +337,7 @@ set(sdrbase_HEADERS
dsp/mimochannel.h
dsp/misc.h
dsp/movingaverage.h
dsp/morsedemod.h
dsp/nco.h
dsp/ncof.h
dsp/phasediscri.h

View File

@ -1177,6 +1177,38 @@ bool ChannelWebAPIUtils::patchFeatureSetting(unsigned int featureSetIndex, unsig
}
}
bool ChannelWebAPIUtils::getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, int &value)
{
SWGSDRangel::SWGFeatureSettings featureSettingsResponse;
Feature *feature;
if (getFeatureSettings(featureSetIndex, featureIndex, featureSettingsResponse, feature))
{
QJsonObject *jsonObj = featureSettingsResponse.asJsonObject();
return WebAPIUtils::getSubObjectInt(*jsonObj, setting, value);
}
else
{
return false;
}
}
bool ChannelWebAPIUtils::getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, QString &value)
{
SWGSDRangel::SWGFeatureSettings featureSettingsResponse;
Feature *feature;
if (getFeatureSettings(featureSetIndex, featureIndex, featureSettingsResponse, feature))
{
QJsonObject *jsonObj = featureSettingsResponse.asJsonObject();
return WebAPIUtils::getSubObjectString(*jsonObj, setting, value);
}
else
{
return false;
}
}
bool ChannelWebAPIUtils::getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, int &value)
{
SWGSDRangel::SWGFeatureReport featureReport;

View File

@ -66,6 +66,8 @@ public:
static bool patchDeviceSetting(unsigned int deviceIndex, const QString &setting, int value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, const QString &value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, int &value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, QString &value);
static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, int &value);
static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, QString &value);
protected:

168
sdrbase/dsp/morsedemod.cpp Normal file
View File

@ -0,0 +1,168 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QDebug>
#include "util/morse.h"
#include "morsedemod.h"
MESSAGE_CLASS_DEFINITION(MorseDemod::MsgReportIdent, Message)
MorseDemod::MorseDemod() :
m_movingAverageIdent(5000),
m_prevBit(0),
m_bitTime(0)
{
}
void MorseDemod::reset()
{
m_binSampleCnt = 0;
m_binCnt = 0;
m_identNoise = 0.0001f;
for (int i = 0; i < m_identBins; i++)
{
m_identMaxs[i] = 0.0f;
}
m_ident = "";
}
void MorseDemod::applyChannelSettings(int channelSampleRate)
{
if (channelSampleRate <= 0) {
return;
}
m_samplesPerDot7wpm = channelSampleRate*60/(50*7);
m_samplesPerDot10wpm = channelSampleRate*60/(50*10);
m_ncoIdent.setFreq(-1020, channelSampleRate); // +-50Hz source offset allowed
m_bandpassIdent.create(1001, channelSampleRate, 970.0f, 1070.0f); // Ident at 1020
m_lowpassIdent.create(301, channelSampleRate, 100.0f);
m_movingAverageIdent.resize(m_samplesPerDot10wpm/5); // Needs to be short enough for noise floor calculation
reset();
}
void MorseDemod::applySettings(int identThreshold)
{
m_identThreshold = identThreshold;
reset();
}
void MorseDemod::processOneSample(const Complex &magc)
{
// Filter to remove voice
Complex c1 = m_bandpassIdent.filter(magc);
// Remove ident sub-carrier offset
c1 *= m_ncoIdent.nextIQ();
// Filter other signals
Complex c2 = std::abs(m_lowpassIdent.filter(c1));
// Filter noise with moving average (moving average preserves edges)
m_movingAverageIdent(c2.real());
Real mav = m_movingAverageIdent.asFloat();
// Caclulate noise floor
if (mav > m_identMaxs[m_binCnt])
m_identMaxs[m_binCnt] = mav;
m_binSampleCnt++;
if (m_binSampleCnt >= m_samplesPerDot10wpm/4)
{
// Calc minimum of maximums
m_identNoise = 1.0f;
for (int i = 0; i < m_identBins; i++)
{
m_identNoise = std::min(m_identNoise, m_identMaxs[i]);
}
m_binSampleCnt = 0;
m_binCnt++;
if (m_binCnt == m_identBins)
m_binCnt = 0;
m_identMaxs[m_binCnt] = 0.0f;
// Prevent divide by zero
if (m_identNoise == 0.0f)
m_identNoise = 1e-20f;
}
// CW demod
int bit = (mav / m_identNoise) >= m_identThreshold;
//m_stream << mav << "," << m_identNoise << "," << bit << "," << (mav / m_identNoise) << "\n";
if ((m_prevBit == 0) && (bit == 1))
{
if (m_bitTime > 7*m_samplesPerDot10wpm)
{
if (m_ident.trimmed().size() > 2) // Filter out noise that may appear as one or two characters
{
qDebug() << "MorseDemod::processOneSample:" << m_ident << " " << Morse::toString(m_ident);
if (getMessageQueueToChannel())
{
MorseDemod::MsgReportIdent *msg = MorseDemod::MsgReportIdent::create(m_ident);
getMessageQueueToChannel()->push(msg);
}
}
m_ident = "";
}
else if (m_bitTime > 2.5*m_samplesPerDot10wpm)
{
m_ident.append(" ");
}
m_bitTime = 0;
}
else if (bit == 1)
{
m_bitTime++;
}
else if ((m_prevBit == 1) && (bit == 0))
{
if (m_bitTime > 2*m_samplesPerDot10wpm)
{
m_ident.append("-");
}
else if (m_bitTime > 0.2*m_samplesPerDot10wpm)
{
m_ident.append(".");
}
m_bitTime = 0;
}
else
{
m_bitTime++;
if (m_bitTime > 10*m_samplesPerDot7wpm)
{
m_ident = m_ident.simplified();
if (m_ident.trimmed().size() > 2) // Filter out noise that may appear as one or two characters
{
qDebug() << "MorseDemod::processOneSample:" << m_ident << " " << Morse::toString(m_ident);
if (getMessageQueueToChannel())
{
MorseDemod::MsgReportIdent *msg = MorseDemod::MsgReportIdent::create(m_ident);
getMessageQueueToChannel()->push(msg);
}
}
m_ident = "";
m_bitTime = 0;
}
}
m_prevBit = bit;
}

87
sdrbase/dsp/morsedemod.h Normal file
View File

@ -0,0 +1,87 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_DSP_MORSEDEMOD_H_
#define SDRBASE_DSP_MORSEDEMOD_H_
#include <QString>
#include "dsp/nco.h"
#include "dsp/firfilter.h"
#include "util/movingaverage.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "export.h"
// Morse code demodulator for use with VOR and ILS
class SDRBASE_API MorseDemod {
public:
class SDRBASE_API MsgReportIdent : public Message {
MESSAGE_CLASS_DECLARATION
public:
QString getIdent() const { return m_ident; }
static MsgReportIdent* create(QString ident)
{
return new MsgReportIdent(ident);
}
private:
QString m_ident;
MsgReportIdent(QString ident) :
Message(),
m_ident(ident)
{
}
};
MorseDemod();
void processOneSample(const Complex &magc);
void applyChannelSettings(int channelSampleRate);
void applySettings(int identThreshold);
void reset();
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; }
MessageQueue *getMessageQueueToChannel() const { return m_messageQueueToChannel; }
private:
MessageQueue *m_messageQueueToChannel;
NCO m_ncoIdent;
Bandpass<Complex> m_bandpassIdent;
Lowpass<Complex> m_lowpassIdent;
MovingAverageUtilVar<Real, double> m_movingAverageIdent;
static const int m_identBins = 20;
Real m_identMaxs[m_identBins];
Real m_identNoise;
int m_binSampleCnt;
int m_binCnt;
int m_samplesPerDot7wpm;
int m_samplesPerDot10wpm;
int m_prevBit;
int m_bitTime;
QString m_ident;
int m_identThreshold;
};
#endif /* SDRBASE_DSP_MORSEDEMOD_H_ */

View File

@ -87,7 +87,7 @@ bool FeatureWebAPIUtils::mapSetDateTime(const QDateTime& dateTime, int featureSe
}
// Get first feature with the given URI
Feature* FeatureWebAPIUtils::getFeature(int featureSetIndex, int featureIndex, const QString& uri)
Feature* FeatureWebAPIUtils::getFeature(int& featureSetIndex, int& featureIndex, const QString& uri)
{
FeatureSet *featureSet;
Feature *feature;
@ -116,13 +116,16 @@ Feature* FeatureWebAPIUtils::getFeature(int featureSetIndex, int featureIndex, c
else
{
// Find first feature matching URI
for (std::vector<FeatureSet*>::const_iterator it = featureSets.begin(); it != featureSets.end(); ++it, featureIndex++)
int fsi = 0;
for (std::vector<FeatureSet*>::const_iterator it = featureSets.begin(); it != featureSets.end(); ++it, ++fsi)
{
for (int fi = 0; fi < (*it)->getNumberOfFeatures(); fi++)
{
feature = (*it)->getFeatureAt(fi);
if (feature->getURI() == uri)
{
featureSetIndex = fsi;
featureIndex = fi;
return feature;
}
}

View File

@ -29,7 +29,7 @@ class SDRBASE_API FeatureWebAPIUtils
public:
static bool mapFind(const QString& target, int featureSetIndex=-1, int featureIndex=-1);
static bool mapSetDateTime(const QDateTime& dateTime, int featureSetIndex=-1, int featureIndex=-1);
static Feature *getFeature(int featureSetIndex, int featureIndex, const QString& uri);
static Feature *getFeature(int& featureSetIndex, int& featureIndex, const QString& uri);
static bool satelliteAOS(const QString name, const QDateTime aos, const QDateTime los);
static bool satelliteLOS(const QString name);
};

View File

@ -268,11 +268,11 @@ public:
// We support both decimal and DMS formats
static bool stringToLatitudeAndLongitude(const QString& string, float& latitude, float& longitude)
{
QRegExp decimal("(-?[0-9]+\\.[0-9]+) *,? *(-?[0-9]+\\.[0-9]+)");
QRegExp decimal("(-?[0-9]+(\\.[0-9]+)?) *,? *(-?[0-9]+(\\.[0-9]+)?)");
if (decimal.exactMatch(string))
{
latitude = decimal.capturedTexts()[1].toFloat();
longitude = decimal.capturedTexts()[2].toFloat();
longitude = decimal.capturedTexts()[3].toFloat();
return true;
}
QRegExp dms(QString("([0-9]+)[%1d]([0-9]+)['m]([0-9]+(\\.[0-9]+)?)[\"s]([NS]) *,? *([0-9]+)[%1d]([0-9]+)['m]([0-9]+(\\.[0-9]+)?)[\"s]([EW])").arg(QChar(0xb0)));
@ -313,6 +313,26 @@ public:
longitude = -longitude;
return true;
}
// 512255.5900N 0024400.6105W as used on aviation charts
QRegExp dms3(QString("(\\d{2})(\\d{2})((\\d{2})(\\.\\d+)?)([NS]) *,?(\\d{3})(\\d{2})((\\d{2})(\\.\\d+)?)([EW])"));
if (dms3.exactMatch(string))
{
float latD = dms3.capturedTexts()[1].toFloat();
float latM = dms3.capturedTexts()[2].toFloat();
float latS = dms3.capturedTexts()[3].toFloat();
bool north = dms3.capturedTexts()[6] == "N";
float lonD = dms3.capturedTexts()[7].toFloat();
float lonM = dms3.capturedTexts()[8].toFloat();
float lonS = dms3.capturedTexts()[9].toFloat();
bool east = dms3.capturedTexts()[12] == "E";
latitude = latD + latM/60.0 + latS/(60.0*60.0);
if (!north)
latitude = -latitude;
longitude = lonD + lonM/60.0 + lonS/(60.0*60.0);
if (!east)
longitude = -longitude;
return true;
}
return false;
}

View File

@ -4538,6 +4538,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setIeee802154ModSettings(new SWGSDRangel::SWGIEEE_802_15_4_ModSettings());
channelSettings->getIeee802154ModSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "ILSDemodSettings")
{
channelSettings->setIlsDemodSettings(new SWGSDRangel::SWGILSDemodSettings());
channelSettings->getIlsDemodSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "InterferometerSettings")
{
channelSettings->setInterferometerSettings(new SWGSDRangel::SWGInterferometerSettings());
@ -5392,6 +5397,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings&
channelSettings.setDsdDemodSettings(nullptr);
channelSettings.setHeatMapSettings(nullptr);
channelSettings.setIeee802154ModSettings(nullptr);
channelSettings.setIlsDemodSettings(nullptr);
channelSettings.setNavtexDemodSettings(nullptr);
channelSettings.setNfmDemodSettings(nullptr);
channelSettings.setNfmModSettings(nullptr);
@ -5430,6 +5436,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan
channelReport.setDatvModReport(nullptr);
channelReport.setDsdDemodReport(nullptr);
channelReport.setHeatMapReport(nullptr);
channelReport.setIlsDemodReport(nullptr);
channelReport.setNavtexDemodReport(nullptr);
channelReport.setNfmDemodReport(nullptr);
channelReport.setNfmModReport(nullptr);

View File

@ -48,6 +48,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"sdrangel.channeltx.freedvmod", "FreeDVModSettings"},
{"sdrangel.channel.freqtracker", "FreqTrackerSettings"},
{"sdrangel.channel.heatmap", "HeatMapSettings"},
{"sdrangel.channel.ilsdemod", "ILSDemodSettings"},
{"sdrangel.channel.navtexemod", "NavtexDemodSettings"},
{"sdrangel.channel.m17demod", "M17DemodSettings"},
{"sdrangel.channeltx.modm17", "M17ModSettings"},

View File

@ -21,6 +21,7 @@ set(sdrgui_SOURCES
gui/commandsdialog.cpp
gui/commandoutputdialog.cpp
gui/configurationsdialog.cpp
gui/coursedeviationindicator.cpp
gui/crightclickenabler.cpp
gui/customtextedit.cpp
gui/cwkeyergui.cpp
@ -130,6 +131,7 @@ set(sdrgui_HEADERS
gui/commandsdialog.h
gui/commandoutputdialog.h
gui/configurationsdialog.h
gui/coursedeviationindicator.h
gui/crightclickenabler.h
gui/customtextedit.h
gui/cwkeyergui.h

View File

@ -0,0 +1,179 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QPainter>
#include "coursedeviationindicator.h"
CourseDeviationIndicator::CourseDeviationIndicator(QWidget *parent) :
QWidget(parent),
m_localizerDDM(0.0f),
m_glideSlopeDDM(0.0f)
{
}
void CourseDeviationIndicator::setMode(Mode mode)
{
m_mode = mode;
update();
}
void CourseDeviationIndicator::setLocalizerDDM(float ddm)
{
m_localizerDDM = ddm;
update();
}
void CourseDeviationIndicator::setGlideSlopeDDM(float ddm)
{
m_glideSlopeDDM = ddm;
update();
}
void CourseDeviationIndicator::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QRect r = rect();
int midW = r.width() / 2;
int midH = r.height() / 2;
int spacing;
// A320 like CDI
// Black background
int bgw, bgh;
if (m_mode == LOC)
{
bgw = r.width();
bgh = 20;
painter.fillRect(0, midH - bgh, bgw, bgh*2, QColor(0, 0, 0));
}
else
{
bgw = 20;
bgh = r.height();
painter.fillRect(midW - bgw, 0, bgw*2, bgh, QColor(0, 0, 0));
}
const int dots = 5;
// Circles
painter.setPen(QColor(255, 255, 255));
const int radius = 4;
int x, y;
if (m_mode == LOC)
{
spacing = r.width() / 5;
x = spacing / 2;
y = midH;
}
else
{
spacing = r.height() / 5;
x = midW;
y = spacing / 2;
}
for (int i = 0; i < dots; i++)
{
if (i != 2) {
painter.drawEllipse(QPointF(x, y), radius, radius);
}
if (m_mode == LOC) {
x += spacing;
} else {
y += spacing;
}
}
// Diamond (index) - only draw half of symbol if out of range
// Shouldn't draw the symbol if signal not vaiid
// Typically, LOC full scale deflection 0.155 DDM (Which is ~2.5deg, so 1 deg per dot, but can be 3 degrees. A320 is 0.8 deg per dot)
// For GS, full deflection is 0.0875 DDM (0.7deg, so 0.14 deg per dot)
painter.setPen(QColor(255, 150, 250));
float dev;
if (m_mode == LOC) {
dev = m_localizerDDM / 0.155;
} else {
dev = m_glideSlopeDDM / 0.0875;
}
dev = std::min(dev, 1.0f);
dev = std::max(dev, -1.0f);
if (m_mode == LOC)
{
x = midW + dev * r.width() / 2; // Positive DDM means we're to left of course line
y = midH;
}
else
{
x = midW;
y = midH + dev * r.height() / 2; // Positive DDM means we're above glide path
}
int dw = 10;
int dh = 8;
painter.drawLine(x, y + dh, x - dw, y);
painter.drawLine(x - dw, y, x, y - dh);
painter.drawLine(x + dw, y, x, y - dh);
painter.drawLine(x, y + dh, x + dw, y);
// Centre line
painter.setPen(QColor(255, 255, 70));
if (m_mode == LOC)
{
int lh = 14;
painter.drawLine(midW, midH + lh, midW, midH - lh);
painter.drawLine(midW-1, midH + lh, midW-1, midH - lh);
painter.drawLine(midW+1, midH + lh, midW+1, midH - lh);
}
else
{
int lw = 14;
painter.drawLine(midW + lw, midH, midW - lw, midH);
painter.drawLine(midW + lw, midH - 1, midW - lw, midH - 1);
painter.drawLine(midW + lw, midH + 1, midW - lw, midH + 1);
}
if (m_mode == LOC)
{
// Indicate localizer capture
if (std::abs(m_localizerDDM) < 0.175) // See 3.1.3.7.4
{
QFontMetrics fm(painter.font());
QString text = "LOC";
int tw = fm.horizontalAdvance(text);
int th = fm.descent();
painter.setPen(QColor(0, 255, 0));
painter.drawText(midW - tw/2, midH - bgh - th, text);
}
}
else
{
// Indicate glideslope capture
if (std::abs(m_glideSlopeDDM) < 0.175) // Can't see a spec for this
{
QFontMetrics fm(painter.font());
QString text = "G/S";
int tw = fm.horizontalAdvance(text);
int th = fm.ascent() / 2;
painter.setPen(QColor(0, 255, 0));
painter.drawText(midW + bgw + 2, midH + th, text);
}
}
}

View File

@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRGUI_GUI_COURSEDEVIATIONINDICATOR_H
#define SDRGUI_GUI_COURSEDEVIATIONINDICATOR_H
#include <QWidget>
#include "export.h"
// Aircraft Course Deviation Indicator (CDI)
class SDRGUI_API CourseDeviationIndicator : public QWidget {
Q_OBJECT
public:
enum Mode {
LOC,
GS,
// TODO: BOTH
};
explicit CourseDeviationIndicator(QWidget *parent = nullptr);
void setLocalizerDDM(float ddm);
float getLocazlierDDM() const { return m_localizerDDM; }
void setGlideSlopeDDM(float ddm);
float getGlideSlopeDDM() const { return m_glideSlopeDDM; }
void setMode(Mode mode);
Mode getMode() const { return m_mode; }
void paintEvent(QPaintEvent *event);
protected:
private:
float m_localizerDDM;
float m_glideSlopeDDM;
Mode m_mode;
private slots:
};
#endif // SDRGUI_GUI_COURSEDEVIATIONINDICATOR_H

View File

@ -55,6 +55,8 @@ ChannelReport:
$ref: "http://swgserver:8081/api/swagger/include/RTTYDemod.yaml#/RTTYDemodReport"
HeatMapReport:
$ref: "http://swgserver:8081/api/swagger/include/HeatMap.yaml#/HeatMapReport"
ILSDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/ILSDemod.yaml#/ILSDemodReport"
M17DemodReport:
$ref: "http://swgserver:8081/api/swagger/include/M17Demod.yaml#/M17DemodReport"
M17ModReport:

View File

@ -69,6 +69,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/RTTYDemod.yaml#/RTTYDemodSettings"
HeatMapSettings:
$ref: "http://swgserver:8081/api/swagger/include/HeatMap.yaml#/HeatMapSettings"
ILSDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/ILSDemod.yaml#/ILSDemodSettings"
InterferometerSettings:
$ref: "http://swgserver:8081/api/swagger/include/Interferometer.yaml#/InterferometerSettings"
IEEE_802_15_4_ModSettings:

View File

@ -0,0 +1,98 @@
ILSDemodSettings:
description: ILSDemod
properties:
inputFrequencyOffset:
type: integer
format: int64
rfBandwidth:
type: number
format: float
mode:
description: "(0 for LOC, 1 for G/S)"
type: integer
frequencyIndex:
type: integer
squelch:
type: integer
volume:
type: number
format: float
audioMute:
type: integer
average:
type: integer
ddmUnits:
type: integer
identThreshold:
type: number
format: float
ident:
type: string
runway:
type: string
trueBearing:
type: number
format: float
latitude:
type: string
longitude:
type: string
elevation:
type: integer
glidePath:
type: number
format: float
refHeight:
type: number
format: float
courseWidth:
type: number
format: float
udpEnabled:
description: "Whether to forward DDM to specified UDP port"
type: integer
udpAddress:
description: "UDP address to forward DDM to"
type: string
udpPort:
description: "UDP port to forward DDM to"
type: integer
logFilename:
type: string
logEnabled:
type: integer
rgbColor:
type: integer
title:
type: string
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
scopeConfig:
$ref: "http://swgserver:8081/api/swagger/include/GLScope.yaml#/GLScope"
channelMarker:
$ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker"
rollupState:
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"
ILSDemodReport:
description: ILSDemod
properties:
channelPowerDB:
description: power received in channel (dB)
type: number
format: float
channelSampleRate:
type: integer

View File

@ -76,6 +76,8 @@ SWGChannelReport::SWGChannelReport() {
m_rtty_demod_report_isSet = false;
heat_map_report = nullptr;
m_heat_map_report_isSet = false;
ils_demod_report = nullptr;
m_ils_demod_report_isSet = false;
m17_demod_report = nullptr;
m_m17_demod_report_isSet = false;
m17_mod_report = nullptr;
@ -174,6 +176,8 @@ SWGChannelReport::init() {
m_rtty_demod_report_isSet = false;
heat_map_report = new SWGHeatMapReport();
m_heat_map_report_isSet = false;
ils_demod_report = new SWGILSDemodReport();
m_ils_demod_report_isSet = false;
m17_demod_report = new SWGM17DemodReport();
m_m17_demod_report_isSet = false;
m17_mod_report = new SWGM17ModReport();
@ -290,6 +294,9 @@ SWGChannelReport::cleanup() {
if(heat_map_report != nullptr) {
delete heat_map_report;
}
if(ils_demod_report != nullptr) {
delete ils_demod_report;
}
if(m17_demod_report != nullptr) {
delete m17_demod_report;
}
@ -414,6 +421,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&heat_map_report, pJson["HeatMapReport"], "SWGHeatMapReport", "SWGHeatMapReport");
::SWGSDRangel::setValue(&ils_demod_report, pJson["ILSDemodReport"], "SWGILSDemodReport", "SWGILSDemodReport");
::SWGSDRangel::setValue(&m17_demod_report, pJson["M17DemodReport"], "SWGM17DemodReport", "SWGM17DemodReport");
::SWGSDRangel::setValue(&m17_mod_report, pJson["M17ModReport"], "SWGM17ModReport", "SWGM17ModReport");
@ -544,6 +553,9 @@ SWGChannelReport::asJsonObject() {
if((heat_map_report != nullptr) && (heat_map_report->isSet())){
toJsonValue(QString("HeatMapReport"), heat_map_report, obj, QString("SWGHeatMapReport"));
}
if((ils_demod_report != nullptr) && (ils_demod_report->isSet())){
toJsonValue(QString("ILSDemodReport"), ils_demod_report, obj, QString("SWGILSDemodReport"));
}
if((m17_demod_report != nullptr) && (m17_demod_report->isSet())){
toJsonValue(QString("M17DemodReport"), m17_demod_report, obj, QString("SWGM17DemodReport"));
}
@ -851,6 +863,16 @@ SWGChannelReport::setHeatMapReport(SWGHeatMapReport* heat_map_report) {
this->m_heat_map_report_isSet = true;
}
SWGILSDemodReport*
SWGChannelReport::getIlsDemodReport() {
return ils_demod_report;
}
void
SWGChannelReport::setIlsDemodReport(SWGILSDemodReport* ils_demod_report) {
this->ils_demod_report = ils_demod_report;
this->m_ils_demod_report_isSet = true;
}
SWGM17DemodReport*
SWGChannelReport::getM17DemodReport() {
return m17_demod_report;
@ -1138,6 +1160,9 @@ SWGChannelReport::isSet(){
if(heat_map_report && heat_map_report->isSet()){
isObjectUpdated = true; break;
}
if(ils_demod_report && ils_demod_report->isSet()){
isObjectUpdated = true; break;
}
if(m17_demod_report && m17_demod_report->isSet()){
isObjectUpdated = true; break;
}

View File

@ -43,6 +43,7 @@
#include "SWGFreqTrackerReport.h"
#include "SWGHeatMapReport.h"
#include "SWGIEEE_802_15_4_ModReport.h"
#include "SWGILSDemodReport.h"
#include "SWGM17DemodReport.h"
#include "SWGM17ModReport.h"
#include "SWGNFMDemodReport.h"
@ -157,6 +158,9 @@ public:
SWGHeatMapReport* getHeatMapReport();
void setHeatMapReport(SWGHeatMapReport* heat_map_report);
SWGILSDemodReport* getIlsDemodReport();
void setIlsDemodReport(SWGILSDemodReport* ils_demod_report);
SWGM17DemodReport* getM17DemodReport();
void setM17DemodReport(SWGM17DemodReport* m17_demod_report);
@ -296,6 +300,9 @@ private:
SWGHeatMapReport* heat_map_report;
bool m_heat_map_report_isSet;
SWGILSDemodReport* ils_demod_report;
bool m_ils_demod_report_isSet;
SWGM17DemodReport* m17_demod_report;
bool m_m17_demod_report_isSet;

View File

@ -88,6 +88,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_rtty_demod_settings_isSet = false;
heat_map_settings = nullptr;
m_heat_map_settings_isSet = false;
ils_demod_settings = nullptr;
m_ils_demod_settings_isSet = false;
interferometer_settings = nullptr;
m_interferometer_settings_isSet = false;
ieee_802_15_4_mod_settings = nullptr;
@ -210,6 +212,8 @@ SWGChannelSettings::init() {
m_rtty_demod_settings_isSet = false;
heat_map_settings = new SWGHeatMapSettings();
m_heat_map_settings_isSet = false;
ils_demod_settings = new SWGILSDemodSettings();
m_ils_demod_settings_isSet = false;
interferometer_settings = new SWGInterferometerSettings();
m_interferometer_settings_isSet = false;
ieee_802_15_4_mod_settings = new SWGIEEE_802_15_4_ModSettings();
@ -352,6 +356,9 @@ SWGChannelSettings::cleanup() {
if(heat_map_settings != nullptr) {
delete heat_map_settings;
}
if(ils_demod_settings != nullptr) {
delete ils_demod_settings;
}
if(interferometer_settings != nullptr) {
delete interferometer_settings;
}
@ -506,6 +513,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&heat_map_settings, pJson["HeatMapSettings"], "SWGHeatMapSettings", "SWGHeatMapSettings");
::SWGSDRangel::setValue(&ils_demod_settings, pJson["ILSDemodSettings"], "SWGILSDemodSettings", "SWGILSDemodSettings");
::SWGSDRangel::setValue(&interferometer_settings, pJson["InterferometerSettings"], "SWGInterferometerSettings", "SWGInterferometerSettings");
::SWGSDRangel::setValue(&ieee_802_15_4_mod_settings, pJson["IEEE_802_15_4_ModSettings"], "SWGIEEE_802_15_4_ModSettings", "SWGIEEE_802_15_4_ModSettings");
@ -666,6 +675,9 @@ SWGChannelSettings::asJsonObject() {
if((heat_map_settings != nullptr) && (heat_map_settings->isSet())){
toJsonValue(QString("HeatMapSettings"), heat_map_settings, obj, QString("SWGHeatMapSettings"));
}
if((ils_demod_settings != nullptr) && (ils_demod_settings->isSet())){
toJsonValue(QString("ILSDemodSettings"), ils_demod_settings, obj, QString("SWGILSDemodSettings"));
}
if((interferometer_settings != nullptr) && (interferometer_settings->isSet())){
toJsonValue(QString("InterferometerSettings"), interferometer_settings, obj, QString("SWGInterferometerSettings"));
}
@ -1051,6 +1063,16 @@ SWGChannelSettings::setHeatMapSettings(SWGHeatMapSettings* heat_map_settings) {
this->m_heat_map_settings_isSet = true;
}
SWGILSDemodSettings*
SWGChannelSettings::getIlsDemodSettings() {
return ils_demod_settings;
}
void
SWGChannelSettings::setIlsDemodSettings(SWGILSDemodSettings* ils_demod_settings) {
this->ils_demod_settings = ils_demod_settings;
this->m_ils_demod_settings_isSet = true;
}
SWGInterferometerSettings*
SWGChannelSettings::getInterferometerSettings() {
return interferometer_settings;
@ -1416,6 +1438,9 @@ SWGChannelSettings::isSet(){
if(heat_map_settings && heat_map_settings->isSet()){
isObjectUpdated = true; break;
}
if(ils_demod_settings && ils_demod_settings->isSet()){
isObjectUpdated = true; break;
}
if(interferometer_settings && interferometer_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -48,6 +48,7 @@
#include "SWGFreqTrackerSettings.h"
#include "SWGHeatMapSettings.h"
#include "SWGIEEE_802_15_4_ModSettings.h"
#include "SWGILSDemodSettings.h"
#include "SWGInterferometerSettings.h"
#include "SWGLocalSinkSettings.h"
#include "SWGLocalSourceSettings.h"
@ -185,6 +186,9 @@ public:
SWGHeatMapSettings* getHeatMapSettings();
void setHeatMapSettings(SWGHeatMapSettings* heat_map_settings);
SWGILSDemodSettings* getIlsDemodSettings();
void setIlsDemodSettings(SWGILSDemodSettings* ils_demod_settings);
SWGInterferometerSettings* getInterferometerSettings();
void setInterferometerSettings(SWGInterferometerSettings* interferometer_settings);
@ -360,6 +364,9 @@ private:
SWGHeatMapSettings* heat_map_settings;
bool m_heat_map_settings_isSet;
SWGILSDemodSettings* ils_demod_settings;
bool m_ils_demod_settings_isSet;
SWGInterferometerSettings* interferometer_settings;
bool m_interferometer_settings_isSet;

View File

@ -0,0 +1,131 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGILSDemodReport.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGILSDemodReport::SWGILSDemodReport(QString* json) {
init();
this->fromJson(*json);
}
SWGILSDemodReport::SWGILSDemodReport() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
SWGILSDemodReport::~SWGILSDemodReport() {
this->cleanup();
}
void
SWGILSDemodReport::init() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
void
SWGILSDemodReport::cleanup() {
}
SWGILSDemodReport*
SWGILSDemodReport::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGILSDemodReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
}
QString
SWGILSDemodReport::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGILSDemodReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_channel_power_db_isSet){
obj->insert("channelPowerDB", QJsonValue(channel_power_db));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
return obj;
}
float
SWGILSDemodReport::getChannelPowerDb() {
return channel_power_db;
}
void
SWGILSDemodReport::setChannelPowerDb(float channel_power_db) {
this->channel_power_db = channel_power_db;
this->m_channel_power_db_isSet = true;
}
qint32
SWGILSDemodReport::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGILSDemodReport::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
bool
SWGILSDemodReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_channel_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,64 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGILSDemodReport.h
*
* ILSDemod
*/
#ifndef SWGILSDemodReport_H_
#define SWGILSDemodReport_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGILSDemodReport: public SWGObject {
public:
SWGILSDemodReport();
SWGILSDemodReport(QString* json);
virtual ~SWGILSDemodReport();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGILSDemodReport* fromJson(QString &jsonString) override;
float getChannelPowerDb();
void setChannelPowerDb(float channel_power_db);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
virtual bool isSet() override;
private:
float channel_power_db;
bool m_channel_power_db_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
};
}
#endif /* SWGILSDemodReport_H_ */

View File

@ -0,0 +1,912 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGILSDemodSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGILSDemodSettings::SWGILSDemodSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGILSDemodSettings::SWGILSDemodSettings() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
mode = 0;
m_mode_isSet = false;
frequency_index = 0;
m_frequency_index_isSet = false;
squelch = 0;
m_squelch_isSet = false;
volume = 0.0f;
m_volume_isSet = false;
audio_mute = 0;
m_audio_mute_isSet = false;
average = 0;
m_average_isSet = false;
ddm_units = 0;
m_ddm_units_isSet = false;
ident_threshold = 0.0f;
m_ident_threshold_isSet = false;
ident = nullptr;
m_ident_isSet = false;
runway = nullptr;
m_runway_isSet = false;
true_bearing = 0.0f;
m_true_bearing_isSet = false;
latitude = nullptr;
m_latitude_isSet = false;
longitude = nullptr;
m_longitude_isSet = false;
elevation = 0;
m_elevation_isSet = false;
glide_path = 0.0f;
m_glide_path_isSet = false;
ref_height = 0.0f;
m_ref_height_isSet = false;
course_width = 0.0f;
m_course_width_isSet = false;
udp_enabled = 0;
m_udp_enabled_isSet = false;
udp_address = nullptr;
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
log_filename = nullptr;
m_log_filename_isSet = false;
log_enabled = 0;
m_log_enabled_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
m_title_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
scope_config = nullptr;
m_scope_config_isSet = false;
channel_marker = nullptr;
m_channel_marker_isSet = false;
rollup_state = nullptr;
m_rollup_state_isSet = false;
}
SWGILSDemodSettings::~SWGILSDemodSettings() {
this->cleanup();
}
void
SWGILSDemodSettings::init() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
mode = 0;
m_mode_isSet = false;
frequency_index = 0;
m_frequency_index_isSet = false;
squelch = 0;
m_squelch_isSet = false;
volume = 0.0f;
m_volume_isSet = false;
audio_mute = 0;
m_audio_mute_isSet = false;
average = 0;
m_average_isSet = false;
ddm_units = 0;
m_ddm_units_isSet = false;
ident_threshold = 0.0f;
m_ident_threshold_isSet = false;
ident = new QString("");
m_ident_isSet = false;
runway = new QString("");
m_runway_isSet = false;
true_bearing = 0.0f;
m_true_bearing_isSet = false;
latitude = new QString("");
m_latitude_isSet = false;
longitude = new QString("");
m_longitude_isSet = false;
elevation = 0;
m_elevation_isSet = false;
glide_path = 0.0f;
m_glide_path_isSet = false;
ref_height = 0.0f;
m_ref_height_isSet = false;
course_width = 0.0f;
m_course_width_isSet = false;
udp_enabled = 0;
m_udp_enabled_isSet = false;
udp_address = new QString("");
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
log_filename = new QString("");
m_log_filename_isSet = false;
log_enabled = 0;
m_log_enabled_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
m_title_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
scope_config = new SWGGLScope();
m_scope_config_isSet = false;
channel_marker = new SWGChannelMarker();
m_channel_marker_isSet = false;
rollup_state = new SWGRollupState();
m_rollup_state_isSet = false;
}
void
SWGILSDemodSettings::cleanup() {
if(ident != nullptr) {
delete ident;
}
if(runway != nullptr) {
delete runway;
}
if(latitude != nullptr) {
delete latitude;
}
if(longitude != nullptr) {
delete longitude;
}
if(udp_address != nullptr) {
delete udp_address;
}
if(log_filename != nullptr) {
delete log_filename;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
if(scope_config != nullptr) {
delete scope_config;
}
if(channel_marker != nullptr) {
delete channel_marker;
}
if(rollup_state != nullptr) {
delete rollup_state;
}
}
SWGILSDemodSettings*
SWGILSDemodSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGILSDemodSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", "");
::SWGSDRangel::setValue(&mode, pJson["mode"], "qint32", "");
::SWGSDRangel::setValue(&frequency_index, pJson["frequencyIndex"], "qint32", "");
::SWGSDRangel::setValue(&squelch, pJson["squelch"], "qint32", "");
::SWGSDRangel::setValue(&volume, pJson["volume"], "float", "");
::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", "");
::SWGSDRangel::setValue(&average, pJson["average"], "qint32", "");
::SWGSDRangel::setValue(&ddm_units, pJson["ddmUnits"], "qint32", "");
::SWGSDRangel::setValue(&ident_threshold, pJson["identThreshold"], "float", "");
::SWGSDRangel::setValue(&ident, pJson["ident"], "QString", "QString");
::SWGSDRangel::setValue(&runway, pJson["runway"], "QString", "QString");
::SWGSDRangel::setValue(&true_bearing, pJson["trueBearing"], "float", "");
::SWGSDRangel::setValue(&latitude, pJson["latitude"], "QString", "QString");
::SWGSDRangel::setValue(&longitude, pJson["longitude"], "QString", "QString");
::SWGSDRangel::setValue(&elevation, pJson["elevation"], "qint32", "");
::SWGSDRangel::setValue(&glide_path, pJson["glidePath"], "float", "");
::SWGSDRangel::setValue(&ref_height, pJson["refHeight"], "float", "");
::SWGSDRangel::setValue(&course_width, pJson["courseWidth"], "float", "");
::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", "");
::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString");
::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", "");
::SWGSDRangel::setValue(&log_filename, pJson["logFilename"], "QString", "QString");
::SWGSDRangel::setValue(&log_enabled, pJson["logEnabled"], "qint32", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", "");
::SWGSDRangel::setValue(&scope_config, pJson["scopeConfig"], "SWGGLScope", "SWGGLScope");
::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker");
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
}
QString
SWGILSDemodSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGILSDemodSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_input_frequency_offset_isSet){
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
}
if(m_rf_bandwidth_isSet){
obj->insert("rfBandwidth", QJsonValue(rf_bandwidth));
}
if(m_mode_isSet){
obj->insert("mode", QJsonValue(mode));
}
if(m_frequency_index_isSet){
obj->insert("frequencyIndex", QJsonValue(frequency_index));
}
if(m_squelch_isSet){
obj->insert("squelch", QJsonValue(squelch));
}
if(m_volume_isSet){
obj->insert("volume", QJsonValue(volume));
}
if(m_audio_mute_isSet){
obj->insert("audioMute", QJsonValue(audio_mute));
}
if(m_average_isSet){
obj->insert("average", QJsonValue(average));
}
if(m_ddm_units_isSet){
obj->insert("ddmUnits", QJsonValue(ddm_units));
}
if(m_ident_threshold_isSet){
obj->insert("identThreshold", QJsonValue(ident_threshold));
}
if(ident != nullptr && *ident != QString("")){
toJsonValue(QString("ident"), ident, obj, QString("QString"));
}
if(runway != nullptr && *runway != QString("")){
toJsonValue(QString("runway"), runway, obj, QString("QString"));
}
if(m_true_bearing_isSet){
obj->insert("trueBearing", QJsonValue(true_bearing));
}
if(latitude != nullptr && *latitude != QString("")){
toJsonValue(QString("latitude"), latitude, obj, QString("QString"));
}
if(longitude != nullptr && *longitude != QString("")){
toJsonValue(QString("longitude"), longitude, obj, QString("QString"));
}
if(m_elevation_isSet){
obj->insert("elevation", QJsonValue(elevation));
}
if(m_glide_path_isSet){
obj->insert("glidePath", QJsonValue(glide_path));
}
if(m_ref_height_isSet){
obj->insert("refHeight", QJsonValue(ref_height));
}
if(m_course_width_isSet){
obj->insert("courseWidth", QJsonValue(course_width));
}
if(m_udp_enabled_isSet){
obj->insert("udpEnabled", QJsonValue(udp_enabled));
}
if(udp_address != nullptr && *udp_address != QString("")){
toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString"));
}
if(m_udp_port_isSet){
obj->insert("udpPort", QJsonValue(udp_port));
}
if(log_filename != nullptr && *log_filename != QString("")){
toJsonValue(QString("logFilename"), log_filename, obj, QString("QString"));
}
if(m_log_enabled_isSet){
obj->insert("logEnabled", QJsonValue(log_enabled));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_stream_index_isSet){
obj->insert("streamIndex", QJsonValue(stream_index));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
}
if(m_reverse_api_port_isSet){
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
}
if(m_reverse_api_device_index_isSet){
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
}
if(m_reverse_api_channel_index_isSet){
obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index));
}
if((scope_config != nullptr) && (scope_config->isSet())){
toJsonValue(QString("scopeConfig"), scope_config, obj, QString("SWGGLScope"));
}
if((channel_marker != nullptr) && (channel_marker->isSet())){
toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker"));
}
if((rollup_state != nullptr) && (rollup_state->isSet())){
toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState"));
}
return obj;
}
qint64
SWGILSDemodSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGILSDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
float
SWGILSDemodSettings::getRfBandwidth() {
return rf_bandwidth;
}
void
SWGILSDemodSettings::setRfBandwidth(float rf_bandwidth) {
this->rf_bandwidth = rf_bandwidth;
this->m_rf_bandwidth_isSet = true;
}
qint32
SWGILSDemodSettings::getMode() {
return mode;
}
void
SWGILSDemodSettings::setMode(qint32 mode) {
this->mode = mode;
this->m_mode_isSet = true;
}
qint32
SWGILSDemodSettings::getFrequencyIndex() {
return frequency_index;
}
void
SWGILSDemodSettings::setFrequencyIndex(qint32 frequency_index) {
this->frequency_index = frequency_index;
this->m_frequency_index_isSet = true;
}
qint32
SWGILSDemodSettings::getSquelch() {
return squelch;
}
void
SWGILSDemodSettings::setSquelch(qint32 squelch) {
this->squelch = squelch;
this->m_squelch_isSet = true;
}
float
SWGILSDemodSettings::getVolume() {
return volume;
}
void
SWGILSDemodSettings::setVolume(float volume) {
this->volume = volume;
this->m_volume_isSet = true;
}
qint32
SWGILSDemodSettings::getAudioMute() {
return audio_mute;
}
void
SWGILSDemodSettings::setAudioMute(qint32 audio_mute) {
this->audio_mute = audio_mute;
this->m_audio_mute_isSet = true;
}
qint32
SWGILSDemodSettings::getAverage() {
return average;
}
void
SWGILSDemodSettings::setAverage(qint32 average) {
this->average = average;
this->m_average_isSet = true;
}
qint32
SWGILSDemodSettings::getDdmUnits() {
return ddm_units;
}
void
SWGILSDemodSettings::setDdmUnits(qint32 ddm_units) {
this->ddm_units = ddm_units;
this->m_ddm_units_isSet = true;
}
float
SWGILSDemodSettings::getIdentThreshold() {
return ident_threshold;
}
void
SWGILSDemodSettings::setIdentThreshold(float ident_threshold) {
this->ident_threshold = ident_threshold;
this->m_ident_threshold_isSet = true;
}
QString*
SWGILSDemodSettings::getIdent() {
return ident;
}
void
SWGILSDemodSettings::setIdent(QString* ident) {
this->ident = ident;
this->m_ident_isSet = true;
}
QString*
SWGILSDemodSettings::getRunway() {
return runway;
}
void
SWGILSDemodSettings::setRunway(QString* runway) {
this->runway = runway;
this->m_runway_isSet = true;
}
float
SWGILSDemodSettings::getTrueBearing() {
return true_bearing;
}
void
SWGILSDemodSettings::setTrueBearing(float true_bearing) {
this->true_bearing = true_bearing;
this->m_true_bearing_isSet = true;
}
QString*
SWGILSDemodSettings::getLatitude() {
return latitude;
}
void
SWGILSDemodSettings::setLatitude(QString* latitude) {
this->latitude = latitude;
this->m_latitude_isSet = true;
}
QString*
SWGILSDemodSettings::getLongitude() {
return longitude;
}
void
SWGILSDemodSettings::setLongitude(QString* longitude) {
this->longitude = longitude;
this->m_longitude_isSet = true;
}
qint32
SWGILSDemodSettings::getElevation() {
return elevation;
}
void
SWGILSDemodSettings::setElevation(qint32 elevation) {
this->elevation = elevation;
this->m_elevation_isSet = true;
}
float
SWGILSDemodSettings::getGlidePath() {
return glide_path;
}
void
SWGILSDemodSettings::setGlidePath(float glide_path) {
this->glide_path = glide_path;
this->m_glide_path_isSet = true;
}
float
SWGILSDemodSettings::getRefHeight() {
return ref_height;
}
void
SWGILSDemodSettings::setRefHeight(float ref_height) {
this->ref_height = ref_height;
this->m_ref_height_isSet = true;
}
float
SWGILSDemodSettings::getCourseWidth() {
return course_width;
}
void
SWGILSDemodSettings::setCourseWidth(float course_width) {
this->course_width = course_width;
this->m_course_width_isSet = true;
}
qint32
SWGILSDemodSettings::getUdpEnabled() {
return udp_enabled;
}
void
SWGILSDemodSettings::setUdpEnabled(qint32 udp_enabled) {
this->udp_enabled = udp_enabled;
this->m_udp_enabled_isSet = true;
}
QString*
SWGILSDemodSettings::getUdpAddress() {
return udp_address;
}
void
SWGILSDemodSettings::setUdpAddress(QString* udp_address) {
this->udp_address = udp_address;
this->m_udp_address_isSet = true;
}
qint32
SWGILSDemodSettings::getUdpPort() {
return udp_port;
}
void
SWGILSDemodSettings::setUdpPort(qint32 udp_port) {
this->udp_port = udp_port;
this->m_udp_port_isSet = true;
}
QString*
SWGILSDemodSettings::getLogFilename() {
return log_filename;
}
void
SWGILSDemodSettings::setLogFilename(QString* log_filename) {
this->log_filename = log_filename;
this->m_log_filename_isSet = true;
}
qint32
SWGILSDemodSettings::getLogEnabled() {
return log_enabled;
}
void
SWGILSDemodSettings::setLogEnabled(qint32 log_enabled) {
this->log_enabled = log_enabled;
this->m_log_enabled_isSet = true;
}
qint32
SWGILSDemodSettings::getRgbColor() {
return rgb_color;
}
void
SWGILSDemodSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGILSDemodSettings::getTitle() {
return title;
}
void
SWGILSDemodSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGILSDemodSettings::getStreamIndex() {
return stream_index;
}
void
SWGILSDemodSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGILSDemodSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGILSDemodSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGILSDemodSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGILSDemodSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGILSDemodSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGILSDemodSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGILSDemodSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGILSDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGILSDemodSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGILSDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
SWGGLScope*
SWGILSDemodSettings::getScopeConfig() {
return scope_config;
}
void
SWGILSDemodSettings::setScopeConfig(SWGGLScope* scope_config) {
this->scope_config = scope_config;
this->m_scope_config_isSet = true;
}
SWGChannelMarker*
SWGILSDemodSettings::getChannelMarker() {
return channel_marker;
}
void
SWGILSDemodSettings::setChannelMarker(SWGChannelMarker* channel_marker) {
this->channel_marker = channel_marker;
this->m_channel_marker_isSet = true;
}
SWGRollupState*
SWGILSDemodSettings::getRollupState() {
return rollup_state;
}
void
SWGILSDemodSettings::setRollupState(SWGRollupState* rollup_state) {
this->rollup_state = rollup_state;
this->m_rollup_state_isSet = true;
}
bool
SWGILSDemodSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_rf_bandwidth_isSet){
isObjectUpdated = true; break;
}
if(m_mode_isSet){
isObjectUpdated = true; break;
}
if(m_frequency_index_isSet){
isObjectUpdated = true; break;
}
if(m_squelch_isSet){
isObjectUpdated = true; break;
}
if(m_volume_isSet){
isObjectUpdated = true; break;
}
if(m_audio_mute_isSet){
isObjectUpdated = true; break;
}
if(m_average_isSet){
isObjectUpdated = true; break;
}
if(m_ddm_units_isSet){
isObjectUpdated = true; break;
}
if(m_ident_threshold_isSet){
isObjectUpdated = true; break;
}
if(ident && *ident != QString("")){
isObjectUpdated = true; break;
}
if(runway && *runway != QString("")){
isObjectUpdated = true; break;
}
if(m_true_bearing_isSet){
isObjectUpdated = true; break;
}
if(latitude && *latitude != QString("")){
isObjectUpdated = true; break;
}
if(longitude && *longitude != QString("")){
isObjectUpdated = true; break;
}
if(m_elevation_isSet){
isObjectUpdated = true; break;
}
if(m_glide_path_isSet){
isObjectUpdated = true; break;
}
if(m_ref_height_isSet){
isObjectUpdated = true; break;
}
if(m_course_width_isSet){
isObjectUpdated = true; break;
}
if(m_udp_enabled_isSet){
isObjectUpdated = true; break;
}
if(udp_address && *udp_address != QString("")){
isObjectUpdated = true; break;
}
if(m_udp_port_isSet){
isObjectUpdated = true; break;
}
if(log_filename && *log_filename != QString("")){
isObjectUpdated = true; break;
}
if(m_log_enabled_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_stream_index_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_channel_index_isSet){
isObjectUpdated = true; break;
}
if(scope_config && scope_config->isSet()){
isObjectUpdated = true; break;
}
if(channel_marker && channel_marker->isSet()){
isObjectUpdated = true; break;
}
if(rollup_state && rollup_state->isSet()){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,266 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGILSDemodSettings.h
*
* ILSDemod
*/
#ifndef SWGILSDemodSettings_H_
#define SWGILSDemodSettings_H_
#include <QJsonObject>
#include "SWGChannelMarker.h"
#include "SWGGLScope.h"
#include "SWGRollupState.h"
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGILSDemodSettings: public SWGObject {
public:
SWGILSDemodSettings();
SWGILSDemodSettings(QString* json);
virtual ~SWGILSDemodSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGILSDemodSettings* fromJson(QString &jsonString) override;
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
float getRfBandwidth();
void setRfBandwidth(float rf_bandwidth);
qint32 getMode();
void setMode(qint32 mode);
qint32 getFrequencyIndex();
void setFrequencyIndex(qint32 frequency_index);
qint32 getSquelch();
void setSquelch(qint32 squelch);
float getVolume();
void setVolume(float volume);
qint32 getAudioMute();
void setAudioMute(qint32 audio_mute);
qint32 getAverage();
void setAverage(qint32 average);
qint32 getDdmUnits();
void setDdmUnits(qint32 ddm_units);
float getIdentThreshold();
void setIdentThreshold(float ident_threshold);
QString* getIdent();
void setIdent(QString* ident);
QString* getRunway();
void setRunway(QString* runway);
float getTrueBearing();
void setTrueBearing(float true_bearing);
QString* getLatitude();
void setLatitude(QString* latitude);
QString* getLongitude();
void setLongitude(QString* longitude);
qint32 getElevation();
void setElevation(qint32 elevation);
float getGlidePath();
void setGlidePath(float glide_path);
float getRefHeight();
void setRefHeight(float ref_height);
float getCourseWidth();
void setCourseWidth(float course_width);
qint32 getUdpEnabled();
void setUdpEnabled(qint32 udp_enabled);
QString* getUdpAddress();
void setUdpAddress(QString* udp_address);
qint32 getUdpPort();
void setUdpPort(qint32 udp_port);
QString* getLogFilename();
void setLogFilename(QString* log_filename);
qint32 getLogEnabled();
void setLogEnabled(qint32 log_enabled);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getTitle();
void setTitle(QString* title);
qint32 getStreamIndex();
void setStreamIndex(qint32 stream_index);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
QString* getReverseApiAddress();
void setReverseApiAddress(QString* reverse_api_address);
qint32 getReverseApiPort();
void setReverseApiPort(qint32 reverse_api_port);
qint32 getReverseApiDeviceIndex();
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
qint32 getReverseApiChannelIndex();
void setReverseApiChannelIndex(qint32 reverse_api_channel_index);
SWGGLScope* getScopeConfig();
void setScopeConfig(SWGGLScope* scope_config);
SWGChannelMarker* getChannelMarker();
void setChannelMarker(SWGChannelMarker* channel_marker);
SWGRollupState* getRollupState();
void setRollupState(SWGRollupState* rollup_state);
virtual bool isSet() override;
private:
qint64 input_frequency_offset;
bool m_input_frequency_offset_isSet;
float rf_bandwidth;
bool m_rf_bandwidth_isSet;
qint32 mode;
bool m_mode_isSet;
qint32 frequency_index;
bool m_frequency_index_isSet;
qint32 squelch;
bool m_squelch_isSet;
float volume;
bool m_volume_isSet;
qint32 audio_mute;
bool m_audio_mute_isSet;
qint32 average;
bool m_average_isSet;
qint32 ddm_units;
bool m_ddm_units_isSet;
float ident_threshold;
bool m_ident_threshold_isSet;
QString* ident;
bool m_ident_isSet;
QString* runway;
bool m_runway_isSet;
float true_bearing;
bool m_true_bearing_isSet;
QString* latitude;
bool m_latitude_isSet;
QString* longitude;
bool m_longitude_isSet;
qint32 elevation;
bool m_elevation_isSet;
float glide_path;
bool m_glide_path_isSet;
float ref_height;
bool m_ref_height_isSet;
float course_width;
bool m_course_width_isSet;
qint32 udp_enabled;
bool m_udp_enabled_isSet;
QString* udp_address;
bool m_udp_address_isSet;
qint32 udp_port;
bool m_udp_port_isSet;
QString* log_filename;
bool m_log_filename_isSet;
qint32 log_enabled;
bool m_log_enabled_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* title;
bool m_title_isSet;
qint32 stream_index;
bool m_stream_index_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;
QString* reverse_api_address;
bool m_reverse_api_address_isSet;
qint32 reverse_api_port;
bool m_reverse_api_port_isSet;
qint32 reverse_api_device_index;
bool m_reverse_api_device_index_isSet;
qint32 reverse_api_channel_index;
bool m_reverse_api_channel_index_isSet;
SWGGLScope* scope_config;
bool m_scope_config_isSet;
SWGChannelMarker* channel_marker;
bool m_channel_marker_isSet;
SWGRollupState* rollup_state;
bool m_rollup_state_isSet;
};
}
#endif /* SWGILSDemodSettings_H_ */

View File

@ -160,6 +160,8 @@
#include "SWGIEEE_802_15_4_ModActions.h"
#include "SWGIEEE_802_15_4_ModReport.h"
#include "SWGIEEE_802_15_4_ModSettings.h"
#include "SWGILSDemodReport.h"
#include "SWGILSDemodSettings.h"
#include "SWGInstanceChannelsResponse.h"
#include "SWGInstanceConfigResponse.h"
#include "SWGInstanceDevicesResponse.h"
@ -1081,6 +1083,16 @@ namespace SWGSDRangel {
obj->init();
return obj;
}
if(QString("SWGILSDemodReport").compare(type) == 0) {
SWGILSDemodReport *obj = new SWGILSDemodReport();
obj->init();
return obj;
}
if(QString("SWGILSDemodSettings").compare(type) == 0) {
SWGILSDemodSettings *obj = new SWGILSDemodSettings();
obj->init();
return obj;
}
if(QString("SWGInstanceChannelsResponse").compare(type) == 0) {
SWGInstanceChannelsResponse *obj = new SWGInstanceChannelsResponse();
obj->init();