From 267ae135340b3fa2fbeec72798a2724a899c6915 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 9 Feb 2022 16:36:09 +0000 Subject: [PATCH 1/6] Add support for receiving global aircraft data from OpenSky Network. Add support for feeding ADS-B data to OpenSky Network. Fix binary Beast format feed. Fix display of USA and Japan flags. --- plugins/channelrx/demodadsb/adsbdemod.cpp | 184 ++++++- .../demodadsb/adsbdemodfeeddialog.cpp | 56 +- .../channelrx/demodadsb/adsbdemodfeeddialog.h | 8 +- .../demodadsb/adsbdemodfeeddialog.ui | 405 +++++++++++--- plugins/channelrx/demodadsb/adsbdemodgui.cpp | 521 ++++++++++++------ plugins/channelrx/demodadsb/adsbdemodgui.h | 9 + plugins/channelrx/demodadsb/adsbdemodgui.ui | 1 + .../channelrx/demodadsb/adsbdemodsettings.cpp | 66 ++- .../channelrx/demodadsb/adsbdemodsettings.h | 20 +- .../channelrx/demodadsb/adsbdemodworker.cpp | 149 ++++- plugins/channelrx/demodadsb/adsbdemodworker.h | 24 + plugins/channelrx/demodadsb/readme.md | 20 +- .../api/swagger/include/ADSBDemod.yaml | 40 +- .../code/qt5/client/SWGADSBDemodSettings.cpp | 423 ++++++++++++-- .../code/qt5/client/SWGADSBDemodSettings.h | 110 +++- 15 files changed, 1641 insertions(+), 395 deletions(-) diff --git a/plugins/channelrx/demodadsb/adsbdemod.cpp b/plugins/channelrx/demodadsb/adsbdemod.cpp index 3d3205ee3..312ec149d 100644 --- a/plugins/channelrx/demodadsb/adsbdemod.cpp +++ b/plugins/channelrx/demodadsb/adsbdemod.cpp @@ -206,16 +206,55 @@ void ADSBDemod::applySettings(const ADSBDemodSettings& settings, bool force) reverseAPIKeys.append("removeTimeout"); } if ((settings.m_feedEnabled != m_settings.m_feedEnabled) || force) { - reverseAPIKeys.append("beastEnabled"); + reverseAPIKeys.append("feedEnabled"); } - if ((settings.m_feedHost != m_settings.m_feedHost) || force) { - reverseAPIKeys.append("beastHost"); + if ((settings.m_exportClientEnabled != m_settings.m_exportClientEnabled) || force) { + reverseAPIKeys.append("exportClientEnabled"); } - if ((settings.m_feedPort != m_settings.m_feedPort) || force) { - reverseAPIKeys.append("beastPort"); + if ((settings.m_exportClientHost != m_settings.m_exportClientHost) || force) { + reverseAPIKeys.append("exportClientHost"); } - if ((settings.m_feedFormat != m_settings.m_feedFormat) || force) { - reverseAPIKeys.append("feedFormat"); + if ((settings.m_exportClientPort != m_settings.m_exportClientPort) || force) { + reverseAPIKeys.append("exportClientPort"); + } + if ((settings.m_exportClientFormat != m_settings.m_exportClientFormat) || force) { + reverseAPIKeys.append("exportClientFormat"); + } + if ((settings.m_exportServerEnabled != m_settings.m_exportServerEnabled) || force) { + reverseAPIKeys.append("exportServerEnabled"); + } + if ((settings.m_exportServerPort != m_settings.m_exportServerPort) || force) { + reverseAPIKeys.append("exportServerPort"); + } + if ((settings.m_importEnabled != m_settings.m_importEnabled) || force) { + reverseAPIKeys.append("importEnabled"); + } + if ((settings.m_importHost != m_settings.m_importHost) || force) { + reverseAPIKeys.append("importHost"); + } + if ((settings.m_importUsername != m_settings.m_importUsername) || force) { + reverseAPIKeys.append("importUsername"); + } + if ((settings.m_importPassword != m_settings.m_importPassword) || force) { + reverseAPIKeys.append("importPassword"); + } + if ((settings.m_importParameters != m_settings.m_importParameters) || force) { + reverseAPIKeys.append("importParameters"); + } + if ((settings.m_importPeriod != m_settings.m_importPeriod) || force) { + reverseAPIKeys.append("importPeriod"); + } + if ((settings.m_importMinLatitude != m_settings.m_importMinLatitude) || force) { + reverseAPIKeys.append("importMinLatitude"); + } + if ((settings.m_importMaxLatitude != m_settings.m_importMaxLatitude) || force) { + reverseAPIKeys.append("importMaxLatitude"); + } + if ((settings.m_importMinLongitude != m_settings.m_importMinLongitude) || force) { + reverseAPIKeys.append("importMinLongitude"); + } + if ((settings.m_importMaxLongitude != m_settings.m_importMaxLongitude) || force) { + reverseAPIKeys.append("importMaxLongitude"); } if ((settings.m_logFilename != m_settings.m_logFilename) || force) { reverseAPIKeys.append("logFilename"); @@ -363,17 +402,56 @@ void ADSBDemod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("removeTimeout")) { settings.m_removeTimeout = response.getAdsbDemodSettings()->getRemoveTimeout(); } - if (channelSettingsKeys.contains("beastEnabled")) { - settings.m_feedEnabled = response.getAdsbDemodSettings()->getBeastEnabled() != 0; + if (channelSettingsKeys.contains("feedEnabled")) { + settings.m_feedEnabled = response.getAdsbDemodSettings()->getFeedEnabled() != 0; } - if (channelSettingsKeys.contains("beastHost")) { - settings.m_feedHost = *response.getAdsbDemodSettings()->getBeastHost(); + if (channelSettingsKeys.contains("exportClientEnabled")) { + settings.m_exportClientEnabled = response.getAdsbDemodSettings()->getExportClientEnabled() != 0; } - if (channelSettingsKeys.contains("beastPort")) { - settings.m_feedPort = response.getAdsbDemodSettings()->getBeastPort(); + if (channelSettingsKeys.contains("exportClientHost")) { + settings.m_exportClientHost = *response.getAdsbDemodSettings()->getExportClientHost(); } - if (channelSettingsKeys.contains("feedFormat")) { - settings.m_feedFormat = (ADSBDemodSettings::FeedFormat) response.getAdsbDemodSettings()->getFeedFormat(); + if (channelSettingsKeys.contains("exportClientPort")) { + settings.m_exportClientPort = response.getAdsbDemodSettings()->getExportClientPort(); + } + if (channelSettingsKeys.contains("exportClientFormat")) { + settings.m_exportClientFormat = (ADSBDemodSettings::FeedFormat) response.getAdsbDemodSettings()->getExportClientFormat(); + } + if (channelSettingsKeys.contains("exportServerEnabled")) { + settings.m_exportServerEnabled = response.getAdsbDemodSettings()->getExportServerEnabled() != 0; + } + if (channelSettingsKeys.contains("exportServerPort")) { + settings.m_exportServerPort = response.getAdsbDemodSettings()->getExportServerPort(); + } + if (channelSettingsKeys.contains("importEnabled")) { + settings.m_importEnabled = response.getAdsbDemodSettings()->getImportEnabled() != 0; + } + if (channelSettingsKeys.contains("importHost")) { + settings.m_importHost = *response.getAdsbDemodSettings()->getImportHost(); + } + if (channelSettingsKeys.contains("importUsername")) { + settings.m_importUsername = *response.getAdsbDemodSettings()->getImportUsername(); + } + if (channelSettingsKeys.contains("importPassword")) { + settings.m_importPassword = *response.getAdsbDemodSettings()->getImportPassword(); + } + if (channelSettingsKeys.contains("importParameters")) { + settings.m_importParameters = *response.getAdsbDemodSettings()->getImportParameters(); + } + if (channelSettingsKeys.contains("importPeriod")) { + settings.m_importPeriod = response.getAdsbDemodSettings()->getImportPeriod(); + } + if (channelSettingsKeys.contains("importMinLatitude")) { + settings.m_importMinLatitude = *response.getAdsbDemodSettings()->getImportMinLatitude(); + } + if (channelSettingsKeys.contains("importMaxLatitude")) { + settings.m_importMaxLatitude = *response.getAdsbDemodSettings()->getImportMaxLatitude(); + } + if (channelSettingsKeys.contains("importMinLongitude")) { + settings.m_importMinLongitude = *response.getAdsbDemodSettings()->getImportMinLongitude(); + } + if (channelSettingsKeys.contains("importMaxLongitude")) { + settings.m_importMaxLongitude = *response.getAdsbDemodSettings()->getImportMaxLongitude(); } if (channelSettingsKeys.contains("logFilename")) { settings.m_logFilename = *response.getAdsbDemodSettings()->getLogFilename(); @@ -435,10 +513,23 @@ void ADSBDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res response.getAdsbDemodSettings()->setInterpolatorPhaseSteps(settings.m_interpolatorPhaseSteps); response.getAdsbDemodSettings()->setInterpolatorTapsPerPhase(settings.m_interpolatorTapsPerPhase); response.getAdsbDemodSettings()->setRemoveTimeout(settings.m_removeTimeout); - response.getAdsbDemodSettings()->setBeastEnabled(settings.m_feedEnabled ? 1 : 0); - response.getAdsbDemodSettings()->setBeastHost(new QString(settings.m_feedHost)); - response.getAdsbDemodSettings()->setBeastPort(settings.m_feedPort); - response.getAdsbDemodSettings()->setFeedFormat((int) settings.m_feedFormat); + response.getAdsbDemodSettings()->setFeedEnabled(settings.m_feedEnabled ? 1 : 0); + response.getAdsbDemodSettings()->setExportClientEnabled(settings.m_exportClientEnabled ? 1 : 0); + response.getAdsbDemodSettings()->setExportClientHost(new QString(settings.m_exportClientHost)); + response.getAdsbDemodSettings()->setExportClientPort(settings.m_exportClientPort); + response.getAdsbDemodSettings()->setExportClientFormat((int) settings.m_exportClientFormat); + response.getAdsbDemodSettings()->setExportServerEnabled(settings.m_exportServerEnabled ? 1 : 0); + response.getAdsbDemodSettings()->setExportServerPort(settings.m_exportServerPort); + response.getAdsbDemodSettings()->setImportEnabled(settings.m_importEnabled ? 1 : 0); + response.getAdsbDemodSettings()->setImportHost(new QString(settings.m_importHost)); + response.getAdsbDemodSettings()->setImportUsername(new QString(settings.m_importUsername)); + response.getAdsbDemodSettings()->setImportPassword(new QString(settings.m_importPassword)); + response.getAdsbDemodSettings()->setImportParameters(new QString(settings.m_importParameters)); + response.getAdsbDemodSettings()->setImportPeriod(settings.m_importPeriod); + response.getAdsbDemodSettings()->setImportMinLatitude(new QString(settings.m_importMinLatitude)); + response.getAdsbDemodSettings()->setImportMaxLatitude(new QString(settings.m_importMaxLatitude)); + response.getAdsbDemodSettings()->setImportMinLongitude(new QString(settings.m_importMinLongitude)); + response.getAdsbDemodSettings()->setImportMaxLongitude(new QString(settings.m_importMaxLongitude)); response.getAdsbDemodSettings()->setRgbColor(settings.m_rgbColor); response.getAdsbDemodSettings()->setLogFilename(new QString(settings.m_logFilename)); response.getAdsbDemodSettings()->setLogEnabled(settings.m_logEnabled); @@ -548,17 +639,56 @@ void ADSBDemod::webapiReverseSendSettings(QList& channelSettingsKeys, c if (channelSettingsKeys.contains("removeTimeout") || force) { swgADSBDemodSettings->setRemoveTimeout(settings.m_removeTimeout); } - if (channelSettingsKeys.contains("beastEnabled") || force) { - swgADSBDemodSettings->setBeastEnabled(settings.m_feedEnabled ? 1 : 0); + if (channelSettingsKeys.contains("feedEnabled") || force) { + swgADSBDemodSettings->setFeedEnabled(settings.m_feedEnabled ? 1 : 0); } - if (channelSettingsKeys.contains("beastHost") || force) { - swgADSBDemodSettings->setBeastHost(new QString(settings.m_feedHost)); + if (channelSettingsKeys.contains("exportClientEnabled") || force) { + swgADSBDemodSettings->setExportClientEnabled(settings.m_exportClientEnabled ? 1 : 0); } - if (channelSettingsKeys.contains("beastPort") || force) { - swgADSBDemodSettings->setBeastPort(settings.m_feedPort); + if (channelSettingsKeys.contains("exportClientHost") || force) { + swgADSBDemodSettings->setExportClientHost(new QString(settings.m_exportClientHost)); } - if (channelSettingsKeys.contains("feedFormat") || force) { - swgADSBDemodSettings->setFeedFormat((int) settings.m_feedFormat); + if (channelSettingsKeys.contains("exportClientPort") || force) { + swgADSBDemodSettings->setExportClientPort(settings.m_exportClientPort); + } + if (channelSettingsKeys.contains("exportClientFormat") || force) { + swgADSBDemodSettings->setExportClientFormat((int) settings.m_exportClientFormat); + } + if (channelSettingsKeys.contains("exportServerEnabled") || force) { + swgADSBDemodSettings->setExportServerEnabled(settings.m_exportServerEnabled ? 1 : 0); + } + if (channelSettingsKeys.contains("exportServerPort") || force) { + swgADSBDemodSettings->setExportServerPort(settings.m_exportServerPort); + } + if (channelSettingsKeys.contains("importEnabled") || force) { + swgADSBDemodSettings->setImportEnabled(settings.m_importEnabled ? 1 : 0); + } + if (channelSettingsKeys.contains("importHost") || force) { + swgADSBDemodSettings->setImportHost(new QString(settings.m_importHost)); + } + if (channelSettingsKeys.contains("importUsername") || force) { + swgADSBDemodSettings->setImportUsername(new QString(settings.m_importUsername)); + } + if (channelSettingsKeys.contains("importPassword") || force) { + swgADSBDemodSettings->setImportPassword(new QString(settings.m_importPassword)); + } + if (channelSettingsKeys.contains("importParameters") || force) { + swgADSBDemodSettings->setImportParameters(new QString(settings.m_importParameters)); + } + if (channelSettingsKeys.contains("importPeriod") || force) { + swgADSBDemodSettings->setImportPeriod(settings.m_importPeriod); + } + if (channelSettingsKeys.contains("importMinLatitude") || force) { + swgADSBDemodSettings->setImportMinLatitude(new QString(settings.m_importMinLatitude)); + } + if (channelSettingsKeys.contains("importMaxLatitude") || force) { + swgADSBDemodSettings->setImportMaxLatitude(new QString(settings.m_importMaxLatitude)); + } + if (channelSettingsKeys.contains("importMinLongitude") || force) { + swgADSBDemodSettings->setImportMinLongitude(new QString(settings.m_importMinLongitude)); + } + if (channelSettingsKeys.contains("importMaxLongitude") || force) { + swgADSBDemodSettings->setImportMaxLongitude(new QString(settings.m_importMaxLongitude)); } if (channelSettingsKeys.contains("logFilename") || force) { swgADSBDemodSettings->setLogFilename(new QString(settings.m_logFilename)); diff --git a/plugins/channelrx/demodadsb/adsbdemodfeeddialog.cpp b/plugins/channelrx/demodadsb/adsbdemodfeeddialog.cpp index 4eaaac506..a5bceeec5 100644 --- a/plugins/channelrx/demodadsb/adsbdemodfeeddialog.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodfeeddialog.cpp @@ -20,14 +20,28 @@ #include "adsbdemodfeeddialog.h" #include "adsbdemodsettings.h" -ADSBDemodFeedDialog::ADSBDemodFeedDialog(QString& feedHost, int feedPort, ADSBDemodSettings::FeedFormat feedFormat, QWidget* parent) : +ADSBDemodFeedDialog::ADSBDemodFeedDialog(ADSBDemodSettings *settings, QWidget* parent) : QDialog(parent), + m_settings(settings), ui(new Ui::ADSBDemodFeedDialog) { ui->setupUi(this); - ui->host->lineEdit()->setText(feedHost); - ui->port->setValue(feedPort); - ui->format->setCurrentIndex((int)feedFormat); + ui->exportClientEnabled->setChecked(m_settings->m_exportClientEnabled); + ui->exportClientHost->lineEdit()->setText(m_settings->m_exportClientHost); + ui->exportClientPort->setValue(m_settings->m_exportClientPort); + ui->exportClientFormat->setCurrentIndex((int)m_settings->m_exportClientFormat); + ui->exportServerEnabled->setChecked(m_settings->m_exportServerEnabled); + ui->exportServerPort->setValue(m_settings->m_exportServerPort); + ui->importEnabled->setChecked(m_settings->m_importEnabled); + ui->importHost->setCurrentIndex(ui->importHost->findText(m_settings->m_importHost)); + ui->importUsername->setText(m_settings->m_importUsername); + ui->importPassword->setText(m_settings->m_importPassword); + ui->importParameters->setText(m_settings->m_importParameters); + ui->importPeriod->setValue(m_settings->m_importPeriod); + ui->latitudeMin->setText(m_settings->m_importMinLatitude); + ui->latitudeMax->setText(m_settings->m_importMaxLatitude); + ui->longitudeMin->setText(m_settings->m_importMinLongitude); + ui->longitudeMax->setText(m_settings->m_importMaxLongitude); } ADSBDemodFeedDialog::~ADSBDemodFeedDialog() @@ -37,24 +51,38 @@ ADSBDemodFeedDialog::~ADSBDemodFeedDialog() void ADSBDemodFeedDialog::accept() { - m_feedHost = ui->host->currentText(); - m_feedPort = ui->port->value(); - m_feedFormat = (ADSBDemodSettings::FeedFormat )ui->format->currentIndex(); + m_settings->m_exportClientEnabled = ui->exportClientEnabled->isChecked(); + m_settings->m_exportClientHost = ui->exportClientHost->currentText(); + m_settings->m_exportClientPort = ui->exportClientPort->value(); + m_settings->m_exportClientFormat = (ADSBDemodSettings::FeedFormat )ui->exportClientFormat->currentIndex(); + m_settings->m_exportServerEnabled = ui->exportServerEnabled->isChecked(); + m_settings->m_exportServerPort = ui->exportServerPort->value(); + m_settings->m_importEnabled = ui->importEnabled->isChecked(); + m_settings->m_importHost = ui->importHost->currentText(); + m_settings->m_importUsername = ui->importUsername->text(); + m_settings->m_importPassword = ui->importPassword->text(); + m_settings->m_importParameters = ui->importParameters->text(); + m_settings->m_importPeriod = ui->importPeriod->value(); + m_settings->m_importMinLatitude = ui->latitudeMin->text(); + m_settings->m_importMaxLatitude = ui->latitudeMax->text(); + m_settings->m_importMinLongitude = ui->longitudeMin->text(); + m_settings->m_importMaxLongitude = ui->longitudeMax->text(); + QDialog::accept(); } -void ADSBDemodFeedDialog::on_host_currentIndexChanged(int value) +void ADSBDemodFeedDialog::on_exportClientHost_currentIndexChanged(int value) { if (value == 0) { - ui->host->lineEdit()->setText("feed.adsbexchange.com"); - ui->port->setValue(30005); - ui->format->setCurrentIndex(0); + ui->exportClientHost->lineEdit()->setText("feed.adsbexchange.com"); + ui->exportClientPort->setValue(30005); + ui->exportClientFormat->setCurrentIndex(0); } else if (value == 1) { - ui->host->lineEdit()->setText("data.adsbhub.org"); - ui->port->setValue(5002); - ui->format->setCurrentIndex(1); + ui->exportClientHost->lineEdit()->setText("data.adsbhub.org"); + ui->exportClientPort->setValue(5002); + ui->exportClientFormat->setCurrentIndex(1); } } diff --git a/plugins/channelrx/demodadsb/adsbdemodfeeddialog.h b/plugins/channelrx/demodadsb/adsbdemodfeeddialog.h index 64af99183..e3a2b162a 100644 --- a/plugins/channelrx/demodadsb/adsbdemodfeeddialog.h +++ b/plugins/channelrx/demodadsb/adsbdemodfeeddialog.h @@ -25,16 +25,14 @@ class ADSBDemodFeedDialog : public QDialog { Q_OBJECT public: - explicit ADSBDemodFeedDialog(QString& feedHost, int feedPort, ADSBDemodSettings::FeedFormat feedFormat, QWidget* parent = 0); + explicit ADSBDemodFeedDialog(ADSBDemodSettings *settings, QWidget* parent = 0); ~ADSBDemodFeedDialog(); - QString m_feedHost; - int m_feedPort; - ADSBDemodSettings::FeedFormat m_feedFormat; + ADSBDemodSettings *m_settings; private slots: void accept(); - void on_host_currentIndexChanged(int value); + void on_exportClientHost_currentIndexChanged(int value); private: Ui::ADSBDemodFeedDialog* ui; diff --git a/plugins/channelrx/demodadsb/adsbdemodfeeddialog.ui b/plugins/channelrx/demodadsb/adsbdemodfeeddialog.ui index 786c2c4ac..19b6c8283 100644 --- a/plugins/channelrx/demodadsb/adsbdemodfeeddialog.ui +++ b/plugins/channelrx/demodadsb/adsbdemodfeeddialog.ui @@ -6,13 +6,12 @@ 0 0 - 351 - 138 + 472 + 553 - Liberation Sans 9 @@ -23,75 +22,334 @@ - - - - Server hostname + + + + Import + + + + + Parameters + + + + + + + Username to access server with + + + + + + + + + Latitude Min + + + + + + + Minimum latitude of area to receive aircraft data from + + + + + + + Latitude Max + + + + + + + Maximum latitude of area to receive aircraft data from + + + + + + + Minimum longitude of area to receive aircraft data from + + + Longitude Min + + + + + + + + + + Longitude Max + + + + + + + Maximum longitude of area to receive aircraft data from + + + + + + + + + Server hostname + + + + + + + Username + + + + + + + Update period (s) + + + + + + + + + + + + + + + 100 + 0 + + + + Enable import + + + + + + + Password to access the server with + + + + + + + Parameters for the API call such as geographic location + + + + + + + Time period in seconds between updates requested from the server + + + 0 + + + 5.000000000000000 + + + 1000.000000000000000 + + + 10.000000000000000 + + + + + + + Hostname of server to receive ADS-B data from + + + + opensky-network.org + + + + + + + + Password + + + + - - - - Port number + + + + Export Client + + + + + + 100 + 0 + + + + Enable client + + + + + + + Enable TCP client + + + + + + + + + + Server hostname + + + + + + + Hostname of server to feed ADS-B data to + + + true + + + + feed.adsbexchange.com + + + + + data.adsbhub.org + + + + + + + + Port + + + + + + + The TCP port number the server is listening on + + + 1024 + + + 65535 + + + + + + + Format + + + + + + + Format to feed the data to the server in + + + + Beast binary + + + + + Beast hex + + + + + - - - - The TCP port number the server is listening on + + + + Export Server - - 1024 - - - 65535 - - - - - - - Format to feed the data to the server in - - - - Beast binary - - - - - Beast hex - - - - - - - - Format - - - - - - - Hostname of server to feed ADS-B data to - - - true - - - - feed.adsbexchange.com - - - - - data.adsbhub.org - - + + + + + Port + + + + + + + TCP port number to listen for connections on + + + 1024 + + + 65535 + + + + + + + + 100 + 0 + + + + Enable server + + + + + + + Enable TCP server + + + + + + + @@ -110,7 +368,22 @@ - port + exportClientEnabled + exportClientHost + exportClientPort + exportClientFormat + exportServerEnabled + exportServerPort + importEnabled + importHost + importUsername + importPassword + importParameters + importPeriod + latitudeMin + latitudeMax + longitudeMin + longitudeMax diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index e7d600efd..7dec4d67c 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include @@ -859,65 +861,11 @@ QIcon *ADSBDemodGUI::getFlagIcon(const QString &country) } } -void ADSBDemodGUI::handleADSB( - const QByteArray data, - const QDateTime dateTime, - float correlation, - float correlationOnes, - bool updateModel) +// Find aircraft with icao, or create if it doesn't exist +Aircraft *ADSBDemodGUI::getAircraft(int icao, bool &newAircraft) { - const char idMap[] = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ############-##0123456789######"; - const QString categorySetA[] = { - QStringLiteral("None"), - QStringLiteral("Light"), - QStringLiteral("Small"), - QStringLiteral("Large"), - QStringLiteral("High vortex"), - QStringLiteral("Heavy"), - QStringLiteral("High performance"), - QStringLiteral("Rotorcraft") - }; - const QString categorySetB[] = { - QStringLiteral("None"), - QStringLiteral("Glider/sailplane"), - QStringLiteral("Lighter-than-air"), - QStringLiteral("Parachutist"), - QStringLiteral("Ultralight"), - QStringLiteral("Reserved"), - QStringLiteral("UAV"), - QStringLiteral("Space vehicle") - }; - const QString categorySetC[] = { - QStringLiteral("None"), - QStringLiteral("Emergency vehicle"), - QStringLiteral("Service vehicle"), - QStringLiteral("Ground obstruction"), - QStringLiteral("Cluster obstacle"), - QStringLiteral("Line obstacle"), - QStringLiteral("Reserved"), - QStringLiteral("Reserved") - }; - const QString emergencyStatus[] = { - QStringLiteral("No emergency"), - QStringLiteral("General emergency"), - QStringLiteral("Lifeguard/Medical"), - QStringLiteral("Minimum fuel"), - QStringLiteral("No communications"), - QStringLiteral("Unlawful interference"), - QStringLiteral("Downed aircraft"), - QStringLiteral("Reserved") - }; - - bool newAircraft = false; - bool updatedCallsign = false; - bool resetAnimation = false; - - int df = (data[0] >> 3) & ADS_B_DF_MASK; // Downlink format - int ca = data[0] & 0x7; // Capability - unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff); // ICAO aircraft address - int tc = (data[4] >> 3) & 0x1f; // Type code - Aircraft *aircraft; + if (m_aircraft.contains(icao)) { // Update existing aircraft info @@ -931,12 +879,7 @@ void ADSBDemodGUI::handleADSB( aircraft->m_icao = icao; aircraft->m_icaoHex = QString::number(aircraft->m_icao, 16); m_aircraft.insert(icao, aircraft); - // Check for TIS-B addresses - if ((df == 18) && !((df == 18) && ((ca == 0) || (ca == 1) || (ca == 6)))) { - aircraft->m_icaoItem->setText(QString("TIS-B %1").arg(aircraft->m_icaoHex)); - } else { - aircraft->m_icaoItem->setText(aircraft->m_icaoHex); - } + aircraft->m_icaoItem->setText(aircraft->m_icaoHex); ui->adsbData->setSortingEnabled(false); int row = ui->adsbData->rowCount(); ui->adsbData->setRowCount(row + 1); @@ -1020,20 +963,30 @@ void ADSBDemodGUI::handleADSB( // Some countries use AA-A - try these first as first letters are common prefix = aircraft->m_aircraftInfo->m_registration.left(idx + 2); if (m_prefixMap->contains(prefix)) - flag = m_prefixMap->value(prefix); + flag = m_prefixMap->value(prefix); else { // Try letters before '-' prefix = aircraft->m_aircraftInfo->m_registration.left(idx); if (m_prefixMap->contains(prefix)) - flag = m_prefixMap->value(prefix); + flag = m_prefixMap->value(prefix); } } else { - // No '-' Could be military - if ((m_militaryMap != nullptr) && (m_militaryMap->contains(aircraft->m_aircraftInfo->m_operator))) + // No '-' Could be one of a few countries or military. + // See: https://en.wikipedia.org/wiki/List_of_aircraft_registration_prefixes + if (aircraft->m_aircraftInfo->m_registration.startsWith("N")) { + flag = m_prefixMap->value("N"); // US + } else if (aircraft->m_aircraftInfo->m_registration.startsWith("JA")) { + flag = m_prefixMap->value("JA"); // Japan + } else if (aircraft->m_aircraftInfo->m_registration.startsWith("HL")) { + flag = m_prefixMap->value("HL"); // Korea + } else if (aircraft->m_aircraftInfo->m_registration.startsWith("YV")) { + flag = m_prefixMap->value("YV"); // Venezuela + } else if ((m_militaryMap != nullptr) && (m_militaryMap->contains(aircraft->m_aircraftInfo->m_operator))) { flag = m_militaryMap->value(aircraft->m_aircraftInfo->m_operator); + } } if (flag != "") { @@ -1070,6 +1023,70 @@ void ADSBDemodGUI::handleADSB( // Check to see if we need to emit a notification about this new aircraft checkStaticNotification(aircraft); } + + return aircraft; +} + +void ADSBDemodGUI::handleADSB( + const QByteArray data, + const QDateTime dateTime, + float correlation, + float correlationOnes, + bool updateModel) +{ + const char idMap[] = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ############-##0123456789######"; + const QString categorySetA[] = { + QStringLiteral("None"), + QStringLiteral("Light"), + QStringLiteral("Small"), + QStringLiteral("Large"), + QStringLiteral("High vortex"), + QStringLiteral("Heavy"), + QStringLiteral("High performance"), + QStringLiteral("Rotorcraft") + }; + const QString categorySetB[] = { + QStringLiteral("None"), + QStringLiteral("Glider/sailplane"), + QStringLiteral("Lighter-than-air"), + QStringLiteral("Parachutist"), + QStringLiteral("Ultralight"), + QStringLiteral("Reserved"), + QStringLiteral("UAV"), + QStringLiteral("Space vehicle") + }; + const QString categorySetC[] = { + QStringLiteral("None"), + QStringLiteral("Emergency vehicle"), + QStringLiteral("Service vehicle"), + QStringLiteral("Ground obstruction"), + QStringLiteral("Cluster obstacle"), + QStringLiteral("Line obstacle"), + QStringLiteral("Reserved"), + QStringLiteral("Reserved") + }; + const QString emergencyStatus[] = { + QStringLiteral("No emergency"), + QStringLiteral("General emergency"), + QStringLiteral("Lifeguard/Medical"), + QStringLiteral("Minimum fuel"), + QStringLiteral("No communications"), + QStringLiteral("Unlawful interference"), + QStringLiteral("Downed aircraft"), + QStringLiteral("Reserved") + }; + + bool newAircraft = false; + bool updatedCallsign = false; + bool resetAnimation = false; + + int df = (data[0] >> 3) & ADS_B_DF_MASK; // Downlink format + int ca = data[0] & 0x7; // Capability + unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff); // ICAO aircraft address + int tc = (data[4] >> 3) & 0x1f; // Type code + + Aircraft *aircraft = getAircraft(icao, newAircraft); + aircraft->m_time = dateTime; QTime time = dateTime.time(); aircraft->m_timeItem->setText(QString("%1:%2:%3").arg(time.hour(), 2, 10, QLatin1Char('0')).arg(time.minute(), 2, 10, QLatin1Char('0')).arg(time.second(), 2, 10, QLatin1Char('0'))); @@ -1171,94 +1188,7 @@ void ADSBDemodGUI::handleADSB( ) ) { - QString aircraftType; - - if (!aircraft->m_emitterCategory.compare("Heavy")) - { - QStringList heavy = {"B744", "B77W", "B788", "A388"}; - aircraftType = heavy[m_random.bounded(heavy.size())]; - } - else if (!aircraft->m_emitterCategory.compare("Large")) - { - QStringList large = {"A319", "A320", "A321", "B737", "B738", "B739"}; - aircraftType = large[m_random.bounded(large.size())]; - } - else if (!aircraft->m_emitterCategory.compare("Small")) - { - aircraftType = "LJ45"; - } - else if (!aircraft->m_emitterCategory.compare("Rotorcraft")) - { - aircraft->m_aircraftCat3DModel = "helicopter.glb"; - aircraft->m_modelAltitudeOffset = 4.0f; - aircraft->m_labelAltitudeOffset = 4.0f; - } - else if (!aircraft->m_emitterCategory.compare("High performance")) - { - aircraft->m_aircraftCat3DModel = "f15.glb"; - aircraft->m_modelAltitudeOffset = 1.0f; - aircraft->m_labelAltitudeOffset = 6.0f; - } - else if (!aircraft->m_emitterCategory.compare("Light")) - { - aircraftType = "C172"; - } - else if (!aircraft->m_emitterCategory.compare("Ultralight")) - { - aircraft->m_aircraftCat3DModel = "ultralight.glb"; - aircraft->m_modelAltitudeOffset = 0.55f; - aircraft->m_labelAltitudeOffset = 0.75f; - } - else if (!aircraft->m_emitterCategory.compare("Glider/sailplane")) - { - aircraft->m_aircraftCat3DModel = "glider.glb"; - aircraft->m_modelAltitudeOffset = 1.0f; - aircraft->m_labelAltitudeOffset = 1.5f; - } - else if (!aircraft->m_emitterCategory.compare("Space vehicle")) - { - aircraft->m_aircraftCat3DModel = "atlas_v.glb"; - aircraft->m_labelAltitudeOffset = 16.0f; - } - else if (!aircraft->m_emitterCategory.compare("UAV")) - { - aircraft->m_aircraftCat3DModel = "drone.glb"; - aircraft->m_labelAltitudeOffset = 1.0f; - } - else if (!aircraft->m_emitterCategory.compare("Emergency vehicle")) - { - aircraft->m_aircraftCat3DModel = "fire_truck.glb"; - aircraft->m_modelAltitudeOffset = 0.3f; - aircraft->m_labelAltitudeOffset = 2.5f; - } - else if (!aircraft->m_emitterCategory.compare("Service vehicle")) - { - aircraft->m_aircraftCat3DModel = "airport_truck.glb"; - aircraft->m_labelAltitudeOffset = 3.0f; - } - else - { - aircraftType = "A320"; - } - - if (!aircraftType.isEmpty()) - { - aircraft->m_aircraftCat3DModel = ""; - if (aircraft->m_aircraftInfo) { - aircraft->m_aircraftCat3DModel = get3DModel(aircraftType, aircraft->m_aircraftInfo->m_operatorICAO); - } - if (aircraft->m_aircraftCat3DModel.isEmpty()) { - aircraft->m_aircraftCat3DModel = get3DModel(aircraftType, aircraft->m_callsign.left(3)); - } - if (aircraft->m_aircraftCat3DModel.isEmpty()) { - aircraft->m_aircraftCat3DModel = get3DModel(aircraftType); - } - if (m_modelAltitudeOffset.contains(aircraftType)) - { - aircraft->m_modelAltitudeOffset = m_modelAltitudeOffset.value(aircraftType); - aircraft->m_labelAltitudeOffset = m_labelAltitudeOffset.value(aircraftType); - } - } + get3DModelBasedOnCategory(aircraft); // As we're changing the model, we need to reset animations to // ensure gear/flaps are in correct position on new model resetAnimation = true; @@ -2314,6 +2244,7 @@ void ADSBDemodGUI::on_feed_clicked(bool checked) m_settings.m_feedEnabled = checked; // Don't disable host/port - so they can be entered before connecting applySettings(); + applyImportSettings(); } void ADSBDemodGUI::on_notifications_clicked() @@ -3001,6 +2932,98 @@ void ADSBDemodGUI::get3DModel(Aircraft *aircraft) } } +void ADSBDemodGUI::get3DModelBasedOnCategory(Aircraft *aircraft) +{ + QString aircraftType; + + if (!aircraft->m_emitterCategory.compare("Heavy")) + { + QStringList heavy = {"B744", "B77W", "B788", "A388"}; + aircraftType = heavy[m_random.bounded(heavy.size())]; + } + else if (!aircraft->m_emitterCategory.compare("Large")) + { + QStringList large = {"A319", "A320", "A321", "B737", "B738", "B739"}; + aircraftType = large[m_random.bounded(large.size())]; + } + else if (!aircraft->m_emitterCategory.compare("Small")) + { + aircraftType = "LJ45"; + } + else if (!aircraft->m_emitterCategory.compare("Rotorcraft")) + { + aircraft->m_aircraftCat3DModel = "helicopter.glb"; + aircraft->m_modelAltitudeOffset = 4.0f; + aircraft->m_labelAltitudeOffset = 4.0f; + } + else if (!aircraft->m_emitterCategory.compare("High performance")) + { + aircraft->m_aircraftCat3DModel = "f15.glb"; + aircraft->m_modelAltitudeOffset = 1.0f; + aircraft->m_labelAltitudeOffset = 6.0f; + } + else if (!aircraft->m_emitterCategory.compare("Light")) + { + aircraftType = "C172"; + } + else if (!aircraft->m_emitterCategory.compare("Ultralight")) + { + aircraft->m_aircraftCat3DModel = "ultralight.glb"; + aircraft->m_modelAltitudeOffset = 0.55f; + aircraft->m_labelAltitudeOffset = 0.75f; + } + else if (!aircraft->m_emitterCategory.compare("Glider/sailplane")) + { + aircraft->m_aircraftCat3DModel = "glider.glb"; + aircraft->m_modelAltitudeOffset = 1.0f; + aircraft->m_labelAltitudeOffset = 1.5f; + } + else if (!aircraft->m_emitterCategory.compare("Space vehicle")) + { + aircraft->m_aircraftCat3DModel = "atlas_v.glb"; + aircraft->m_labelAltitudeOffset = 16.0f; + } + else if (!aircraft->m_emitterCategory.compare("UAV")) + { + aircraft->m_aircraftCat3DModel = "drone.glb"; + aircraft->m_labelAltitudeOffset = 1.0f; + } + else if (!aircraft->m_emitterCategory.compare("Emergency vehicle")) + { + aircraft->m_aircraftCat3DModel = "fire_truck.glb"; + aircraft->m_modelAltitudeOffset = 0.3f; + aircraft->m_labelAltitudeOffset = 2.5f; + } + else if (!aircraft->m_emitterCategory.compare("Service vehicle")) + { + aircraft->m_aircraftCat3DModel = "airport_truck.glb"; + aircraft->m_labelAltitudeOffset = 3.0f; + } + else + { + aircraftType = "A320"; + } + + if (!aircraftType.isEmpty()) + { + aircraft->m_aircraftCat3DModel = ""; + if (aircraft->m_aircraftInfo) { + aircraft->m_aircraftCat3DModel = get3DModel(aircraftType, aircraft->m_aircraftInfo->m_operatorICAO); + } + if (aircraft->m_aircraftCat3DModel.isEmpty()) { + aircraft->m_aircraftCat3DModel = get3DModel(aircraftType, aircraft->m_callsign.left(3)); + } + if (aircraft->m_aircraftCat3DModel.isEmpty()) { + aircraft->m_aircraftCat3DModel = get3DModel(aircraftType); + } + if (m_modelAltitudeOffset.contains(aircraftType)) + { + aircraft->m_modelAltitudeOffset = m_modelAltitudeOffset.value(aircraftType); + aircraft->m_labelAltitudeOffset = m_labelAltitudeOffset.value(aircraftType); + } + } +} + void ADSBDemodGUI::update3DModels() { // Look for all aircraft gltfs in 3d directory @@ -3399,6 +3422,8 @@ void ADSBDemodGUI::updatePhotoText(Aircraft *aircraft) QList sizes = icon.availableSizes(); if (sizes.size() > 0) { ui->photoFlag->setPixmap(icon.pixmap(sizes[0])); + } else { + ui->photoFlag->setPixmap(QPixmap()); } updatePhotoFlightInformation(aircraft); @@ -3512,13 +3537,11 @@ void ADSBDemodGUI::highlightAircraft(Aircraft *aircraft) // Show feed dialog void ADSBDemodGUI::feedSelect() { - ADSBDemodFeedDialog dialog(m_settings.m_feedHost, m_settings.m_feedPort, m_settings.m_feedFormat); + ADSBDemodFeedDialog dialog(&m_settings); if (dialog.exec() == QDialog::Accepted) { - m_settings.m_feedHost = dialog.m_feedHost; - m_settings.m_feedPort = dialog.m_feedPort; - m_settings.m_feedFormat = dialog.m_feedFormat; applySettings(); + applyImportSettings(); } } @@ -3799,6 +3822,11 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb displaySettings(); applySettings(true); + connect(&m_importTimer, &QTimer::timeout, this, &ADSBDemodGUI::import); + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleImportReply(QNetworkReply*))); + applyImportSettings(); + ui->map->installEventFilter(this); } @@ -3832,6 +3860,7 @@ ADSBDemodGUI::~ADSBDemodGUI() qDeleteAll(m_airspaces); qDeleteAll(m_navAids); qDeleteAll(m_3DModelMatch); + delete m_networkManager; } void ADSBDemodGUI::applySettings(bool force) @@ -3940,6 +3969,7 @@ void ADSBDemodGUI::displaySettings() initFlightInformation(); applyMapSettings(); + applyImportSettings(); restoreState(m_rollupState); blockApplySettings(false); @@ -4384,3 +4414,176 @@ bool ADSBDemodGUI::eventFilter(QObject *obj, QEvent *event) } return false; } + +void ADSBDemodGUI::applyImportSettings() +{ + m_importTimer.setInterval(m_settings.m_importPeriod * 1000); + if (m_settings.m_feedEnabled && m_settings.m_importEnabled) { + m_importTimer.start(); + } else { + m_importTimer.stop(); + } +} + +// Import ADS-B data from opensky-network via an API call +void ADSBDemodGUI::import() +{ + QString urlString = "https://"; + if (!m_settings.m_importUsername.isEmpty() && !m_settings.m_importPassword.isEmpty()) { + urlString = urlString + m_settings.m_importUsername + ":" + m_settings.m_importPassword + "@"; + } + urlString = urlString + m_settings.m_importHost + "/api/states/all"; + QChar join = '?'; + if (!m_settings.m_importParameters.isEmpty()) + { + urlString = urlString + join + m_settings.m_importParameters; + join = '&'; + } + if (!m_settings.m_importMinLatitude.isEmpty()) + { + urlString = urlString + join + "lamin=" + m_settings.m_importMinLatitude; + join = '&'; + } + if (!m_settings.m_importMaxLatitude.isEmpty()) + { + urlString = urlString + join + "lamax=" + m_settings.m_importMaxLatitude; + join = '&'; + } + if (!m_settings.m_importMinLongitude.isEmpty()) + { + urlString = urlString + join + "lomin=" + m_settings.m_importMinLongitude; + join = '&'; + } + if (!m_settings.m_importMaxLongitude.isEmpty()) + { + urlString = urlString + join + "lomax=" + m_settings.m_importMaxLongitude; + join = '&'; + } + m_networkManager->get(QNetworkRequest(QUrl(urlString))); +} + +// Handle opensky-network API call reply +void ADSBDemodGUI::handleImportReply(QNetworkReply* reply) +{ + if (reply) + { + if (!reply->error()) + { + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (document.isObject()) + { + QJsonObject obj = document.object(); + if (obj.contains("time") && obj.contains("states")) + { + int seconds = obj.value("time").toInt(); + QDateTime dateTime = QDateTime::fromSecsSinceEpoch(seconds); + QJsonArray states = obj.value("states").toArray(); + for (int i = 0; i < states.size(); i++) + { + QJsonArray state = states[i].toArray(); + int icao = state[0].toString().toInt(nullptr, 16); + + bool newAircraft; + Aircraft *aircraft = getAircraft(icao, newAircraft); + + QString callsign = state[1].toString().trimmed(); + if (!callsign.isEmpty()) + { + aircraft->m_callsign = callsign; + aircraft->m_callsignItem->setText(aircraft->m_callsign); + } + + QDateTime timePosition = dateTime; + if (state[3].isNull()) { + timePosition = dateTime.addSecs(-15); // At least 15 seconds old + } else { + timePosition = QDateTime::fromSecsSinceEpoch(state[3].toInt()); + } + aircraft->m_time = QDateTime::fromSecsSinceEpoch(state[4].toInt()); + QTime time = aircraft->m_time.time(); + aircraft->m_timeItem->setText(QString("%1:%2:%3").arg(time.hour(), 2, 10, QLatin1Char('0')).arg(time.minute(), 2, 10, QLatin1Char('0')).arg(time.second(), 2, 10, QLatin1Char('0'))); + aircraft->m_adsbFrameCount++; + aircraft->m_adsbFrameCountItem->setData(Qt::DisplayRole, aircraft->m_adsbFrameCount); + + if (timePosition > aircraft->m_positionDateTime) + { + if (!state[5].isNull() && !state[6].isNull()) + { + aircraft->m_longitude = state[5].toDouble(); + aircraft->m_latitude = state[6].toDouble(); + aircraft->m_longitudeItem->setData(Qt::DisplayRole, aircraft->m_longitude); + aircraft->m_latitudeItem->setData(Qt::DisplayRole, aircraft->m_latitude); + updatePosition(aircraft); + aircraft->m_cprValid[0] = false; + aircraft->m_cprValid[1] = false; + } + if (!state[7].isNull()) + { + aircraft->m_altitude = (int)Units::metresToFeet(state[7].toDouble()); + aircraft->m_altitudeValid = true; + aircraft->m_altitudeGNSS = false; + aircraft->m_altitudeItem->setData(Qt::DisplayRole, aircraft->m_altitude); + } + aircraft->m_positionDateTime = timePosition; + } + aircraft->m_onSurface = state[8].toBool(false); + if (!state[9].isNull()) + { + aircraft->m_speed = (int)state[9].toDouble(); + aircraft->m_speedItem->setData(Qt::DisplayRole, aircraft->m_speed); + aircraft->m_speedValid = true; + aircraft->m_speedType = Aircraft::GS; + } + if (!state[10].isNull()) + { + aircraft->m_heading = (float)state[10].toDouble(); + aircraft->m_headingItem->setData(Qt::DisplayRole, std::round(aircraft->m_heading)); + aircraft->m_headingValid = true; + aircraft->m_headingDateTime = aircraft->m_time; + } + if (!state[11].isNull()) + { + aircraft->m_verticalRate = (int)state[10].toDouble(); + aircraft->m_verticalRateItem->setData(Qt::DisplayRole, aircraft->m_verticalRate); + aircraft->m_verticalRateValid = true; + } + if (!state[14].isNull()) + { + aircraft->m_squawk = state[14].toString().toInt(); + aircraft->m_squawkItem->setText(QString("%1").arg(aircraft->m_squawk, 4, 10, QLatin1Char('0'))); + } + + // Update aircraft in map + if (aircraft->m_positionValid) + { + // Check to see if we need to start any animations + QList *animations = animate(dateTime, aircraft); + + // Update map displayed in channel + m_aircraftModel.aircraftUpdated(aircraft); + + // Send to Map feature + sendToMap(aircraft, animations); + } + + // Check to see if we need to emit a notification about this aircraft + checkDynamicNotification(aircraft); + } + } + else + { + qDebug() << "ADSBDemodGUI::handleImportReply: Document object does not contain time and states: " << document; + } + } + else + { + qDebug() << "ADSBDemodGUI::handleImportReply: Document is not an object: " << document; + } + } + else + { + qDebug() << "ADSBDemodGUI::handleImportReply: error " << reply->error(); + } + reply->deleteLater(); + } +} diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.h b/plugins/channelrx/demodadsb/adsbdemodgui.h index 7f49bbb9b..8ca93d000 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.h +++ b/plugins/channelrx/demodadsb/adsbdemodgui.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "channel/channelgui.h" #include "dsp/dsptypes.h" @@ -760,6 +761,7 @@ public: QString get3DModel(const QString &aircraft, const QString &operatorICAO) const; QString get3DModel(const QString &aircraft); void get3DModel(Aircraft *aircraft); + void get3DModelBasedOnCategory(Aircraft *aircraft); public slots: void channelMarkerChangedByCursor(); @@ -824,6 +826,9 @@ private: QHash m_modelAltitudeOffset; QHash m_labelAltitudeOffset; + QTimer m_importTimer; + QNetworkAccessManager *m_networkManager; + explicit ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); virtual ~ADSBDemodGUI(); @@ -835,6 +840,7 @@ private: void updatePosition(Aircraft *aircraft); bool updateLocalPosition(Aircraft *aircraft, double latitude, double longitude, bool surfacePosition); void sendToMap(Aircraft *aircraft, QList *animations); + Aircraft *getAircraft(int icao, bool &newAircraft); void handleADSB( const QByteArray data, const QDateTime dateTime, @@ -884,6 +890,7 @@ private: void findOnChannelMap(Aircraft *aircraft); int grayToBinary(int gray, int bits) const; void redrawMap(); + void applyImportSettings(); void leaveEvent(QEvent*); void enterEvent(QEvent*); @@ -933,6 +940,8 @@ private slots: void photoClicked(); virtual void showEvent(QShowEvent *event); virtual bool eventFilter(QObject *obj, QEvent *event); + void import(); + void handleImportReply(QNetworkReply* reply); signals: void homePositionChanged(); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.ui b/plugins/channelrx/demodadsb/adsbdemodgui.ui index f74220be9..aadb4abb3 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.ui +++ b/plugins/channelrx/demodadsb/adsbdemodgui.ui @@ -1336,6 +1336,7 @@ logEnable logFilename logOpen + findOnMapFeature devicesRefresh device adsbData diff --git a/plugins/channelrx/demodadsb/adsbdemodsettings.cpp b/plugins/channelrx/demodadsb/adsbdemodsettings.cpp index 375d2cda1..5790fa66e 100644 --- a/plugins/channelrx/demodadsb/adsbdemodsettings.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodsettings.cpp @@ -42,9 +42,22 @@ void ADSBDemodSettings::resetToDefaults() m_samplesPerBit = 4; m_removeTimeout = 60; m_feedEnabled = false; - m_feedHost = "feed.adsbexchange.com"; - m_feedPort = 30005; - m_feedFormat = BeastBinary; + m_exportClientEnabled = true; + m_exportClientHost = "feed.adsbexchange.com"; + m_exportClientPort = 30005; + m_exportClientFormat = BeastBinary; + m_exportServerEnabled = false; + m_exportServerPort = 30005; + m_importEnabled = false; + m_importHost = "opensky-network.org"; + m_importUsername = ""; + m_importPassword = ""; + m_importParameters = ""; + m_importPeriod = 10.0; + m_importMinLatitude = ""; + m_importMaxLatitude = ""; + m_importMinLongitude = ""; + m_importMaxLongitude = ""; m_rgbColor = QColor(244, 151, 57).rgb(); m_title = "ADS-B Demodulator"; m_streamIndex = 0; @@ -94,8 +107,8 @@ QByteArray ADSBDemodSettings::serialize() const s.writeS32(4, m_samplesPerBit); s.writeS32(5, m_removeTimeout); s.writeBool(6, m_feedEnabled); - s.writeString(7, m_feedHost); - s.writeU32(8, m_feedPort); + s.writeString(7, m_exportClientHost); + s.writeU32(8, m_exportClientPort); s.writeU32(9, m_rgbColor); if (m_channelMarker) { @@ -116,7 +129,7 @@ QByteArray ADSBDemodSettings::serialize() const s.writeBool(21, m_flightPaths); s.writeS32(22, m_deviceIndex); s.writeBool(23, m_siUnits); - s.writeS32(24, (int)m_feedFormat); + s.writeS32(24, (int)m_exportClientFormat); s.writeString(25, m_tableFontName); s.writeS32(26, m_tableFontSize); s.writeBool(27, m_displayDemodStats); @@ -146,6 +159,20 @@ QByteArray ADSBDemodSettings::serialize() const s.writeBool(44, m_verboseModelMatching); s.writeS32(45, m_airfieldElevation); + s.writeBool(46, m_exportClientEnabled); + s.writeBool(47, m_exportServerEnabled); + s.writeBool(48, m_exportServerPort); + s.writeBool(49, m_importEnabled); + s.writeString(50, m_importHost); + s.writeString(51, m_importUsername); + s.writeString(52, m_importPassword); + s.writeString(53, m_importParameters); + s.writeFloat(54, m_importPeriod); + s.writeString(55, m_importMinLatitude); + s.writeString(56, m_importMaxLatitude); + s.writeString(57, m_importMinLongitude); + s.writeString(58, m_importMaxLongitude); + for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); } @@ -187,12 +214,12 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data) d.readS32(4, &m_samplesPerBit, 4); d.readS32(5, &m_removeTimeout, 60); d.readBool(6, &m_feedEnabled, false); - d.readString(7, &m_feedHost, "feed.adsbexchange.com"); + d.readString(7, &m_exportClientHost, "feed.adsbexchange.com"); d.readU32(8, &utmp, 0); if ((utmp > 1023) && (utmp < 65535)) { - m_feedPort = utmp; + m_exportClientPort = utmp; } else { - m_feedPort = 30005; + m_exportClientPort = 30005; } d.readU32(9, &m_rgbColor, QColor(244, 151, 57).rgb()); @@ -219,7 +246,7 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data) d.readBool(21, &m_flightPaths, true); d.readS32(22, &m_deviceIndex, -1); d.readBool(23, &m_siUnits, false); - d.readS32(24, (int *) &m_feedFormat, BeastBinary); + d.readS32(24, (int *) &m_exportClientFormat, BeastBinary); d.readString(25, &m_tableFontName, "Liberation Sans"); d.readS32(26, &m_tableFontSize, 9); d.readBool(27, &m_displayDemodStats, false); @@ -253,6 +280,25 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data) d.readBool(44, &m_verboseModelMatching, false); d.readS32(45, &m_airfieldElevation, 0); + d.readBool(46, &m_exportClientEnabled, true); + d.readBool(47, &m_exportServerEnabled, true); + d.readU32(48, &utmp, 0); + if ((utmp > 1023) && (utmp < 65535)) { + m_exportServerPort = utmp; + } else { + m_exportServerPort = 30005; + } + d.readBool(49, &m_importEnabled, false); + d.readString(50, &m_importHost, "opensky-network.org"); + d.readString(51, &m_importUsername, ""); + d.readString(52, &m_importPassword, ""); + d.readString(53, &m_importParameters, ""); + d.readFloat(54, &m_importPeriod, 10.0f); + d.readString(55, &m_importMinLatitude, ""); + d.readString(56, &m_importMaxLatitude, ""); + d.readString(57, &m_importMinLongitude, ""); + d.readString(58, &m_importMaxLongitude, ""); + for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); } diff --git a/plugins/channelrx/demodadsb/adsbdemodsettings.h b/plugins/channelrx/demodadsb/adsbdemodsettings.h index 86c21153d..88ae38cc4 100644 --- a/plugins/channelrx/demodadsb/adsbdemodsettings.h +++ b/plugins/channelrx/demodadsb/adsbdemodsettings.h @@ -86,13 +86,27 @@ struct ADSBDemodSettings Real m_correlationThreshold; //!< Correlation power threshold in dB int m_samplesPerBit; int m_removeTimeout; //!< Time in seconds before removing an aircraft, unless a new frame is received + bool m_feedEnabled; - QString m_feedHost; - uint16_t m_feedPort; + bool m_exportClientEnabled; + QString m_exportClientHost; + uint16_t m_exportClientPort; enum FeedFormat { BeastBinary, BeastHex - } m_feedFormat; + } m_exportClientFormat; + bool m_exportServerEnabled; + uint16_t m_exportServerPort; + bool m_importEnabled; + QString m_importHost; + QString m_importUsername; + QString m_importPassword; + QString m_importParameters; + float m_importPeriod; + QString m_importMinLatitude; + QString m_importMaxLatitude; + QString m_importMinLongitude; + QString m_importMaxLongitude; quint32 m_rgbColor; QString m_title; diff --git a/plugins/channelrx/demodadsb/adsbdemodworker.cpp b/plugins/channelrx/demodadsb/adsbdemodworker.cpp index 8a66508d9..d03e1455f 100644 --- a/plugins/channelrx/demodadsb/adsbdemodworker.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodworker.cpp @@ -28,6 +28,57 @@ MESSAGE_CLASS_DEFINITION(ADSBDemodWorker::MsgConfigureADSBDemodWorker, Message) +ADSBBeastServer::ADSBBeastServer() +{ +} + +void ADSBBeastServer::listen(quint16 port) +{ + QTcpServer::listen(QHostAddress::Any, port); + qDebug() << "ADSBBeastServer listening on port " << serverPort(); +} + +void ADSBBeastServer::incomingConnection(qintptr socket) +{ + qDebug() << "ADSBBeastServer client connected"; + QTcpSocket *s = new QTcpSocket(this); + connect(s, &QTcpSocket::readyRead, this, &ADSBBeastServer::readClient); + connect(s, SIGNAL(disconnected()), this, SLOT(discardClient())); + s->setSocketDescriptor(socket); + m_clients.append(s); +} + +void ADSBBeastServer::send(const char *data, int length) +{ + // Send frame to all clients + for (auto client : m_clients) { + client->write(data, length); + } +} + +void ADSBBeastServer::close() +{ + for (auto client : m_clients) { + client->deleteLater(); + } + m_clients.clear(); + QTcpServer::close(); +} + +void ADSBBeastServer::readClient() +{ + QTcpSocket *socket = (QTcpSocket *)sender(); + socket->readAll(); +} + +void ADSBBeastServer::discardClient() +{ + qDebug() << "ADSBBeastServer client disconnected"; + QTcpSocket *socket = (QTcpSocket*)sender(); + socket->deleteLater(); + m_clients.removeAll(socket); +} + ADSBDemodWorker::ADSBDemodWorker() : m_running(false), m_mutex(QMutex::Recursive) @@ -41,6 +92,8 @@ ADSBDemodWorker::ADSBDemodWorker() : #else connect(&m_socket, &QAbstractSocket::errorOccurred, this, &ADSBDemodWorker::errorOccurred); #endif + m_startTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + m_heartbeatTimer.start(60*1000); } ADSBDemodWorker::~ADSBDemodWorker() @@ -63,7 +116,6 @@ bool ADSBDemodWorker::startWork() } connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); - m_heartbeatTimer.start(60*1000); m_running = true; return m_running; } @@ -76,7 +128,6 @@ void ADSBDemodWorker::stopWork() return; } - m_heartbeatTimer.stop(); disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); m_running = false; } @@ -119,23 +170,43 @@ void ADSBDemodWorker::applySettings(const ADSBDemodSettings& settings, bool forc { qDebug() << "ADSBDemodWorker::applySettings:" << " m_feedEnabled: " << settings.m_feedEnabled - << " m_feedHost: " << settings.m_feedHost - << " m_feedPort: " << settings.m_feedPort - << " m_feedFormat: " << settings.m_feedFormat + << " m_exportClientEnabled: " << settings.m_exportClientEnabled + << " m_exportClientHost: " << settings.m_exportClientHost + << " m_exportClientPort: " << settings.m_exportClientPort + << " m_exportClientFormat: " << settings.m_exportClientFormat + << " m_exportServerEnabled: " << settings.m_exportServerEnabled + << " m_exportServerPort: " << settings.m_exportServerPort << " m_logEnabled: " << settings.m_logEnabled << " m_logFilename: " << settings.m_logFilename << " force: " << force; if ((settings.m_feedEnabled != m_settings.m_feedEnabled) - || (settings.m_feedHost != m_settings.m_feedHost) - || (settings.m_feedPort != m_settings.m_feedPort) || force) + || (settings.m_exportClientEnabled != m_settings.m_exportClientEnabled) + || (settings.m_exportClientHost != m_settings.m_exportClientHost) + || (settings.m_exportClientPort != m_settings.m_exportClientPort) + || force) { // Close any existing connection - if (m_socket.isOpen()) + if (m_socket.isOpen()) { m_socket.close(); + } // Open connection - if (settings.m_feedEnabled) - m_socket.connectToHost(settings.m_feedHost, settings.m_feedPort); + if (settings.m_feedEnabled && settings.m_exportClientEnabled) { + m_socket.connectToHost(settings.m_exportClientHost, settings.m_exportClientPort); + } + } + + if ((settings.m_feedEnabled != m_settings.m_feedEnabled) + || (settings.m_exportServerEnabled != m_settings.m_exportServerEnabled) + || (settings.m_exportServerPort != m_settings.m_exportServerPort) + || force) + { + if (m_beastServer.isListening()) { + m_beastServer.close(); + } + if (settings.m_feedEnabled && settings.m_exportServerEnabled) { + m_beastServer.listen(settings.m_exportServerPort); + } } if ((settings.m_logEnabled != m_settings.m_logEnabled) @@ -173,7 +244,7 @@ void ADSBDemodWorker::applySettings(const ADSBDemodSettings& settings, bool forc void ADSBDemodWorker::connected() { - qDebug() << "ADSBDemodWorker::connected " << m_settings.m_feedHost; + qDebug() << "ADSBDemodWorker::connected " << m_settings.m_exportClientHost; } void ADSBDemodWorker::disconnected() @@ -195,11 +266,12 @@ void ADSBDemodWorker::recv() void ADSBDemodWorker::send(const char *data, int length) { - if (m_settings.m_feedEnabled) + if (m_settings.m_feedEnabled && m_settings.m_exportClientEnabled) { // Reopen connection if it was lost - if (!m_socket.isOpen()) - m_socket.connectToHost(m_settings.m_feedHost, m_settings.m_feedPort); + if (!m_socket.isOpen()) { + m_socket.connectToHost(m_settings.m_exportClientHost, m_settings.m_exportClientPort); + } // Send data m_socket.write(data, length); } @@ -210,8 +282,9 @@ void ADSBDemodWorker::send(const char *data, int length) char *ADSBDemodWorker::escape(char *p, char c) { *p++ = c; - if (c == BEAST_ESC) + if (c == BEAST_ESC) { *p++ = BEAST_ESC; + } return p; } @@ -220,13 +293,14 @@ char *ADSBDemodWorker::escape(char *p, char c) // Log to .csv file void ADSBDemodWorker::handleADSB(QByteArray data, const QDateTime dateTime, float correlation) { - if (m_logFile.isOpen()) - { + if (m_logFile.isOpen()) { m_logStream << dateTime.date().toString() << "," << dateTime.time().toString() << "," << data.toHex() << "," << correlation << "\n"; } - if (m_settings.m_feedEnabled) + + if (m_settings.m_feedEnabled && (m_settings.m_exportClientEnabled || m_settings.m_exportServerEnabled)) { - if (m_settings.m_feedFormat == ADSBDemodSettings::BeastBinary) + if ((m_settings.m_exportClientEnabled && (m_settings.m_exportClientFormat == ADSBDemodSettings::BeastBinary)) + || m_settings.m_exportServerEnabled) { char beastBinary[2+6*2+1*2+14*2]; int length; @@ -234,20 +308,21 @@ void ADSBDemodWorker::handleADSB(QByteArray data, const QDateTime dateTime, floa qint64 timestamp; unsigned char signalStrength; - timestamp = dateTime.toMSecsSinceEpoch(); + // Timestamp seems to be 12MHz ticks since device started + timestamp = (dateTime.toMSecsSinceEpoch() - m_startTime) * 12000; - if (correlation > 255) + if (correlation > 255) { signalStrength = 255; - if (correlation < 1) + } else if (correlation < 1) { signalStrength = 1; - else + } else { signalStrength = (unsigned char)correlation; + } *p++ = BEAST_ESC; *p++ = '3'; // Mode-S long - p = escape(p, timestamp >> 56); // Big-endian timestamp - p = escape(p, timestamp >> 48); + p = escape(p, timestamp >> 48); // Big-endian timestamp p = escape(p, timestamp >> 32); p = escape(p, timestamp >> 24); p = escape(p, timestamp >> 16); @@ -256,14 +331,20 @@ void ADSBDemodWorker::handleADSB(QByteArray data, const QDateTime dateTime, floa p = escape(p, signalStrength); // Signal strength - for (int i = 0; i < data.length(); i++) // ADS-B data + for (int i = 0; i < data.length(); i++) { // ADS-B data p = escape(p, data[i]); + } length = p - beastBinary; - send(beastBinary, length); + if ((m_settings.m_exportClientEnabled) && (m_settings.m_exportClientFormat == ADSBDemodSettings::BeastBinary)) { + send(beastBinary, length); + } + if (m_settings.m_exportServerEnabled) { + m_beastServer.send(beastBinary, length); + } } - else if (m_settings.m_feedFormat == ADSBDemodSettings::BeastHex) + if (m_settings.m_exportClientEnabled && (m_settings.m_exportClientFormat == ADSBDemodSettings::BeastHex)) { QString beastHex = "*" + data.toHex() + ";\n"; send(beastHex.toUtf8(), beastHex.size()); @@ -274,6 +355,14 @@ void ADSBDemodWorker::handleADSB(QByteArray data, const QDateTime dateTime, floa // Periodically send heartbeat to keep connection alive void ADSBDemodWorker::heartbeat() { - const char heartbeat[] = {BEAST_ESC, '1', 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Mode AC packet - send(heartbeat, sizeof(heartbeat)); + if (m_running) + { + const char heartbeat[] = {BEAST_ESC, '1', 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Mode AC packet + if (m_settings.m_exportClientEnabled) { + send(heartbeat, sizeof(heartbeat)); + } + if (m_settings.m_exportServerEnabled) { + m_beastServer.send(heartbeat, sizeof(heartbeat)); + } + } } diff --git a/plugins/channelrx/demodadsb/adsbdemodworker.h b/plugins/channelrx/demodadsb/adsbdemodworker.h index b60b122be..858b4fa11 100644 --- a/plugins/channelrx/demodadsb/adsbdemodworker.h +++ b/plugins/channelrx/demodadsb/adsbdemodworker.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,27 @@ #include "adsbdemodsettings.h" +// Beast binary server for sending ADS-B data to OpenSky Network (and others) +class ADSBBeastServer : public QTcpServer +{ + Q_OBJECT + +private: + QList m_clients; + +public: + ADSBBeastServer(); + void listen(quint16 port = 30005); + void incomingConnection(qintptr socket); + void send(const char *data, int length); + void close(); + +private slots: + void readClient(); + void discardClient(); +}; + +// Worker that forwards ADS-B frames to various aggregators class ADSBDemodWorker : public QObject { Q_OBJECT @@ -75,6 +97,8 @@ private: QTcpSocket m_socket; QFile m_logFile; QTextStream m_logStream; + qint64 m_startTime; + ADSBBeastServer m_beastServer; bool handleMessage(const Message& cmd); void applySettings(const ADSBDemodSettings& settings, bool force = false); diff --git a/plugins/channelrx/demodadsb/readme.md b/plugins/channelrx/demodadsb/readme.md index 5feb3ef2e..54e2de187 100644 --- a/plugins/channelrx/demodadsb/readme.md +++ b/plugins/channelrx/demodadsb/readme.md @@ -106,16 +106,26 @@ To use this feature, an [aviationstack](https://aviationstack.com) API Key must

16: Feed

-Checking Feed enables feeding received ADS-B frames to aggregators such as ADS-B Exchange: https://www.adsbexchange.com or ADSBHub -: https://www.adsbhub.org. Right clicking on the Feed button opens the Feed Settings dialog. +Checking Feed enables feeding received ADS-B frames to aggregators such as [ADS-B Exchange](https://www.adsbexchange.com), [ADSBHub](https://www.adsbhub.org) or [OpenSky Network](https://opensky-network.org/) +and receiving aircraft state from anywhere in the world from [OpenSky Network](https://opensky-network.org/). Right clicking on the Feed button opens the Feed Settings dialog. -The server hostname and port to send the frames to should be entered in the Server and Port fields, with the appropriate format selected: +The ADS-B plugin can export ADS-B frames acting as both a client and server. When a client, the ADS-B plugin opens a connection to a remote host. When a server, the remote computer connects to this computer. -* For ADS-B Exchange, set Server hostname to feed.adsbexchange.com, Port to 30005 and Format to Beast binary. You can check for successful feeding (after about 30 seconds) at: https://www.adsbexchange.com/myip/ -* For ADSBHub, set Server hostname to data.adsbhub.org, Port to 5002 and Format to Beast hex. You will need to have setup an account on ADSBHub first. You can check for successful feeding at: https://www.adsbhub.org/statistic.php +As a client: + +* For ADS-B Exchange, check Enable Client, set Server hostname to feed.adsbexchange.com, Port to 30005 and Format to Beast binary. You can check for successful feeding (after about 30 seconds) at: https://www.adsbexchange.com/myip/ +* For ADSBHub, check Enable Client, set Server hostname to data.adsbhub.org, Port to 5002 and Format to Beast hex. You will need to have setup an account on ADSBHub first. You can check for successful feeding at: https://www.adsbhub.org/statistic.php + +As a server: + +* For OpenSky Network, check Enable Server and set Port to 30005. You can check for successfull feeding at: https://opensky-network.org/my-opensky The Beast binary and Hex formats are as detailed here: https://wiki.jetvision.de/wiki/Mode-S_Beast:Data_Output_Formats +When Enable import is checked, aircraft data for aircraft anywhere in the world can be imported from OpenSky Network. +A username and password are not required, but when specified, this allows the update period to be reduced to 5 seconds instead of 10 seconds. +To limit network traffic and processing power requirements, a geographical region can be set via the mininum and maximum latitude and longitude fields. +

17: Open Notifications Dialog

When clicked, opens the Notifications Dialog, which allows speech notifications or programs/scripts to be run when aircraft matching user-defined rules are seen. diff --git a/swagger/sdrangel/api/swagger/include/ADSBDemod.yaml b/swagger/sdrangel/api/swagger/include/ADSBDemod.yaml index 14d7690d8..da23a93f3 100644 --- a/swagger/sdrangel/api/swagger/include/ADSBDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/ADSBDemod.yaml @@ -35,22 +35,50 @@ ADSBDemodSettings: description: Number of taps per phase in channel interpolator removeTimeout: type: integer - beastEnabled: + feedEnabled: + type: integer + exportClientEnabled: type: integer description: > - Send data to beast server + Send data to server * 0 - Do not send data * 1 - Send data - beastHost: + exportClientHost: + description: Host to send data to type: string - beastPort: + exportClientPort: type: integer - feedFormat: + exportClientFormat: type: integer description: > Format of sent data * 0 - Beast binary - * 1 - Beast index + * 1 - Beast hex + exportServerEnabled: + type: integer + exportServerPort: + type: integer + importEnabled: + type: integer + importHost: + type: string + importUsername: + type: string + importPassword: + type: string + importParameters: + type: string + importPeriod: + type: number + format: float + importMinLatitude: + type: string + importMaxLatitude: + type: string + importMinLongitude: + type: string + importMaxLongitude: + type: string logFilename: type: string logEnabled: diff --git a/swagger/sdrangel/code/qt5/client/SWGADSBDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGADSBDemodSettings.cpp index 9b85b35a1..882ebd9a2 100644 --- a/swagger/sdrangel/code/qt5/client/SWGADSBDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGADSBDemodSettings.cpp @@ -46,14 +46,40 @@ SWGADSBDemodSettings::SWGADSBDemodSettings() { m_interpolator_taps_per_phase_isSet = false; remove_timeout = 0; m_remove_timeout_isSet = false; - beast_enabled = 0; - m_beast_enabled_isSet = false; - beast_host = nullptr; - m_beast_host_isSet = false; - beast_port = 0; - m_beast_port_isSet = false; - feed_format = 0; - m_feed_format_isSet = false; + feed_enabled = 0; + m_feed_enabled_isSet = false; + export_client_enabled = 0; + m_export_client_enabled_isSet = false; + export_client_host = nullptr; + m_export_client_host_isSet = false; + export_client_port = 0; + m_export_client_port_isSet = false; + export_client_format = 0; + m_export_client_format_isSet = false; + export_server_enabled = 0; + m_export_server_enabled_isSet = false; + export_server_port = 0; + m_export_server_port_isSet = false; + import_enabled = 0; + m_import_enabled_isSet = false; + import_host = nullptr; + m_import_host_isSet = false; + import_username = nullptr; + m_import_username_isSet = false; + import_password = nullptr; + m_import_password_isSet = false; + import_parameters = nullptr; + m_import_parameters_isSet = false; + import_period = 0.0f; + m_import_period_isSet = false; + import_min_latitude = nullptr; + m_import_min_latitude_isSet = false; + import_max_latitude = nullptr; + m_import_max_latitude_isSet = false; + import_min_longitude = nullptr; + m_import_min_longitude_isSet = false; + import_max_longitude = nullptr; + m_import_max_longitude_isSet = false; log_filename = nullptr; m_log_filename_isSet = false; log_enabled = 0; @@ -104,14 +130,40 @@ SWGADSBDemodSettings::init() { m_interpolator_taps_per_phase_isSet = false; remove_timeout = 0; m_remove_timeout_isSet = false; - beast_enabled = 0; - m_beast_enabled_isSet = false; - beast_host = new QString(""); - m_beast_host_isSet = false; - beast_port = 0; - m_beast_port_isSet = false; - feed_format = 0; - m_feed_format_isSet = false; + feed_enabled = 0; + m_feed_enabled_isSet = false; + export_client_enabled = 0; + m_export_client_enabled_isSet = false; + export_client_host = new QString(""); + m_export_client_host_isSet = false; + export_client_port = 0; + m_export_client_port_isSet = false; + export_client_format = 0; + m_export_client_format_isSet = false; + export_server_enabled = 0; + m_export_server_enabled_isSet = false; + export_server_port = 0; + m_export_server_port_isSet = false; + import_enabled = 0; + m_import_enabled_isSet = false; + import_host = new QString(""); + m_import_host_isSet = false; + import_username = new QString(""); + m_import_username_isSet = false; + import_password = new QString(""); + m_import_password_isSet = false; + import_parameters = new QString(""); + m_import_parameters_isSet = false; + import_period = 0.0f; + m_import_period_isSet = false; + import_min_latitude = new QString(""); + m_import_min_latitude_isSet = false; + import_max_latitude = new QString(""); + m_import_max_latitude_isSet = false; + import_min_longitude = new QString(""); + m_import_min_longitude_isSet = false; + import_max_longitude = new QString(""); + m_import_max_longitude_isSet = false; log_filename = new QString(""); m_log_filename_isSet = false; log_enabled = 0; @@ -150,11 +202,40 @@ SWGADSBDemodSettings::cleanup() { - if(beast_host != nullptr) { - delete beast_host; + + if(export_client_host != nullptr) { + delete export_client_host; } + + + + if(import_host != nullptr) { + delete import_host; + } + if(import_username != nullptr) { + delete import_username; + } + if(import_password != nullptr) { + delete import_password; + } + if(import_parameters != nullptr) { + delete import_parameters; + } + + if(import_min_latitude != nullptr) { + delete import_min_latitude; + } + if(import_max_latitude != nullptr) { + delete import_max_latitude; + } + if(import_min_longitude != nullptr) { + delete import_min_longitude; + } + if(import_max_longitude != nullptr) { + delete import_max_longitude; + } if(log_filename != nullptr) { delete log_filename; } @@ -208,13 +289,39 @@ SWGADSBDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&remove_timeout, pJson["removeTimeout"], "qint32", ""); - ::SWGSDRangel::setValue(&beast_enabled, pJson["beastEnabled"], "qint32", ""); + ::SWGSDRangel::setValue(&feed_enabled, pJson["feedEnabled"], "qint32", ""); - ::SWGSDRangel::setValue(&beast_host, pJson["beastHost"], "QString", "QString"); + ::SWGSDRangel::setValue(&export_client_enabled, pJson["exportClientEnabled"], "qint32", ""); - ::SWGSDRangel::setValue(&beast_port, pJson["beastPort"], "qint32", ""); + ::SWGSDRangel::setValue(&export_client_host, pJson["exportClientHost"], "QString", "QString"); - ::SWGSDRangel::setValue(&feed_format, pJson["feedFormat"], "qint32", ""); + ::SWGSDRangel::setValue(&export_client_port, pJson["exportClientPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&export_client_format, pJson["exportClientFormat"], "qint32", ""); + + ::SWGSDRangel::setValue(&export_server_enabled, pJson["exportServerEnabled"], "qint32", ""); + + ::SWGSDRangel::setValue(&export_server_port, pJson["exportServerPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&import_enabled, pJson["importEnabled"], "qint32", ""); + + ::SWGSDRangel::setValue(&import_host, pJson["importHost"], "QString", "QString"); + + ::SWGSDRangel::setValue(&import_username, pJson["importUsername"], "QString", "QString"); + + ::SWGSDRangel::setValue(&import_password, pJson["importPassword"], "QString", "QString"); + + ::SWGSDRangel::setValue(&import_parameters, pJson["importParameters"], "QString", "QString"); + + ::SWGSDRangel::setValue(&import_period, pJson["importPeriod"], "float", ""); + + ::SWGSDRangel::setValue(&import_min_latitude, pJson["importMinLatitude"], "QString", "QString"); + + ::SWGSDRangel::setValue(&import_max_latitude, pJson["importMaxLatitude"], "QString", "QString"); + + ::SWGSDRangel::setValue(&import_min_longitude, pJson["importMinLongitude"], "QString", "QString"); + + ::SWGSDRangel::setValue(&import_max_longitude, pJson["importMaxLongitude"], "QString", "QString"); ::SWGSDRangel::setValue(&log_filename, pJson["logFilename"], "QString", "QString"); @@ -283,17 +390,56 @@ SWGADSBDemodSettings::asJsonObject() { if(m_remove_timeout_isSet){ obj->insert("removeTimeout", QJsonValue(remove_timeout)); } - if(m_beast_enabled_isSet){ - obj->insert("beastEnabled", QJsonValue(beast_enabled)); + if(m_feed_enabled_isSet){ + obj->insert("feedEnabled", QJsonValue(feed_enabled)); } - if(beast_host != nullptr && *beast_host != QString("")){ - toJsonValue(QString("beastHost"), beast_host, obj, QString("QString")); + if(m_export_client_enabled_isSet){ + obj->insert("exportClientEnabled", QJsonValue(export_client_enabled)); } - if(m_beast_port_isSet){ - obj->insert("beastPort", QJsonValue(beast_port)); + if(export_client_host != nullptr && *export_client_host != QString("")){ + toJsonValue(QString("exportClientHost"), export_client_host, obj, QString("QString")); } - if(m_feed_format_isSet){ - obj->insert("feedFormat", QJsonValue(feed_format)); + if(m_export_client_port_isSet){ + obj->insert("exportClientPort", QJsonValue(export_client_port)); + } + if(m_export_client_format_isSet){ + obj->insert("exportClientFormat", QJsonValue(export_client_format)); + } + if(m_export_server_enabled_isSet){ + obj->insert("exportServerEnabled", QJsonValue(export_server_enabled)); + } + if(m_export_server_port_isSet){ + obj->insert("exportServerPort", QJsonValue(export_server_port)); + } + if(m_import_enabled_isSet){ + obj->insert("importEnabled", QJsonValue(import_enabled)); + } + if(import_host != nullptr && *import_host != QString("")){ + toJsonValue(QString("importHost"), import_host, obj, QString("QString")); + } + if(import_username != nullptr && *import_username != QString("")){ + toJsonValue(QString("importUsername"), import_username, obj, QString("QString")); + } + if(import_password != nullptr && *import_password != QString("")){ + toJsonValue(QString("importPassword"), import_password, obj, QString("QString")); + } + if(import_parameters != nullptr && *import_parameters != QString("")){ + toJsonValue(QString("importParameters"), import_parameters, obj, QString("QString")); + } + if(m_import_period_isSet){ + obj->insert("importPeriod", QJsonValue(import_period)); + } + if(import_min_latitude != nullptr && *import_min_latitude != QString("")){ + toJsonValue(QString("importMinLatitude"), import_min_latitude, obj, QString("QString")); + } + if(import_max_latitude != nullptr && *import_max_latitude != QString("")){ + toJsonValue(QString("importMaxLatitude"), import_max_latitude, obj, QString("QString")); + } + if(import_min_longitude != nullptr && *import_min_longitude != QString("")){ + toJsonValue(QString("importMinLongitude"), import_min_longitude, obj, QString("QString")); + } + if(import_max_longitude != nullptr && *import_max_longitude != QString("")){ + toJsonValue(QString("importMaxLongitude"), import_max_longitude, obj, QString("QString")); } if(log_filename != nullptr && *log_filename != QString("")){ toJsonValue(QString("logFilename"), log_filename, obj, QString("QString")); @@ -426,43 +572,173 @@ SWGADSBDemodSettings::setRemoveTimeout(qint32 remove_timeout) { } qint32 -SWGADSBDemodSettings::getBeastEnabled() { - return beast_enabled; +SWGADSBDemodSettings::getFeedEnabled() { + return feed_enabled; } void -SWGADSBDemodSettings::setBeastEnabled(qint32 beast_enabled) { - this->beast_enabled = beast_enabled; - this->m_beast_enabled_isSet = true; +SWGADSBDemodSettings::setFeedEnabled(qint32 feed_enabled) { + this->feed_enabled = feed_enabled; + this->m_feed_enabled_isSet = true; +} + +qint32 +SWGADSBDemodSettings::getExportClientEnabled() { + return export_client_enabled; +} +void +SWGADSBDemodSettings::setExportClientEnabled(qint32 export_client_enabled) { + this->export_client_enabled = export_client_enabled; + this->m_export_client_enabled_isSet = true; } QString* -SWGADSBDemodSettings::getBeastHost() { - return beast_host; +SWGADSBDemodSettings::getExportClientHost() { + return export_client_host; } void -SWGADSBDemodSettings::setBeastHost(QString* beast_host) { - this->beast_host = beast_host; - this->m_beast_host_isSet = true; +SWGADSBDemodSettings::setExportClientHost(QString* export_client_host) { + this->export_client_host = export_client_host; + this->m_export_client_host_isSet = true; } qint32 -SWGADSBDemodSettings::getBeastPort() { - return beast_port; +SWGADSBDemodSettings::getExportClientPort() { + return export_client_port; } void -SWGADSBDemodSettings::setBeastPort(qint32 beast_port) { - this->beast_port = beast_port; - this->m_beast_port_isSet = true; +SWGADSBDemodSettings::setExportClientPort(qint32 export_client_port) { + this->export_client_port = export_client_port; + this->m_export_client_port_isSet = true; } qint32 -SWGADSBDemodSettings::getFeedFormat() { - return feed_format; +SWGADSBDemodSettings::getExportClientFormat() { + return export_client_format; } void -SWGADSBDemodSettings::setFeedFormat(qint32 feed_format) { - this->feed_format = feed_format; - this->m_feed_format_isSet = true; +SWGADSBDemodSettings::setExportClientFormat(qint32 export_client_format) { + this->export_client_format = export_client_format; + this->m_export_client_format_isSet = true; +} + +qint32 +SWGADSBDemodSettings::getExportServerEnabled() { + return export_server_enabled; +} +void +SWGADSBDemodSettings::setExportServerEnabled(qint32 export_server_enabled) { + this->export_server_enabled = export_server_enabled; + this->m_export_server_enabled_isSet = true; +} + +qint32 +SWGADSBDemodSettings::getExportServerPort() { + return export_server_port; +} +void +SWGADSBDemodSettings::setExportServerPort(qint32 export_server_port) { + this->export_server_port = export_server_port; + this->m_export_server_port_isSet = true; +} + +qint32 +SWGADSBDemodSettings::getImportEnabled() { + return import_enabled; +} +void +SWGADSBDemodSettings::setImportEnabled(qint32 import_enabled) { + this->import_enabled = import_enabled; + this->m_import_enabled_isSet = true; +} + +QString* +SWGADSBDemodSettings::getImportHost() { + return import_host; +} +void +SWGADSBDemodSettings::setImportHost(QString* import_host) { + this->import_host = import_host; + this->m_import_host_isSet = true; +} + +QString* +SWGADSBDemodSettings::getImportUsername() { + return import_username; +} +void +SWGADSBDemodSettings::setImportUsername(QString* import_username) { + this->import_username = import_username; + this->m_import_username_isSet = true; +} + +QString* +SWGADSBDemodSettings::getImportPassword() { + return import_password; +} +void +SWGADSBDemodSettings::setImportPassword(QString* import_password) { + this->import_password = import_password; + this->m_import_password_isSet = true; +} + +QString* +SWGADSBDemodSettings::getImportParameters() { + return import_parameters; +} +void +SWGADSBDemodSettings::setImportParameters(QString* import_parameters) { + this->import_parameters = import_parameters; + this->m_import_parameters_isSet = true; +} + +float +SWGADSBDemodSettings::getImportPeriod() { + return import_period; +} +void +SWGADSBDemodSettings::setImportPeriod(float import_period) { + this->import_period = import_period; + this->m_import_period_isSet = true; +} + +QString* +SWGADSBDemodSettings::getImportMinLatitude() { + return import_min_latitude; +} +void +SWGADSBDemodSettings::setImportMinLatitude(QString* import_min_latitude) { + this->import_min_latitude = import_min_latitude; + this->m_import_min_latitude_isSet = true; +} + +QString* +SWGADSBDemodSettings::getImportMaxLatitude() { + return import_max_latitude; +} +void +SWGADSBDemodSettings::setImportMaxLatitude(QString* import_max_latitude) { + this->import_max_latitude = import_max_latitude; + this->m_import_max_latitude_isSet = true; +} + +QString* +SWGADSBDemodSettings::getImportMinLongitude() { + return import_min_longitude; +} +void +SWGADSBDemodSettings::setImportMinLongitude(QString* import_min_longitude) { + this->import_min_longitude = import_min_longitude; + this->m_import_min_longitude_isSet = true; +} + +QString* +SWGADSBDemodSettings::getImportMaxLongitude() { + return import_max_longitude; +} +void +SWGADSBDemodSettings::setImportMaxLongitude(QString* import_max_longitude) { + this->import_max_longitude = import_max_longitude; + this->m_import_max_longitude_isSet = true; } QString* @@ -617,16 +893,55 @@ SWGADSBDemodSettings::isSet(){ if(m_remove_timeout_isSet){ isObjectUpdated = true; break; } - if(m_beast_enabled_isSet){ + if(m_feed_enabled_isSet){ isObjectUpdated = true; break; } - if(beast_host && *beast_host != QString("")){ + if(m_export_client_enabled_isSet){ isObjectUpdated = true; break; } - if(m_beast_port_isSet){ + if(export_client_host && *export_client_host != QString("")){ isObjectUpdated = true; break; } - if(m_feed_format_isSet){ + if(m_export_client_port_isSet){ + isObjectUpdated = true; break; + } + if(m_export_client_format_isSet){ + isObjectUpdated = true; break; + } + if(m_export_server_enabled_isSet){ + isObjectUpdated = true; break; + } + if(m_export_server_port_isSet){ + isObjectUpdated = true; break; + } + if(m_import_enabled_isSet){ + isObjectUpdated = true; break; + } + if(import_host && *import_host != QString("")){ + isObjectUpdated = true; break; + } + if(import_username && *import_username != QString("")){ + isObjectUpdated = true; break; + } + if(import_password && *import_password != QString("")){ + isObjectUpdated = true; break; + } + if(import_parameters && *import_parameters != QString("")){ + isObjectUpdated = true; break; + } + if(m_import_period_isSet){ + isObjectUpdated = true; break; + } + if(import_min_latitude && *import_min_latitude != QString("")){ + isObjectUpdated = true; break; + } + if(import_max_latitude && *import_max_latitude != QString("")){ + isObjectUpdated = true; break; + } + if(import_min_longitude && *import_min_longitude != QString("")){ + isObjectUpdated = true; break; + } + if(import_max_longitude && *import_max_longitude != QString("")){ isObjectUpdated = true; break; } if(log_filename && *log_filename != QString("")){ diff --git a/swagger/sdrangel/code/qt5/client/SWGADSBDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGADSBDemodSettings.h index a87aac050..fc141c3a6 100644 --- a/swagger/sdrangel/code/qt5/client/SWGADSBDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGADSBDemodSettings.h @@ -71,17 +71,56 @@ public: qint32 getRemoveTimeout(); void setRemoveTimeout(qint32 remove_timeout); - qint32 getBeastEnabled(); - void setBeastEnabled(qint32 beast_enabled); + qint32 getFeedEnabled(); + void setFeedEnabled(qint32 feed_enabled); - QString* getBeastHost(); - void setBeastHost(QString* beast_host); + qint32 getExportClientEnabled(); + void setExportClientEnabled(qint32 export_client_enabled); - qint32 getBeastPort(); - void setBeastPort(qint32 beast_port); + QString* getExportClientHost(); + void setExportClientHost(QString* export_client_host); - qint32 getFeedFormat(); - void setFeedFormat(qint32 feed_format); + qint32 getExportClientPort(); + void setExportClientPort(qint32 export_client_port); + + qint32 getExportClientFormat(); + void setExportClientFormat(qint32 export_client_format); + + qint32 getExportServerEnabled(); + void setExportServerEnabled(qint32 export_server_enabled); + + qint32 getExportServerPort(); + void setExportServerPort(qint32 export_server_port); + + qint32 getImportEnabled(); + void setImportEnabled(qint32 import_enabled); + + QString* getImportHost(); + void setImportHost(QString* import_host); + + QString* getImportUsername(); + void setImportUsername(QString* import_username); + + QString* getImportPassword(); + void setImportPassword(QString* import_password); + + QString* getImportParameters(); + void setImportParameters(QString* import_parameters); + + float getImportPeriod(); + void setImportPeriod(float import_period); + + QString* getImportMinLatitude(); + void setImportMinLatitude(QString* import_min_latitude); + + QString* getImportMaxLatitude(); + void setImportMaxLatitude(QString* import_max_latitude); + + QString* getImportMinLongitude(); + void setImportMinLongitude(QString* import_min_longitude); + + QString* getImportMaxLongitude(); + void setImportMaxLongitude(QString* import_max_longitude); QString* getLogFilename(); void setLogFilename(QString* log_filename); @@ -150,17 +189,56 @@ private: qint32 remove_timeout; bool m_remove_timeout_isSet; - qint32 beast_enabled; - bool m_beast_enabled_isSet; + qint32 feed_enabled; + bool m_feed_enabled_isSet; - QString* beast_host; - bool m_beast_host_isSet; + qint32 export_client_enabled; + bool m_export_client_enabled_isSet; - qint32 beast_port; - bool m_beast_port_isSet; + QString* export_client_host; + bool m_export_client_host_isSet; - qint32 feed_format; - bool m_feed_format_isSet; + qint32 export_client_port; + bool m_export_client_port_isSet; + + qint32 export_client_format; + bool m_export_client_format_isSet; + + qint32 export_server_enabled; + bool m_export_server_enabled_isSet; + + qint32 export_server_port; + bool m_export_server_port_isSet; + + qint32 import_enabled; + bool m_import_enabled_isSet; + + QString* import_host; + bool m_import_host_isSet; + + QString* import_username; + bool m_import_username_isSet; + + QString* import_password; + bool m_import_password_isSet; + + QString* import_parameters; + bool m_import_parameters_isSet; + + float import_period; + bool m_import_period_isSet; + + QString* import_min_latitude; + bool m_import_min_latitude_isSet; + + QString* import_max_latitude; + bool m_import_max_latitude_isSet; + + QString* import_min_longitude; + bool m_import_min_longitude_isSet; + + QString* import_max_longitude; + bool m_import_max_longitude_isSet; QString* log_filename; bool m_log_filename_isSet; From 73848c94e49751c95c1757bc03c4971f93796dc4 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 9 Feb 2022 16:39:22 +0000 Subject: [PATCH 2/6] AIS - Handle ShipStaticData being received before position --- plugins/feature/ais/aisgui.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp index 62ab14042..e10ea4a48 100644 --- a/plugins/feature/ais/aisgui.cpp +++ b/plugins/feature/ais/aisgui.cpp @@ -663,8 +663,10 @@ void AISGUI::updateVessels(AISMessage *ais, QDateTime dateTime) QString status = statusItem->text(); // Only update model if change in type - so we don't keeping picking new - // random models - if ((previousType != type) || (previousShipType != shipType)) { + // random models. + // Check if image is empty to handle case where ShipStaticData is received + // before position + if ((previousType != type) || (previousShipType != shipType) || vessel->m_image.isEmpty()) { getImageAndModel(type, shipType, length, status, vessel); } @@ -712,7 +714,6 @@ void AISGUI::updateVessels(AISMessage *ais, QDateTime dateTime) if (!status.isEmpty()) { text.append(QString("Status: %1").arg(status)); } - // Send to map feature sendToMap(mmsiItem->text(), callsign, vessel->m_image, text.join("
"), @@ -724,7 +725,7 @@ void AISGUI::updateVessels(AISMessage *ais, QDateTime dateTime) void AISGUI::getImageAndModel(const QString &type, const QString &shipType, int length, const QString &status, Vessel *vessel) { - if (type == "Aircraft") + if (type == "Aircraft") { // I presume search and rescue aircraft are more likely to be helicopters vessel->m_image = "helicopter.png"; @@ -766,7 +767,7 @@ void AISGUI::getImageAndModel(const QString &type, const QString &shipType, int vessel->m_model = "ship_65m.glbe"; } } - else if ((shipType == "Tug") || (shipType == "Port tender") || (shipType == "Pilot vessel")) + else if ((shipType == "Tug") || (shipType == "Port tender") || (shipType == "Pilot vessel") || (shipType == "Vessel - Towing")) { vessel->m_image = "tug.png"; if (length < 25) From 29b29419510310f70e9e94e23b4414940c2ac766 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 9 Feb 2022 16:41:40 +0000 Subject: [PATCH 3/6] Map Updates Fix 3D map for Qt < 5.15. Add 3D map label scale setting. Add 3D map time to Web report. Reduce height of display settings dialog to fit on smaller screens. --- plugins/feature/map/czml.cpp | 2 +- plugins/feature/map/map.cpp | 51 +- plugins/feature/map/map.h | 14 + plugins/feature/map/map/map3d.html | 38 +- plugins/feature/map/mapgui.cpp | 17 +- plugins/feature/map/mapsettings.cpp | 3 + plugins/feature/map/mapsettings.h | 1 + plugins/feature/map/mapsettingsdialog.cpp | 8 + plugins/feature/map/mapsettingsdialog.h | 5 +- plugins/feature/map/mapsettingsdialog.ui | 876 +++++++++--------- plugins/feature/map/webserver.cpp | 3 + sdrbase/util/fits.cpp | 3 + sdrbase/webapi/webapirequestmapper.cpp | 15 + .../api/swagger/include/FeatureReport.yaml | 2 + swagger/sdrangel/api/swagger/include/Map.yaml | 7 + .../code/qt5/client/SWGFeatureReport.cpp | 25 + .../code/qt5/client/SWGFeatureReport.h | 7 + .../code/qt5/client/SWGModelFactory.h | 6 + 18 files changed, 646 insertions(+), 437 deletions(-) diff --git a/plugins/feature/map/czml.cpp b/plugins/feature/map/czml.cpp index e4138e798..fc1e42f63 100644 --- a/plugins/feature/map/czml.cpp +++ b/plugins/feature/map/czml.cpp @@ -443,7 +443,7 @@ QJsonObject CZML::update(MapItem *mapItem, bool isTarget, bool isSelected) QJsonObject label { {"text", mapItem->m_label}, {"show", m_settings->m_displayNames && mapItem->m_itemSettings->m_enabled && mapItem->m_itemSettings->m_display3DLabel}, - {"scale", 0.5}, + {"scale", mapItem->m_itemSettings->m_3DLabelScale}, {"pixelOffset", labelPixelOffset}, {"eyeOffset", labelEyeOffset}, {"verticalOrigin", "BASELINE"}, diff --git a/plugins/feature/map/map.cpp b/plugins/feature/map/map.cpp index 4445b8a3f..1e2dea20b 100644 --- a/plugins/feature/map/map.cpp +++ b/plugins/feature/map/map.cpp @@ -43,7 +43,9 @@ const char* const Map::m_featureIdURI = "sdrangel.feature.map"; const char* const Map::m_featureId = "Map"; Map::Map(WebAPIAdapterInterface *webAPIAdapterInterface) : - Feature(m_featureIdURI, webAPIAdapterInterface) + Feature(m_featureIdURI, webAPIAdapterInterface), + m_multiplier(0.0), + m_dateTimeMutex(QMutex::Recursive) { qDebug("Map::Map: webAPIAdapterInterface: %p", webAPIAdapterInterface); setObjectName(m_featureId); @@ -206,6 +208,17 @@ int Map::webapiSettingsPutPatch( return 200; } +int Map::webapiReportGet( + SWGSDRangel::SWGFeatureReport& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setMapReport(new SWGSDRangel::SWGMapReport()); + response.getMapReport()->init(); + webapiFormatFeatureReport(response); + return 200; +} + int Map::webapiActionsPost( const QStringList& featureActionsKeys, SWGSDRangel::SWGFeatureActions& query, @@ -356,6 +369,16 @@ void Map::webapiReverseSendSettings(QList& featureSettingsKeys, const M delete swgFeatureSettings; } +void Map::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response) +{ + QString mapDateTime = getMapDateTime().toString(Qt::ISODateWithMs); + if (response.getMapReport()->getDateTime()) { + *response.getMapReport()->getDateTime() = mapDateTime; + } else { + response.getMapReport()->setDateTime(new QString(mapDateTime)); + } +} + void Map::networkManagerFinished(QNetworkReply *reply) { QNetworkReply::NetworkError replyError = reply->error(); @@ -376,3 +399,29 @@ void Map::networkManagerFinished(QNetworkReply *reply) reply->deleteLater(); } + +void Map::setMapDateTime(QDateTime mapDateTime, QDateTime systemDateTime, double multiplier) +{ + QMutexLocker mutexLocker(&m_dateTimeMutex); + m_mapDateTime = mapDateTime; + m_systemDateTime = systemDateTime; + m_multiplier = multiplier; +} + +QDateTime Map::getMapDateTime() +{ + QMutexLocker mutexLocker(&m_dateTimeMutex); + if (m_multiplier == 0.0) + { + return m_mapDateTime; + } + else + { + // It's not possible to synchronously get the time from Cesium + // so we calculate it based on the system clock difference from + // when changes were made to the clock GUI elements + // Should be accurate enough for satellite tracker + qint64 diffMsecs = m_systemDateTime.msecsTo(QDateTime::currentDateTime()); + return m_mapDateTime.addMSecs(diffMsecs * m_multiplier); + } +} diff --git a/plugins/feature/map/map.h b/plugins/feature/map/map.h index 2e356d140..fca1a7475 100644 --- a/plugins/feature/map/map.h +++ b/plugins/feature/map/map.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "feature/feature.h" #include "util/message.h" @@ -127,6 +128,10 @@ public: SWGSDRangel::SWGFeatureSettings& response, QString& errorMessage); + virtual int webapiReportGet( + SWGSDRangel::SWGFeatureReport& response, + QString& errorMessage); + virtual int webapiActionsPost( const QStringList& featureActionsKeys, SWGSDRangel::SWGFeatureActions& query, @@ -141,6 +146,9 @@ public: const QStringList& featureSettingsKeys, SWGSDRangel::SWGFeatureSettings& response); + void setMapDateTime(QDateTime mapDateTime, QDateTime systemDateTime, double multiplier); + QDateTime getMapDateTime(); + static const char* const m_featureIdURI; static const char* const m_featureId; @@ -154,8 +162,14 @@ private: QNetworkRequest m_networkRequest; void applySettings(const MapSettings& settings, bool force = false); + void webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response); void webapiReverseSendSettings(QList& featureSettingsKeys, const MapSettings& settings, bool force); + QDateTime m_mapDateTime; + QDateTime m_systemDateTime; + double m_multiplier; + QMutex m_dateTimeMutex; + private slots: void updatePipes(); void networkManagerFinished(QNetworkReply *reply); diff --git a/plugins/feature/map/map/map3d.html b/plugins/feature/map/map/map3d.html index a136fd5b8..57747267d 100644 --- a/plugins/feature/map/map/map3d.html +++ b/plugins/feature/map/map/map3d.html @@ -216,10 +216,7 @@ viewer.clock.currentTime = dateTime; } else if (command.command == "getDateTime") { // Get current date and time of viewer - socket.send(JSON.stringify({ - command: "getDateTime", - dateTime: Cesium.JulianDate.toIso8601(viewer.clock.currentTime) - })); + reportClock(); } else if (command.command == "setTerrain") { // Support using Ellipsoid terrain for performance and also // because paths can't be clammped to ground, so AIS paths @@ -397,6 +394,39 @@ } }); + // Report clock changes for use by other plugins + var systemTime = new Cesium.JulianDate(); + function reportClock() { + if (socket.readyState === 1) { + Cesium.JulianDate.now(systemTime); + socket.send(JSON.stringify({ + event: "clock", + canAnimate: viewer.clock.canAnimate, + shouldAnimate: viewer.clock.shouldAnimate, + currentTime: Cesium.JulianDate.toIso8601(viewer.clock.currentTime), + multiplier: viewer.clock.multiplier, + systemTime: Cesium.JulianDate.toIso8601(systemTime) + })); + } + }; + + Cesium.knockout.getObservable(viewer.clockViewModel, 'shouldAnimate').subscribe(function(isAnimating) { + reportClock(); + }); + Cesium.knockout.getObservable(viewer.clockViewModel, 'multiplier').subscribe(function(multiplier) { + reportClock(); + }); + // This is called every frame + //Cesium.knockout.getObservable(viewer.clockViewModel, 'currentTime').subscribe(function(currentTime) { + //reportClock(); + //}); + viewer.timeline.addEventListener('settime', reportClock, false); + + socket.onopen = () => { + reportClock(); + }; + + diff --git a/plugins/feature/map/mapgui.cpp b/plugins/feature/map/mapgui.cpp index 211a3eefb..cbf49acb9 100644 --- a/plugins/feature/map/mapgui.cpp +++ b/plugins/feature/map/mapgui.cpp @@ -812,6 +812,7 @@ void MapGUI::applyMap3DSettings(bool reloadMap) m_cesium->setSunLight(m_settings.m_sunLightEnabled); m_cesium->setCameraReferenceFrame(m_settings.m_eciCamera); m_cesium->setAntiAliasing(m_settings.m_antiAliasing); + m_cesium->getDateTime(); } } @@ -826,14 +827,14 @@ void MapGUI::init3DMap() m_cesium->setSunLight(m_settings.m_sunLightEnabled); m_cesium->setCameraReferenceFrame(m_settings.m_eciCamera); m_cesium->setAntiAliasing(m_settings.m_antiAliasing); + m_cesium->getDateTime(); + m_mapModel.allUpdated(); float stationLatitude = MainCore::instance()->getSettings().getLatitude(); float stationLongitude = MainCore::instance()->getSettings().getLongitude(); // Set 3D view after loading initial objects m_cesium->setHomeView(stationLatitude, stationLongitude); - - m_mapModel.allUpdated(); } void MapGUI::displaySettings() @@ -1144,6 +1145,18 @@ void MapGUI::receivedCesiumEvent(const QJsonObject &obj) //m_mapModel.setTarget(""); } } + else if (event == "clock") + { + if (m_map) + { + QDateTime mapDateTime = QDateTime::fromString(obj.value("currentTime").toString(), Qt::ISODateWithMs); + QDateTime systemDateTime = QDateTime::fromString(obj.value("systemTime").toString(), Qt::ISODateWithMs); + double multiplier = obj.value("multiplier").toDouble(); + bool canAnimate = obj.value("canAnimate").toBool(); + bool shouldAnimate = obj.value("shouldAnimate").toBool(); + m_map->setMapDateTime(mapDateTime, systemDateTime, canAnimate && shouldAnimate ? multiplier : 0.0); + } + } } else { diff --git a/plugins/feature/map/mapsettings.cpp b/plugins/feature/map/mapsettings.cpp index e12997a18..b4352481f 100644 --- a/plugins/feature/map/mapsettings.cpp +++ b/plugins/feature/map/mapsettings.cpp @@ -251,6 +251,7 @@ void MapSettings::MapItemSettings::resetToDefaults() m_display3DTrack = true; m_3DTrackColor = QColor(150, 0, 20).rgb(); m_3DModelMinPixelSize = 0; + m_3DLabelScale = 0.5f; } QByteArray MapSettings::MapItemSettings::serialize() const @@ -271,6 +272,7 @@ QByteArray MapSettings::MapItemSettings::serialize() const s.writeBool(12, m_display3DTrack); s.writeU32(13, m_3DTrackColor); s.writeS32(14, m_3DModelMinPixelSize); + s.writeFloat(15, m_3DLabelScale); return s.final(); } @@ -301,6 +303,7 @@ bool MapSettings::MapItemSettings::deserialize(const QByteArray& data) d.readBool(12, &m_display3DTrack, true); d.readU32(13, &m_3DTrackColor, QColor(150, 0, 20).rgb()); d.readS32(14, &m_3DModelMinPixelSize, 0); + d.readFloat(15, &m_3DLabelScale, 0.5f); return true; } else diff --git a/plugins/feature/map/mapsettings.h b/plugins/feature/map/mapsettings.h index 8f4f81f16..d480059e1 100644 --- a/plugins/feature/map/mapsettings.h +++ b/plugins/feature/map/mapsettings.h @@ -42,6 +42,7 @@ struct MapSettings bool m_display3DTrack; // Display a ground track for this item on the 3D map quint32 m_3DTrackColor; int m_3DModelMinPixelSize; + float m_3DLabelScale; MapItemSettings(const QString& group, const QColor color, bool display3DPoint=true, int minZoom=11, int modelMinPixelSize=0); MapItemSettings(const QByteArray& data); diff --git a/plugins/feature/map/mapsettingsdialog.cpp b/plugins/feature/map/mapsettingsdialog.cpp index 019e4c50f..4d81b0f2c 100644 --- a/plugins/feature/map/mapsettingsdialog.cpp +++ b/plugins/feature/map/mapsettingsdialog.cpp @@ -96,8 +96,13 @@ MapItemSettingsGUI::MapItemSettingsGUI(QTableWidget *table, int row, MapSettings m_minPixels = new QSpinBox(table); m_minPixels->setRange(0, 200); m_minPixels->setValue(settings->m_3DModelMinPixelSize); + m_labelScale = new QDoubleSpinBox(table); + m_labelScale->setDecimals(2); + m_labelScale->setRange(0.01, 10.0); + m_labelScale->setValue(settings->m_3DLabelScale); table->setCellWidget(row, MapSettingsDialog::COL_2D_MIN_ZOOM, m_minZoom); table->setCellWidget(row, MapSettingsDialog::COL_3D_MIN_PIXELS, m_minPixels); + table->setCellWidget(row, MapSettingsDialog::COL_3D_LABEL_SCALE, m_labelScale); } MapSettingsDialog::MapSettingsDialog(MapSettings *settings, QWidget* parent) : @@ -241,6 +246,7 @@ void MapSettingsDialog::accept() itemSettings->m_display3DTrack = !gui->m_track3D.m_noColor; itemSettings->m_3DTrackColor = gui->m_track3D.m_color; itemSettings->m_3DModelMinPixelSize = gui->m_minPixels->value(); + itemSettings->m_3DLabelScale = gui->m_labelScale->value(); } QDialog::accept(); @@ -276,6 +282,7 @@ void MapSettingsDialog::on_map3DEnabled_clicked(bool checked) ui->mapItemSettings->showColumn(COL_3D_LABEL); ui->mapItemSettings->showColumn(COL_3D_POINT); ui->mapItemSettings->showColumn(COL_3D_TRACK); + ui->mapItemSettings->showColumn(COL_3D_LABEL_SCALE); } else { @@ -284,6 +291,7 @@ void MapSettingsDialog::on_map3DEnabled_clicked(bool checked) ui->mapItemSettings->hideColumn(COL_3D_LABEL); ui->mapItemSettings->hideColumn(COL_3D_POINT); ui->mapItemSettings->hideColumn(COL_3D_TRACK); + ui->mapItemSettings->hideColumn(COL_3D_LABEL_SCALE); } ui->terrain->setEnabled(checked); ui->buildings->setEnabled(checked); diff --git a/plugins/feature/map/mapsettingsdialog.h b/plugins/feature/map/mapsettingsdialog.h index ee82acd5c..c8d60b9e7 100644 --- a/plugins/feature/map/mapsettingsdialog.h +++ b/plugins/feature/map/mapsettingsdialog.h @@ -19,6 +19,7 @@ #define INCLUDE_FEATURE_MAPSETTINGSDIALOG_H #include +#include #include #include "gui/httpdownloadmanagergui.h" @@ -56,6 +57,7 @@ public: MapColorGUI m_track3D; QSpinBox *m_minZoom; QSpinBox *m_minPixels; + QDoubleSpinBox *m_labelScale; }; class MapSettingsDialog : public QDialog { @@ -75,7 +77,8 @@ public: COL_3D_MIN_PIXELS, COL_3D_LABEL, COL_3D_POINT, - COL_3D_TRACK + COL_3D_TRACK, + COL_3D_LABEL_SCALE }; public: diff --git a/plugins/feature/map/mapsettingsdialog.ui b/plugins/feature/map/mapsettingsdialog.ui index b9426a6d8..30cc95f37 100644 --- a/plugins/feature/map/mapsettingsdialog.ui +++ b/plugins/feature/map/mapsettingsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 946 - 800 + 1016 + 720 @@ -26,434 +26,453 @@ 0 - - - Select how to display items on the maps: + + + 0 + + + Maps + + + + + + QAbstractItemView::NoSelection + + + + Enabled + + + + + 2D Icon + + + + + 2D Label + + + + + 2D Min Zoom + + + + + 2D Track + + + + + 3D Model + + + + + 3D Min Pixels + + + + + 3D Label + + + + + 3D Point + + + + + 3D Track + + + + + 3D Label Scale + + + + + + + + 2D Map Settings + + + + + + + 140 + 0 + + + + Enabled + + + + + + + + + + + + + + Map provider + + + + + + + Select map provider + + + + OpenStreetMap + + + + + ESRI + + + + + Mapbox + + + + + MapboxGL + + + + + MapLibre + + + + + + + + OSM Custom URL + + + + + + + URL of custom map for use with OpenStreetMap provider + + + + + + + MapboxGL Styles + + + + + + + Comma separated list of MapBox styles + + + + + + + + + + 3D Map Settings + + + + + + + 140 + 0 + + + + Enabled + + + + + + + + + + + + + + Terrain + + + + + + + + Cesium World Terrain + + + + + Ellipsoid + + + + + Maptiler + + + + + ArcGIS + + + + + + + + Buildings + + + + + + + + None + + + + + Cesium OSM Buildings + + + + + + + + Lighting + + + + + + + Whether lighting is from the Sun or Camera + + + + Camera + + + + + Sun + + + + + + + + Camera reference frame + + + + + + + Selects camera reference frame. For ECEF the camera rotates with the Earth. For ECI, the camera position is fixed relative to the stars and the Earth's rotation will be visible. + + + + ECEF + + + + + ECI + + + + + + + + Anti-aliasing + + + + + + + Set anti-aliasing to use. This can remove jagged pixels on the edge of 3D models. + + + + None + + + + + FXAA + + + + + + + + + + + + + Download 3D models. It is recommended to restart SDRangel after download. + + + Download 3D Models (1.6GB) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + API Keys + + + + + + API Keys + + + + + + + 140 + 0 + + + + Thunderforest API Key + + + + + + + Enter a Thunderforest API key in order to use non-watermarked Thunderforest maps: https://www.thunderforest.com/ + + + + + + + + 140 + 0 + + + + Maptiler API Key + + + + + + + Enter a Maptiler API key in order to use Maptiler maps: https://www.maptiler.com/ + + + + + + + + 140 + 0 + + + + Mapbox API Key + + + + + + + Enter a Mapbox API key in order to use Mapbox maps: https://www.mapbox.com/ + + + + + + + + 140 + 0 + + + + Cesium Ion API Key + + + + + + + Enter a Cesium Ion Access Token + + + + + + + + - - - - QAbstractItemView::NoSelection - - - - Enabled - - - - - 2D Icon - - - - - 2D Label - - - - - 2D Min Zoom - - - - - 2D Track - - - - - 3D Model - - - - - 3D Min Pixels - - - - - 3D Label - - - - - 3D Point - - - - - 3D Track - - - - - - - - 2D Map Settings - - - - - - - 140 - 0 - - - - Enabled - - - - - - - - - - - - - - Map provider - - - - - - - Select map provider - - - - OpenStreetMap - - - - - ESRI - - - - - Mapbox - - - - - MapboxGL - - - - - MapLibre - - - - - - - - OSM Custom URL - - - - - - - URL of custom map for use with OpenStreetMap provider - - - - - - - MapboxGL Styles - - - - - - - Comma separated list of MapBox styles - - - - - - - - - - 3D Map Settings - - - - - - - 140 - 0 - - - - Enabled - - - - - - - - - - - - - - Terrain - - - - - - - - Cesium World Terrain - - - - - Ellipsoid - - - - - Maptiler - - - - - ArcGIS - - - - - - - - Buildings - - - - - - - - None - - - - - Cesium OSM Buildings - - - - - - - - Lighting - - - - - - - Whether lighting is from the Sun or Camera - - - - Camera - - - - - Sun - - - - - - - - Camera reference frame - - - - - - - Selects camera reference frame. For ECEF the camera rotates with the Earth. For ECI, the camera position is fixed relative to the stars and the Earth's rotation will be visible. - - - - ECEF - - - - - ECI - - - - - - - - Anti-aliasing - - - - - - - Set anti-aliasing to use. This can remove jagged pixels on the edge of 3D models. - - - - None - - - - - FXAA - - - - - - - - - - - API Keys - - - - - - - 140 - 0 - - - - Thunderforest API Key - - - - - - - Enter a Thunderforest API key in order to use non-watermarked Thunderforest maps: https://www.thunderforest.com/ - - - - - - - - 140 - 0 - - - - Maptiler API Key - - - - - - - Enter a Maptiler API key in order to use Maptiler maps: https://www.maptiler.com/ - - - - - - - - 140 - 0 - - - - Mapbox API Key - - - - - - - Enter a Mapbox API key in order to use Mapbox maps: https://www.mapbox.com/ - - - - - - - - 140 - 0 - - - - Cesium Ion API Key - - - - - - - Enter a Cesium Ion Access Token - - - - - - - - - - - - Download 3D models. It is recommended to restart SDRangel after download. - - - Download 3D Models (1.6GB) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - -
@@ -470,6 +489,7 @@ + tabWidget mapItemSettings map2DEnabled mapProvider @@ -481,11 +501,11 @@ sunLightEnabled eciCamera antiAliasing + downloadModels thunderforestAPIKey maptilerAPIKey mapBoxAPIKey cesiumIonAPIKey - downloadModels diff --git a/plugins/feature/map/webserver.cpp b/plugins/feature/map/webserver.cpp index f3ba9b799..17c5e4a80 100644 --- a/plugins/feature/map/webserver.cpp +++ b/plugins/feature/map/webserver.cpp @@ -157,6 +157,9 @@ void WebServer::readClient() if (res.isValid() && (res.size() > 0)) { QByteArray data = QByteArray::fromRawData((const char *)res.data(), res.size()); + if (res.isCompressed()) { + data = qUncompress(data); + } sendFile(socket, data, mimeType, path); } #endif diff --git a/sdrbase/util/fits.cpp b/sdrbase/util/fits.cpp index afc3c448f..9e4882053 100644 --- a/sdrbase/util/fits.cpp +++ b/sdrbase/util/fits.cpp @@ -40,6 +40,9 @@ FITS::FITS(QString resourceName) : m_fileSize = m_res.uncompressedSize(); #else m_data = QByteArray::fromRawData((const char *)m_res.data(), m_res.size()); + if (res.isCompressed()) { + m_data = qUncompress(m_data); + } m_fileSize = m_res.size(); #endif int hLen = std::min((qint64)m_headerSize * 3, m_fileSize); // Could possibly be bigger diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index c43d78f13..0f5034bbe 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -5145,14 +5145,29 @@ void WebAPIRequestMapper::resetFeatureReport(SWGSDRangel::SWGFeatureReport& feat { featureReport.cleanup(); featureReport.setFeatureType(nullptr); + featureReport.setAfcReport(nullptr); + featureReport.setGs232ControllerReport(nullptr); + featureReport.setPerTesterReport(nullptr); + featureReport.setRigCtlServerReport(nullptr); + featureReport.setMapReport(nullptr); + featureReport.setSatelliteTrackerReport(nullptr); featureReport.setSimplePttReport(nullptr); + featureReport.setStarTrackerReport(nullptr); + featureReport.setVorLocalizerReport(nullptr); } void WebAPIRequestMapper::resetFeatureActions(SWGSDRangel::SWGFeatureActions& featureActions) { featureActions.cleanup(); featureActions.setFeatureType(nullptr); + featureActions.setAfcActions(nullptr); + featureActions.setGs232ControllerActions(nullptr); featureActions.setMapActions(nullptr); + featureActions.setPerTesterActions(nullptr); + featureActions.setRigCtlServerActions(nullptr); + featureActions.setSatelliteTrackerActions(nullptr); featureActions.setSimplePttActions(nullptr); + featureActions.setStarTrackerActions(nullptr); + featureActions.setVorLocalizerActions(nullptr); } diff --git a/swagger/sdrangel/api/swagger/include/FeatureReport.yaml b/swagger/sdrangel/api/swagger/include/FeatureReport.yaml index f9b74f849..468022ad5 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureReport.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureReport.yaml @@ -11,6 +11,8 @@ FeatureReport: $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCReport" GS232ControllerReport: $ref: "http://swgserver:8081/api/swagger/include/GS232Controller.yaml#/GS232ControllerReport" + MapReport: + $ref: "http://swgserver:8081/api/swagger/include/Map.yaml#/MapReport" PERTesterReport: $ref: "http://swgserver:8081/api/swagger/include/PERTester.yaml#/PERTesterReport" RigCtlServerReport: diff --git a/swagger/sdrangel/api/swagger/include/Map.yaml b/swagger/sdrangel/api/swagger/include/Map.yaml index 38a112b99..5909000b5 100644 --- a/swagger/sdrangel/api/swagger/include/Map.yaml +++ b/swagger/sdrangel/api/swagger/include/Map.yaml @@ -22,6 +22,13 @@ MapSettings: rollupState: $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" +MapReport: + description: Map + properties: + dateTime: + description: "Current date and time being displayed by 3D map" + type: string + MapActions: description: Map properties: diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp index 919ec8b20..0aaf03ea2 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.cpp @@ -34,6 +34,8 @@ SWGFeatureReport::SWGFeatureReport() { m_afc_report_isSet = false; gs232_controller_report = nullptr; m_gs232_controller_report_isSet = false; + map_report = nullptr; + m_map_report_isSet = false; per_tester_report = nullptr; m_per_tester_report_isSet = false; rig_ctl_server_report = nullptr; @@ -60,6 +62,8 @@ SWGFeatureReport::init() { m_afc_report_isSet = false; gs232_controller_report = new SWGGS232ControllerReport(); m_gs232_controller_report_isSet = false; + map_report = new SWGMapReport(); + m_map_report_isSet = false; per_tester_report = new SWGPERTesterReport(); m_per_tester_report_isSet = false; rig_ctl_server_report = new SWGRigCtlServerReport(); @@ -85,6 +89,9 @@ SWGFeatureReport::cleanup() { if(gs232_controller_report != nullptr) { delete gs232_controller_report; } + if(map_report != nullptr) { + delete map_report; + } if(per_tester_report != nullptr) { delete per_tester_report; } @@ -122,6 +129,8 @@ SWGFeatureReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&gs232_controller_report, pJson["GS232ControllerReport"], "SWGGS232ControllerReport", "SWGGS232ControllerReport"); + ::SWGSDRangel::setValue(&map_report, pJson["MapReport"], "SWGMapReport", "SWGMapReport"); + ::SWGSDRangel::setValue(&per_tester_report, pJson["PERTesterReport"], "SWGPERTesterReport", "SWGPERTesterReport"); ::SWGSDRangel::setValue(&rig_ctl_server_report, pJson["RigCtlServerReport"], "SWGRigCtlServerReport", "SWGRigCtlServerReport"); @@ -159,6 +168,9 @@ SWGFeatureReport::asJsonObject() { if((gs232_controller_report != nullptr) && (gs232_controller_report->isSet())){ toJsonValue(QString("GS232ControllerReport"), gs232_controller_report, obj, QString("SWGGS232ControllerReport")); } + if((map_report != nullptr) && (map_report->isSet())){ + toJsonValue(QString("MapReport"), map_report, obj, QString("SWGMapReport")); + } if((per_tester_report != nullptr) && (per_tester_report->isSet())){ toJsonValue(QString("PERTesterReport"), per_tester_report, obj, QString("SWGPERTesterReport")); } @@ -211,6 +223,16 @@ SWGFeatureReport::setGs232ControllerReport(SWGGS232ControllerReport* gs232_contr this->m_gs232_controller_report_isSet = true; } +SWGMapReport* +SWGFeatureReport::getMapReport() { + return map_report; +} +void +SWGFeatureReport::setMapReport(SWGMapReport* map_report) { + this->map_report = map_report; + this->m_map_report_isSet = true; +} + SWGPERTesterReport* SWGFeatureReport::getPerTesterReport() { return per_tester_report; @@ -285,6 +307,9 @@ SWGFeatureReport::isSet(){ if(gs232_controller_report && gs232_controller_report->isSet()){ isObjectUpdated = true; break; } + if(map_report && map_report->isSet()){ + isObjectUpdated = true; break; + } if(per_tester_report && per_tester_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h index 4e833a729..bc65e2a0c 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureReport.h @@ -24,6 +24,7 @@ #include "SWGAFCReport.h" #include "SWGGS232ControllerReport.h" +#include "SWGMapReport.h" #include "SWGPERTesterReport.h" #include "SWGRigCtlServerReport.h" #include "SWGSatelliteTrackerReport.h" @@ -59,6 +60,9 @@ public: SWGGS232ControllerReport* getGs232ControllerReport(); void setGs232ControllerReport(SWGGS232ControllerReport* gs232_controller_report); + SWGMapReport* getMapReport(); + void setMapReport(SWGMapReport* map_report); + SWGPERTesterReport* getPerTesterReport(); void setPerTesterReport(SWGPERTesterReport* per_tester_report); @@ -90,6 +94,9 @@ private: SWGGS232ControllerReport* gs232_controller_report; bool m_gs232_controller_report_isSet; + SWGMapReport* map_report; + bool m_map_report_isSet; + SWGPERTesterReport* per_tester_report; bool m_per_tester_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 00b127339..5043473cc 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -175,6 +175,7 @@ #include "SWGMapCoordinate.h" #include "SWGMapItem.h" #include "SWGMapItem_2.h" +#include "SWGMapReport.h" #include "SWGMapSettings.h" #include "SWGMetisMISOSettings.h" #include "SWGNFMDemodReport.h" @@ -1124,6 +1125,11 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGMapReport").compare(type) == 0) { + SWGMapReport *obj = new SWGMapReport(); + obj->init(); + return obj; + } if(QString("SWGMapSettings").compare(type) == 0) { SWGMapSettings *obj = new SWGMapSettings(); obj->init(); From 81157696f7700a0b3eb659b28e6bbf1dd6351972 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 9 Feb 2022 16:42:51 +0000 Subject: [PATCH 4/6] Satellite Tracker - Get time from Map or File Input device. --- plugins/feature/satellitetracker/readme.md | 9 +- .../satellitetracker/satellitetracker.cpp | 65 +++++++---- .../satellitetracker/satellitetrackergui.cpp | 105 +++++++++++++++--- .../satellitetracker/satellitetrackergui.h | 7 +- .../satellitetracker/satellitetrackergui.ui | 46 +++++--- .../satellitetrackersettings.cpp | 12 +- .../satellitetrackersettings.h | 4 +- .../satellitetrackersettingsdialog.cpp | 2 - .../satellitetrackersettingsdialog.ui | 23 +--- sdrbase/channel/channelwebapiutils.cpp | 105 ++++++++++++------ sdrbase/channel/channelwebapiutils.h | 3 + 11 files changed, 268 insertions(+), 113 deletions(-) diff --git a/plugins/feature/satellitetracker/readme.md b/plugins/feature/satellitetracker/readme.md index 8b9a48188..238fbc065 100644 --- a/plugins/feature/satellitetracker/readme.md +++ b/plugins/feature/satellitetracker/readme.md @@ -127,7 +127,14 @@ Specifies the longitude in decimal degrees (East positive) of the antenna locati

11: Time

-Select the date and time at which the position of the satellite should be calculated. Select either Now, for the current time, or Custom to manually enter a date and time. +Select the date and time at which the position of the satellite should be calculated. Select either: + +* Now, for the current time based on the computers clock, +* Custom to manually enter a date and time, +* Map to get the time from the 3D map or +* File to get the time from a File Input device. + +When Map or File are selected, a combo box is displayed that allows choosing which Map feature or File Input device, should there be several.

12: Target

diff --git a/plugins/feature/satellitetracker/satellitetracker.cpp b/plugins/feature/satellitetracker/satellitetracker.cpp index fda71ab0c..cc39e4723 100644 --- a/plugins/feature/satellitetracker/satellitetracker.cpp +++ b/plugins/feature/satellitetracker/satellitetracker.cpp @@ -80,12 +80,11 @@ void SatelliteTracker::start() { qDebug("SatelliteTracker::start"); - if (m_settings.m_replayEnabled) - { + if (m_settings.m_replayEnabled) { m_startedDateTime = QDateTime::currentDateTimeUtc(); - if (m_settings.m_sendTimeToMap) { - FeatureWebAPIUtils::mapSetDateTime(currentDateTime()); - } + } + if (m_settings.m_sendTimeToMap) { + FeatureWebAPIUtils::mapSetDateTime(currentDateTime()); } m_worker->reset(); @@ -1139,26 +1138,50 @@ void SatelliteTracker::updateSatData() /// Redirect requests for current time via these methods, for replays QDateTime SatelliteTracker::currentDateTimeUtc() { - if (m_settings.m_replayEnabled) + if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_FILE) { - if (m_settings.m_useFileInputTime) + QString dateTimeStr; + int deviceIdx = 0; + if (m_settings.m_fileInputDevice.size() >= 2) { + deviceIdx = m_settings.m_fileInputDevice.mid(1).toInt(); + } + if (ChannelWebAPIUtils::getDeviceReportValue(deviceIdx, "absoluteTime", dateTimeStr)) { - QString dateTimeStr; - if (ChannelWebAPIUtils::getDeviceReportValue(0, "absoluteTime", dateTimeStr)) - { - return QDateTime::fromString(dateTimeStr, Qt::ISODateWithMs); - } - else - { - return QDateTime::currentDateTimeUtc(); - } + return QDateTime::fromString(dateTimeStr, Qt::ISODateWithMs); } else { - QDateTime now = QDateTime::currentDateTimeUtc(); - return m_settings.m_replayStartDateTime.addSecs(m_startedDateTime.secsTo(now)); + return QDateTime::currentDateTimeUtc(); } } + else if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_MAP) + { + QString dateTimeStr; + int featureSet = 0; + int featureIdx = 0; + if (m_settings.m_mapFeature.size() >= 4) + { + QStringList numbers = m_settings.m_mapFeature.mid(1).split(":"); + if (numbers.size() == 2) + { + featureSet = numbers[0].toInt(); + featureIdx = numbers[1].toInt(); + } + } + if (ChannelWebAPIUtils::getFeatureReportValue(featureSet, featureIdx, "dateTime", dateTimeStr)) + { + return QDateTime::fromString(dateTimeStr, Qt::ISODateWithMs); + } + else + { + return QDateTime::currentDateTimeUtc(); + } + } + else if (m_settings.m_replayEnabled) + { + QDateTime now = QDateTime::currentDateTimeUtc(); + return m_settings.m_replayStartDateTime.addSecs(m_startedDateTime.secsTo(now)); + } else { return QDateTime::currentDateTimeUtc(); @@ -1167,9 +1190,9 @@ QDateTime SatelliteTracker::currentDateTimeUtc() QDateTime SatelliteTracker::currentDateTime() { - if (m_settings.m_replayEnabled) { - return currentDateTimeUtc().toLocalTime(); - } else { + if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::NOW) { return QDateTime::currentDateTime(); + } else { + return currentDateTimeUtc().toLocalTime(); } } diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index 059e60807..b830e6c05 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -26,6 +26,9 @@ #include #include +#include "device/deviceapi.h" +#include "device/deviceset.h" +#include "feature/featureset.h" #include "feature/featureuiset.h" #include "feature/featurewebapiutils.h" #include "gui/basicfeaturesettingsdialog.h" @@ -269,6 +272,7 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea ui->passChart->setRenderHint(QPainter::Antialiasing); ui->dateTime->setDateTime(m_satelliteTracker->currentDateTime()); + ui->deviceFeatureSelect->setVisible(false); // Use My Position from preferences, if none set if ((m_settings.m_latitude == 0.0) && (m_settings.m_longitude == 0.0)) { @@ -328,17 +332,9 @@ void SatelliteTrackerGUI::displaySettings() } ui->target->setCurrentIndex(ui->target->findText(m_settings.m_target)); - if (m_settings.m_dateTime == "") - { - ui->dateTimeSelect->setCurrentIndex(0); - ui->dateTime->setVisible(false); - } - else - { - ui->dateTime->setDateTime(QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs)); - ui->dateTime->setVisible(true); - ui->dateTimeSelect->setCurrentIndex(1); - } + ui->dateTimeSelect->setCurrentIndex((int)m_settings.m_dateTimeSelect); + ui->dateTime->setVisible(m_settings.m_dateTimeSelect == SatelliteTrackerSettings::CUSTOM); + ui->dateTime->setDateTime(QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs)); ui->autoTarget->setChecked(m_settings.m_autoTarget); ui->darkTheme->setChecked(m_settings.m_chartsDarkTheme); restoreState(m_rollupState); @@ -485,9 +481,10 @@ void SatelliteTrackerGUI::on_displaySettings_clicked() } } -void SatelliteTrackerGUI::on_dateTimeSelect_currentTextChanged(const QString &text) +void SatelliteTrackerGUI::on_dateTimeSelect_currentIndexChanged(int index) { - if (text == "Now") + m_settings.m_dateTimeSelect = (SatelliteTrackerSettings::DateTimeSelect)index; + if (m_settings.m_dateTimeSelect != SatelliteTrackerSettings::CUSTOM) { m_settings.m_dateTime = ""; ui->dateTime->setVisible(false); @@ -497,6 +494,9 @@ void SatelliteTrackerGUI::on_dateTimeSelect_currentTextChanged(const QString &te m_settings.m_dateTime = ui->dateTime->dateTime().toString(Qt::ISODateWithMs); ui->dateTime->setVisible(true); } + ui->deviceFeatureSelect->setVisible(m_settings.m_dateTimeSelect >= SatelliteTrackerSettings::FROM_MAP); + updateDeviceFeatureCombo(); + applySettings(); plotChart(); } @@ -595,6 +595,8 @@ void SatelliteTrackerGUI::updateStatus() } updateTimeToAOS(); + + updateDeviceFeatureCombo(); } // Update time to AOS @@ -1234,3 +1236,80 @@ QAction *SatelliteTrackerGUI::createCheckableItem(QString &text, int idx, bool c connect(action, SIGNAL(triggered()), this, SLOT(columnSelectMenuChecked())); return action; } + +void SatelliteTrackerGUI::updateDeviceFeatureCombo() +{ + if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_MAP) { + updateMapList(); + } else if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_FILE) { + updateFileInputList(); + } +} + +void SatelliteTrackerGUI::updateDeviceFeatureCombo(const QStringList &items, const QString &selected) +{ + // Remove items no longer in list + int i = 0; + while (i < ui->deviceFeatureSelect->count()) + { + if (!items.contains(ui->deviceFeatureSelect->itemText(i))) { + ui->deviceFeatureSelect->removeItem(i); + } else { + i++; + } + } + // Add new items to list + for (auto item : items) + { + int idx = ui->deviceFeatureSelect->findText(item); + if (idx == -1) { + ui->deviceFeatureSelect->addItem(item); + } + } + ui->deviceFeatureSelect->setCurrentIndex(ui->deviceFeatureSelect->findText(selected)); +} + +void SatelliteTrackerGUI::updateFileInputList() +{ + // Create list of File Input devices + std::vector& deviceSets = MainCore::instance()->getDeviceSets(); + int deviceIndex = 0; + QStringList items; + for (std::vector::const_iterator it = deviceSets.begin(); it != deviceSets.end(); ++it, deviceIndex++) + { + if ((*it)->m_deviceAPI && (*it)->m_deviceAPI->getHardwareId() == "FileInput") { + items.append(QString("R%1").arg(deviceIndex)); + } + } + updateDeviceFeatureCombo(items, m_settings.m_fileInputDevice); +} + +void SatelliteTrackerGUI::updateMapList() +{ + // Create list of Map features + std::vector& featureSets = MainCore::instance()->getFeatureeSets(); + int featureIndex = 0; + QStringList items; + for (std::vector::const_iterator it = featureSets.begin(); it != featureSets.end(); ++it, featureIndex++) + { + for (int fi = 0; fi < (*it)->getNumberOfFeatures(); fi++) + { + Feature *feature = (*it)->getFeatureAt(fi); + if (feature->getURI() == "sdrangel.feature.map") { + items.append(QString("F%1:%2").arg(featureIndex).arg(fi)); + } + } + } + updateDeviceFeatureCombo(items, m_settings.m_mapFeature); +} + +void SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged(int index) +{ + (int) index; + if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_MAP) { + m_settings.m_mapFeature = ui->deviceFeatureSelect->currentText(); + } else { + m_settings.m_fileInputDevice = ui->deviceFeatureSelect->currentText(); + } + applySettings(); +} diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h index 8170886b8..f9c9939e9 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.h +++ b/plugins/feature/satellitetracker/satellitetrackergui.h @@ -126,6 +126,10 @@ private: void updateTimeToAOS(); QString formatDaysTime(qint64 days, QDateTime dateTime); QString formatSecondsHHMM(qint64 seconds); + void updateDeviceFeatureCombo(); + void updateDeviceFeatureCombo(const QStringList &items, const QString &selected); + void updateFileInputList(); + void updateMapList(); void leaveEvent(QEvent*); void enterEvent(QEvent*); @@ -141,7 +145,7 @@ private slots: void on_target_currentTextChanged(const QString &text); void on_displaySettings_clicked(); void on_radioControl_clicked(); - void on_dateTimeSelect_currentTextChanged(const QString &text); + void on_dateTimeSelect_currentIndexChanged(int index); void on_dateTime_dateTimeChanged(const QDateTime &datetime); void updateStatus(); void on_viewOnMap_clicked(); @@ -157,6 +161,7 @@ private slots: void satTable_sectionResized(int logicalIndex, int oldSize, int newSize); void columnSelectMenu(QPoint pos); void columnSelectMenuChecked(bool checked = false); + void on_deviceFeatureSelect_currentIndexChanged(int index); }; diff --git a/plugins/feature/satellitetracker/satellitetrackergui.ui b/plugins/feature/satellitetracker/satellitetrackergui.ui index 5b157c292..f15e45f34 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.ui +++ b/plugins/feature/satellitetracker/satellitetrackergui.ui @@ -30,7 +30,6 @@ - Liberation Sans 9 @@ -96,6 +95,16 @@ Custom + + + From map + + + + + From file + + @@ -349,19 +358,6 @@ - - - - Date and time to use when calculating satellite's position - - - dd/MM/yyyy HH:mm:ss - - - true - - - @@ -369,6 +365,26 @@ + + + + + + Date and time to use when calculating satellite's position + + + dd/MM/yyyy HH:mm:ss + + + true + + + + + + + + @@ -736,6 +752,7 @@ longitude dateTimeSelect dateTime + deviceFeatureSelect target aos azimuth @@ -743,6 +760,7 @@ chartSelect prevPass nextPass + darkTheme passChart satTable diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.cpp b/plugins/feature/satellitetracker/satellitetrackersettings.cpp index ddc28624a..1d7eaa9ad 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettings.cpp @@ -74,8 +74,10 @@ void SatelliteTrackerSettings::resetToDefaults() m_reverseAPIFeatureIndex = 0; m_chartsDarkTheme = true; m_replayEnabled = false; - m_useFileInputTime = true; m_sendTimeToMap = true; + m_dateTimeSelect = NOW; + m_mapFeature = ""; + m_fileInputDevice = ""; for (int i = 0; i < SAT_COL_COLUMNS; i++) { m_columnIndexes[i] = i; @@ -128,8 +130,10 @@ QByteArray SatelliteTrackerSettings::serialize() const } s.writeBool(38, m_replayEnabled); s.writeString(39, m_replayStartDateTime.toString(Qt::ISODate)); - s.writeBool(40, m_useFileInputTime); s.writeBool(41, m_sendTimeToMap); + s.writeS32(42, (int)m_dateTimeSelect); + s.writeString(43, m_mapFeature); + s.writeString(44, m_fileInputDevice); for (int i = 0; i < SAT_COL_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -217,8 +221,10 @@ bool SatelliteTrackerSettings::deserialize(const QByteArray& data) d.readBool(38, &m_replayEnabled, false); d.readString(39, &strtmp); m_replayStartDateTime = QDateTime::fromString(strtmp, Qt::ISODate); - d.readBool(40, &m_useFileInputTime, true); d.readBool(41, &m_sendTimeToMap, true); + d.readS32(42, (int *)&m_dateTimeSelect, (int)NOW); + d.readString(43, &m_mapFeature, ""); + d.readString(44, &m_fileInputDevice, ""); for (int i = 0; i < SAT_COL_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.h b/plugins/feature/satellitetracker/satellitetrackersettings.h index 8335c9869..238d1f5c7 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.h +++ b/plugins/feature/satellitetracker/satellitetrackersettings.h @@ -78,8 +78,10 @@ struct SatelliteTrackerSettings QHash *> m_deviceSettings; //!< Settings for each device set for each satellite bool m_replayEnabled; //!< Replay a pass in the past, by setting date and time to m_replayStartDateTime QDateTime m_replayStartDateTime; //!< Time to start the replay at - bool m_useFileInputTime; //!< Get time from FileInput device bool m_sendTimeToMap; //!< Send time to map when start pressed + enum DateTimeSelect {NOW, CUSTOM, FROM_MAP, FROM_FILE} m_dateTimeSelect; + QString m_mapFeature; //!< Which feature when FROM_MAP + QString m_fileInputDevice; //!< Which device when FROM_FILE int m_columnIndexes[SAT_COL_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[SAT_COL_COLUMNS]; //!< Size of the coumns in the table diff --git a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp index 161de1571..7ae16814c 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp @@ -53,7 +53,6 @@ SatelliteTrackerSettingsDialog::SatelliteTrackerSettingsDialog(SatelliteTrackerS } ui->replayEnabled->setChecked(settings->m_replayEnabled); ui->replayDateTime->setDateTime(settings->m_replayStartDateTime); - ui->useFileInputTime->setChecked(settings->m_useFileInputTime); ui->sendTimeToMap->setChecked(settings->m_sendTimeToMap); } @@ -104,7 +103,6 @@ void SatelliteTrackerSettingsDialog::accept() } m_settings->m_replayEnabled = ui->replayEnabled->isChecked(); m_settings->m_replayStartDateTime = ui->replayDateTime->dateTime(); - m_settings->m_useFileInputTime = ui->useFileInputTime->isChecked(); m_settings->m_sendTimeToMap = ui->sendTimeToMap->isChecked(); QDialog::accept(); } diff --git a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui index b031215fd..27f18a45a 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui +++ b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui @@ -517,30 +517,13 @@ - - - Get time from FileInput device - - - - - - - Get the time from the FileInput device - - - - - - - Start date and time - + Set date and time to the displayed value when Satellite Tracker's start button is pressed @@ -560,14 +543,14 @@ - + Send time to map - + Send time to Map feature when Satellite Tracker's start button is pressed diff --git a/sdrbase/channel/channelwebapiutils.cpp b/sdrbase/channel/channelwebapiutils.cpp index 551f73e9b..d6126d883 100644 --- a/sdrbase/channel/channelwebapiutils.cpp +++ b/sdrbase/channel/channelwebapiutils.cpp @@ -134,6 +134,44 @@ bool ChannelWebAPIUtils::getFeatureSettings(unsigned int featureSetIndex, unsign return true; } +bool ChannelWebAPIUtils::getFeatureReport(unsigned int featureSetIndex, unsigned int featureIndex, SWGSDRangel::SWGFeatureReport &featureReport) +{ + QString errorResponse; + int httpRC; + FeatureSet *featureSet; + Feature *feature; + + // Get feature report + std::vector featureSets = MainCore::instance()->getFeatureeSets(); + if (featureSetIndex < featureSets.size()) + { + featureSet = featureSets[featureSetIndex]; + if (featureIndex < (unsigned int)featureSet->getNumberOfFeatures()) + { + feature = featureSet->getFeatureAt(featureIndex); + httpRC = feature->webapiReportGet(featureReport, errorResponse); + } + else + { + qDebug() << "ChannelWebAPIUtils::getFeatureReport: no feature " << featureSetIndex << ":" << featureIndex; + return false; + } + } + else + { + qDebug() << "ChannelWebAPIUtils::getFeatureReport: no feature set " << featureSetIndex; + return false; + } + + if (httpRC/100 != 2) + { + qWarning("ChannelWebAPIUtils::getFeatureReport: get feature settings error %d: %s", + httpRC, qPrintable(errorResponse)); + return false; + } + + return true; +} // Get device center frequency bool ChannelWebAPIUtils::getCenterFrequency(unsigned int deviceIndex, double &frequencyInHz) @@ -711,50 +749,43 @@ bool ChannelWebAPIUtils::patchFeatureSetting(unsigned int featureSetIndex, unsig bool ChannelWebAPIUtils::getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, int &value) { SWGSDRangel::SWGFeatureReport featureReport; - QString errorResponse; - int httpRC; - FeatureSet *featureSet; - Feature *feature; - // Get feature report - std::vector featureSets = MainCore::instance()->getFeatureeSets(); - if (featureSetIndex < featureSets.size()) + if (getFeatureReport(featureSetIndex, featureIndex, featureReport)) { - featureSet = featureSets[featureSetIndex]; - if (featureIndex < (unsigned int)featureSet->getNumberOfFeatures()) + // Get value of requested key + QJsonObject *jsonObj = featureReport.asJsonObject(); + if (WebAPIUtils::getSubObjectInt(*jsonObj, key, value)) { - feature = featureSet->getFeatureAt(featureIndex); - httpRC = feature->webapiReportGet(featureReport, errorResponse); + // Done + return true; } else { - qDebug() << "ChannelWebAPIUtils::getFeatureReportValue: no feature " << featureSetIndex << ":" << featureIndex; + qWarning("ChannelWebAPIUtils::getFeatureReportValue: no key %s in feature report", qPrintable(key)); return false; } } - else - { - qDebug() << "ChannelWebAPIUtils::getFeatureReportValue: no feature set " << featureSetIndex; - return false; - } - - if (httpRC/100 != 2) - { - qWarning("ChannelWebAPIUtils::getFeatureReportValue: get feature report error %d: %s", - httpRC, qPrintable(errorResponse)); - return false; - } - - // Get value of requested key - QJsonObject *jsonObj = featureReport.asJsonObject(); - if (WebAPIUtils::getSubObjectInt(*jsonObj, key, value)) - { - // Done - return true; - } - else - { - qWarning("ChannelWebAPIUtils::getFeatureReportValue: no key %s in feature report", qPrintable(key)); - return false; - } + return false; +} + +bool ChannelWebAPIUtils::getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, QString &value) +{ + SWGSDRangel::SWGFeatureReport featureReport; + + if (getFeatureReport(featureSetIndex, featureIndex, featureReport)) + { + // Get value of requested key + QJsonObject *jsonObj = featureReport.asJsonObject(); + if (WebAPIUtils::getSubObjectString(*jsonObj, key, value)) + { + // Done + return true; + } + else + { + qWarning("ChannelWebAPIUtils::getFeatureReportValue: no key %s in feature report", qPrintable(key)); + return false; + } + } + return false; } diff --git a/sdrbase/channel/channelwebapiutils.h b/sdrbase/channel/channelwebapiutils.h index 80639cbce..c1e8cdc1a 100644 --- a/sdrbase/channel/channelwebapiutils.h +++ b/sdrbase/channel/channelwebapiutils.h @@ -22,6 +22,7 @@ #include "SWGDeviceSettings.h" #include "SWGFeatureSettings.h" +#include "SWGFeatureReport.h" #include "export.h" @@ -46,9 +47,11 @@ public: 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 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: static bool getDeviceSettings(unsigned int deviceIndex, SWGSDRangel::SWGDeviceSettings &deviceSettingsResponse, DeviceSet *&deviceSet); static bool getFeatureSettings(unsigned int featureSetIndex, unsigned int featureIndex, SWGSDRangel::SWGFeatureSettings &featureSettingsResponse, Feature *&feature); + static bool getFeatureReport(unsigned int featureSetIndex, unsigned int featureIndex, SWGSDRangel::SWGFeatureReport &featureReport); }; #endif // SDRBASE_CHANNEL_CHANNELWEBAPIUTILS_H_ From 96b926fa333457f8e03de19ebd0c02fa657453d3 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 9 Feb 2022 16:44:41 +0000 Subject: [PATCH 5/6] Add Map swagger files --- .../sdrangel/code/qt5/client/SWGMapReport.cpp | 110 ++++++++++++++++++ .../sdrangel/code/qt5/client/SWGMapReport.h | 59 ++++++++++ 2 files changed, 169 insertions(+) create mode 100644 swagger/sdrangel/code/qt5/client/SWGMapReport.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGMapReport.h diff --git a/swagger/sdrangel/code/qt5/client/SWGMapReport.cpp b/swagger/sdrangel/code/qt5/client/SWGMapReport.cpp new file mode 100644 index 000000000..767456561 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGMapReport.cpp @@ -0,0 +1,110 @@ +/** + * 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: 6.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 "SWGMapReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGMapReport::SWGMapReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGMapReport::SWGMapReport() { + date_time = nullptr; + m_date_time_isSet = false; +} + +SWGMapReport::~SWGMapReport() { + this->cleanup(); +} + +void +SWGMapReport::init() { + date_time = new QString(""); + m_date_time_isSet = false; +} + +void +SWGMapReport::cleanup() { + if(date_time != nullptr) { + delete date_time; + } +} + +SWGMapReport* +SWGMapReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGMapReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&date_time, pJson["dateTime"], "QString", "QString"); + +} + +QString +SWGMapReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGMapReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(date_time != nullptr && *date_time != QString("")){ + toJsonValue(QString("dateTime"), date_time, obj, QString("QString")); + } + + return obj; +} + +QString* +SWGMapReport::getDateTime() { + return date_time; +} +void +SWGMapReport::setDateTime(QString* date_time) { + this->date_time = date_time; + this->m_date_time_isSet = true; +} + + +bool +SWGMapReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(date_time && *date_time != QString("")){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGMapReport.h b/swagger/sdrangel/code/qt5/client/SWGMapReport.h new file mode 100644 index 000000000..1cbb2b6e6 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGMapReport.h @@ -0,0 +1,59 @@ +/** + * 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: 6.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. + */ + +/* + * SWGMapReport.h + * + * Map + */ + +#ifndef SWGMapReport_H_ +#define SWGMapReport_H_ + +#include + + +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGMapReport: public SWGObject { +public: + SWGMapReport(); + SWGMapReport(QString* json); + virtual ~SWGMapReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGMapReport* fromJson(QString &jsonString) override; + + QString* getDateTime(); + void setDateTime(QString* date_time); + + + virtual bool isSet() override; + +private: + QString* date_time; + bool m_date_time_isSet; + +}; + +} + +#endif /* SWGMapReport_H_ */ From c31a585cb6e384c9fdc0569dfec051c4c7aa7a2d Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 9 Feb 2022 16:59:24 +0000 Subject: [PATCH 6/6] Fix gcc warning --- plugins/feature/satellitetracker/satellitetrackergui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index b830e6c05..d3f983817 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -1305,7 +1305,7 @@ void SatelliteTrackerGUI::updateMapList() void SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged(int index) { - (int) index; + (void) index; if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_MAP) { m_settings.m_mapFeature = ui->deviceFeatureSelect->currentText(); } else {