From a77b6f1b361708ff8b44baf6f831b59b43c7872b Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 01:31:39 +0100 Subject: [PATCH 1/9] Radiosonde: Support uploading to SondeHub. Improve humidity calculation. Fix a couple of bugs. --- .../Radiosonde_plugin_sondehub_settings.png | Bin 0 -> 11571 bytes plugins/feature/radiosonde/CMakeLists.txt | 3 + .../radiosondefeedsettingsdialog.cpp | 48 +++++ .../radiosonde/radiosondefeedsettingsdialog.h | 42 ++++ .../radiosondefeedsettingsdialog.ui | 155 ++++++++++++++ plugins/feature/radiosonde/radiosondegui.cpp | 126 ++++++++++- plugins/feature/radiosonde/radiosondegui.h | 9 + plugins/feature/radiosonde/radiosondegui.ui | 22 ++ .../feature/radiosonde/radiosondesettings.cpp | 61 +++++- .../feature/radiosonde/radiosondesettings.h | 11 +- plugins/feature/radiosonde/readme.md | 16 ++ sdrbase/CMakeLists.txt | 2 + sdrbase/util/radiosonde.cpp | 98 +++++++-- sdrbase/util/radiosonde.h | 5 + sdrbase/util/sondehub.cpp | 202 ++++++++++++++++++ sdrbase/util/sondehub.h | 74 +++++++ 16 files changed, 849 insertions(+), 25 deletions(-) create mode 100644 doc/img/Radiosonde_plugin_sondehub_settings.png create mode 100644 plugins/feature/radiosonde/radiosondefeedsettingsdialog.cpp create mode 100644 plugins/feature/radiosonde/radiosondefeedsettingsdialog.h create mode 100644 plugins/feature/radiosonde/radiosondefeedsettingsdialog.ui create mode 100644 sdrbase/util/sondehub.cpp create mode 100644 sdrbase/util/sondehub.h diff --git a/doc/img/Radiosonde_plugin_sondehub_settings.png b/doc/img/Radiosonde_plugin_sondehub_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..102fb2e790c7d8e133a3383f4f35712e22e6916b GIT binary patch literal 11571 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 XqI@C`M+*JW8Ae7@5m5cfIOu-?It0-g literal 0 HcmV?d00001 diff --git a/plugins/feature/radiosonde/CMakeLists.txt b/plugins/feature/radiosonde/CMakeLists.txt index c9d5a22b8..c8315a826 100644 --- a/plugins/feature/radiosonde/CMakeLists.txt +++ b/plugins/feature/radiosonde/CMakeLists.txt @@ -24,10 +24,13 @@ if(NOT SERVER_MODE) radiosondegui.cpp radiosondegui.ui radiosonde.qrc + radiosondefeedsettingsdialog.cpp + radiosondefeedsettingsdialog.ui ) set(radiosonde_HEADERS ${radiosonde_HEADERS} radiosondegui.h + radiosondefeedsettingsdialog.h ) set(TARGET_NAME featureradiosonde) diff --git a/plugins/feature/radiosonde/radiosondefeedsettingsdialog.cpp b/plugins/feature/radiosonde/radiosondefeedsettingsdialog.cpp new file mode 100644 index 000000000..11d5759c6 --- /dev/null +++ b/plugins/feature/radiosonde/radiosondefeedsettingsdialog.cpp @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2024 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "radiosondefeedsettingsdialog.h" + +RadiosondeFeedSettingsDialog::RadiosondeFeedSettingsDialog(RadiosondeSettings *settings, QWidget* parent) : + QDialog(parent), + ui(new Ui::RadiosondeFeedSettingsDialog), + m_settings(settings) +{ + ui->setupUi(this); + + ui->callsign->setText(m_settings->m_callsign); + ui->antenna->setText(m_settings->m_antenna); + ui->displayPosition->setChecked(m_settings->m_displayPosition); + ui->mobile->setChecked(m_settings->m_mobile); + ui->email->setText(m_settings->m_email); +} + +RadiosondeFeedSettingsDialog::~RadiosondeFeedSettingsDialog() +{ + delete ui; +} + +void RadiosondeFeedSettingsDialog::accept() +{ + m_settings->m_callsign = ui->callsign->text(); + m_settings->m_antenna = ui->antenna->text(); + m_settings->m_displayPosition = ui->displayPosition->isChecked(); + m_settings->m_mobile = ui->mobile->isChecked(); + m_settings->m_email = ui->email->text(); + + QDialog::accept(); +} diff --git a/plugins/feature/radiosonde/radiosondefeedsettingsdialog.h b/plugins/feature/radiosonde/radiosondefeedsettingsdialog.h new file mode 100644 index 000000000..4953935b7 --- /dev/null +++ b/plugins/feature/radiosonde/radiosondefeedsettingsdialog.h @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2024 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_RADIOSONDEFEEDSETTINGSDIALOG_H +#define INCLUDE_FEATURE_RADIOSONDEFEEDSETTINGSDIALOG_H + +#include "ui_radiosondefeedsettingsdialog.h" +#include "radiosondesettings.h" + +class RadiosondeFeedSettingsDialog : public QDialog { + Q_OBJECT + +public: + explicit RadiosondeFeedSettingsDialog(RadiosondeSettings *settings, QWidget* parent = 0); + ~RadiosondeFeedSettingsDialog(); + +private: + +private slots: + void accept(); + +private: + Ui::RadiosondeFeedSettingsDialog* ui; + RadiosondeSettings *m_settings; + +}; + +#endif // INCLUDE_FEATURE_RADIOSONDEFEEDSETTINGSDIALOG_H diff --git a/plugins/feature/radiosonde/radiosondefeedsettingsdialog.ui b/plugins/feature/radiosonde/radiosondefeedsettingsdialog.ui new file mode 100644 index 000000000..377b578ca --- /dev/null +++ b/plugins/feature/radiosonde/radiosondefeedsettingsdialog.ui @@ -0,0 +1,155 @@ + + + RadiosondeFeedSettingsDialog + + + + 0 + 0 + 441 + 211 + + + + + Liberation Sans + 9 + + + + SondeHub Feed Settings + + + + + + SondeHub Feed Settings + + + + + + Callsign + + + + + + + Callsign of feeder / uploader + + + + + + + E-mail + + + + + + + E-mail of feeder / uploader + + + + + + + Display position + + + + + + + Check to publically display receiver position on SondeHub map + + + + + + + + + + Mobile + + + + + + + Check to indicate if receiver is mobile (E.g. chase car) + + + + + + + + + + Antenna + + + + + + + Description of antenna + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + RadiosondeFeedSettingsDialog + accept() + + + 257 + 31 + + + 157 + 274 + + + + + buttonBox + rejected() + RadiosondeFeedSettingsDialog + reject() + + + 325 + 31 + + + 286 + 274 + + + + + diff --git a/plugins/feature/radiosonde/radiosondegui.cpp b/plugins/feature/radiosonde/radiosondegui.cpp index 533dcfb2a..585a6707a 100644 --- a/plugins/feature/radiosonde/radiosondegui.cpp +++ b/plugins/feature/radiosonde/radiosondegui.cpp @@ -29,12 +29,15 @@ #include "gui/decimaldelegate.h" #include "gui/tabletapandhold.h" #include "gui/dialogpositioner.h" +#include "gui/crightclickenabler.h" #include "mainwindow.h" #include "device/deviceuiset.h" +#include "device/deviceapi.h" #include "ui_radiosondegui.h" #include "radiosonde.h" #include "radiosondegui.h" +#include "radiosondefeedsettingsdialog.h" #include "SWGMapItem.h" @@ -153,6 +156,8 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + m_sondeHub = SondeHub::create(); + // Intialise chart ui->chart->setRenderHint(QPainter::Antialiasing); @@ -180,14 +185,20 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F TableTapAndHold *tableTapAndHold = new TableTapAndHold(ui->radiosondes); connect(tableTapAndHold, &TableTapAndHold::tapAndHold, this, &RadiosondeGUI::customContextMenuRequested); - ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LATITUDE, new DecimalDelegate(5)); - ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LONGITUDE, new DecimalDelegate(5)); - ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_ALTITUDE, new DecimalDelegate(1)); - ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_SPEED, new DecimalDelegate(1)); - ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_VERTICAL_RATE, new DecimalDelegate(1)); - ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_HEADING, new DecimalDelegate(1)); - ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_ALT_MAX, new DecimalDelegate(1)); - ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LAST_UPDATE, new DateTimeDelegate()); + CRightClickEnabler *feedRightClickEnabler = new CRightClickEnabler(ui->feed); + connect(feedRightClickEnabler, &CRightClickEnabler::rightClick, this, &RadiosondeGUI::feedSelect); + + // Get updated when position changes + connect(&MainCore::instance()->getSettings(), &MainSettings::preferenceChanged, this, &RadiosondeGUI::preferenceChanged); + + ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LATITUDE, new DecimalDelegate(5, ui->radiosondes)); + ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LONGITUDE, new DecimalDelegate(5, ui->radiosondes)); + ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_ALTITUDE, new DecimalDelegate(1, ui->radiosondes)); + ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_SPEED, new DecimalDelegate(1, ui->radiosondes)); + ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_VERTICAL_RATE, new DecimalDelegate(1, ui->radiosondes)); + ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_HEADING, new DecimalDelegate(1, ui->radiosondes)); + ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_ALT_MAX, new DecimalDelegate(1, ui->radiosondes)); + ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LAST_UPDATE, new DateTimeDelegate("yyyy/MM/dd hh:mm:ss", ui->radiosondes)); m_settings.setRollupState(&m_rollupState); @@ -201,9 +212,11 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F RadiosondeGUI::~RadiosondeGUI() { + disconnect(&MainCore::instance()->getSettings(), &MainSettings::preferenceChanged, this, &RadiosondeGUI::preferenceChanged); // Remove from map and free memory on_deleteAll_clicked(); delete ui; + delete m_sondeHub; } void RadiosondeGUI::setWorkspaceIndex(int index) @@ -241,9 +254,13 @@ void RadiosondeGUI::displaySettings() ui->y1->setCurrentIndex((int)m_settings.m_y1); ui->y2->setCurrentIndex((int)m_settings.m_y2); + ui->feed->setChecked(m_settings.m_feedEnabled); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); getRollupContents()->arrangeRollups(); + + updatePosition(); } void RadiosondeGUI::onMenuDialogCalled(const QPoint &p) @@ -640,6 +657,20 @@ void RadiosondeGUI::updateRadiosondes(RS41Frame *message, QDateTime dateTime) } plotChart(); + + if (m_sondeHub && m_settings.m_feedEnabled) + { + // Feed to SondeHub + m_sondeHub->upload( + MainCore::instance()->getSettings().getStationName(), + dateTime, + message, + &radiosonde->m_subframe, + MainCore::instance()->getSettings().getLatitude(), + MainCore::instance()->getSettings().getLongitude(), + MainCore::instance()->getSettings().getAltitude() + ); + } } void RadiosondeGUI::on_radiosondes_itemSelectionChanged() @@ -894,4 +925,83 @@ void RadiosondeGUI::makeUIConnections() QObject::connect(ui->y1, qOverload(&QComboBox::currentIndexChanged), this, &RadiosondeGUI::on_y1_currentIndexChanged); QObject::connect(ui->y2, qOverload(&QComboBox::currentIndexChanged), this, &RadiosondeGUI::on_y2_currentIndexChanged); QObject::connect(ui->deleteAll, &QPushButton::clicked, this, &RadiosondeGUI::on_deleteAll_clicked); + QObject::connect(ui->feed, &ButtonSwitch::clicked, this, &RadiosondeGUI::on_feed_clicked); +} + +void RadiosondeGUI::on_feed_clicked(bool checked) +{ + m_settings.m_feedEnabled = checked; + m_settingsKeys.append("feedEnabled"); + applySettings(); +} + +// Show feed dialog +void RadiosondeGUI::feedSelect(const QPoint& p) +{ + RadiosondeFeedSettingsDialog dialog(&m_settings); + dialog.move(p); + new DialogPositioner(&dialog, false); + + if (dialog.exec() == QDialog::Accepted) + { + m_settingsKeys.append("callsign"); + m_settingsKeys.append("antenna"); + m_settingsKeys.append("displayPosition"); + m_settingsKeys.append("mobile"); + m_settingsKeys.append("email"); + applySettings(); + updatePosition(); + } +} + +// Get names of devices with radiosonde demods, for SondeHub Radio string +QStringList RadiosondeGUI::getRadios() +{ + MainCore *mainCore = MainCore::instance(); + QStringList deviceList; + AvailableChannelOrFeatureList channels = mainCore->getAvailableChannels({"sdrangel.channel.radiosondedemod"}); + + for (const auto& channel : channels) + { + DeviceAPI *device = mainCore->getDevice(channel.m_index); + if (device) + { + QString name = device->getHardwareId(); + + if (!deviceList.contains(name)) { + deviceList.append(name); + } + } + } + + return deviceList; +} + +void RadiosondeGUI::updatePosition() +{ + if (m_sondeHub && m_settings.m_displayPosition) + { + float stationLatitude = MainCore::instance()->getSettings().getLatitude(); + float stationLongitude = MainCore::instance()->getSettings().getLongitude(); + float stationAltitude = MainCore::instance()->getSettings().getAltitude(); + + m_sondeHub->updatePosition( + m_settings.m_callsign, + stationLatitude, + stationLongitude, + stationAltitude, + getRadios().join(" "), + m_settings.m_antenna, + m_settings.m_email, + m_settings.m_mobile + ); + } +} + +void RadiosondeGUI::preferenceChanged(int elementType) +{ + Preferences::ElementType pref = (Preferences::ElementType)elementType; + if ((pref == Preferences::Latitude) || (pref == Preferences::Longitude) || (pref == Preferences::Altitude)) { + updatePosition(); + } } diff --git a/plugins/feature/radiosonde/radiosondegui.h b/plugins/feature/radiosonde/radiosondegui.h index 2d5a58d52..124a189ea 100644 --- a/plugins/feature/radiosonde/radiosondegui.h +++ b/plugins/feature/radiosonde/radiosondegui.h @@ -31,6 +31,7 @@ #include "feature/featuregui.h" #include "util/messagequeue.h" #include "util/radiosonde.h" +#include "util/sondehub.h" #include "settings/rollupstate.h" #include "radiosondesettings.h" @@ -101,6 +102,8 @@ private: QMenu *radiosondesMenu; // Column select context menu + SondeHub *m_sondeHub; + explicit RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); virtual ~RadiosondeGUI(); @@ -121,6 +124,8 @@ private: QAction *createCheckableItem(QString& text, int idx, bool checked, const char *slot); void plotChart(); float getData(RadiosondeSettings::ChartData dataType, RadiosondeData *radiosonde, RS41Frame *message); + void updatePosition(); + QStringList getRadios(); enum RadiosondeCol { RADIOSONDE_COL_SERIAL, @@ -157,6 +162,10 @@ private slots: void on_y1_currentIndexChanged(int index); void on_y2_currentIndexChanged(int index); void on_deleteAll_clicked(); + void on_feed_clicked(bool checked); + void feedSelect(const QPoint& p); + void preferenceChanged(int elementType); + }; #endif // INCLUDE_FEATURE_RADIOSONDEGUI_H_ diff --git a/plugins/feature/radiosonde/radiosondegui.ui b/plugins/feature/radiosonde/radiosondegui.ui index 4dccbfa46..eee177b19 100644 --- a/plugins/feature/radiosonde/radiosondegui.ui +++ b/plugins/feature/radiosonde/radiosondegui.ui @@ -399,6 +399,23 @@ + + + + Enable feeding of received frames to SondeHub. Right click for settings. + + + ... + + + + :/txon.png:/txon.png + + + true + + + @@ -419,6 +436,11 @@ + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
RollupContents QWidget diff --git a/plugins/feature/radiosonde/radiosondesettings.cpp b/plugins/feature/radiosonde/radiosondesettings.cpp index 75b6d3309..c45c27b90 100644 --- a/plugins/feature/radiosonde/radiosondesettings.cpp +++ b/plugins/feature/radiosonde/radiosondesettings.cpp @@ -2,7 +2,7 @@ // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // // written by Christian Daniel // // Copyright (C) 2015-2017, 2019-2020, 2022 Edouard Griffiths, F4EXB // -// Copyright (C) 2021-2022 Jon Beniston, M7RCE // +// Copyright (C) 2021-2024 Jon Beniston, M7RCE // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -23,6 +23,7 @@ #include "util/simpleserializer.h" #include "settings/serializable.h" +#include "maincore.h" #include "radiosondesettings.h" @@ -53,6 +54,13 @@ void RadiosondeSettings::resetToDefaults() m_y1 = ALTITUDE; m_y2 = TEMPERATURE; + m_feedEnabled = false; + m_callsign = MainCore::instance()->getSettings().getStationName(); + m_antenna = ""; + m_displayPosition = false; + m_mobile = false; + m_email = ""; + for (int i = 0; i < RADIOSONDES_COLUMNS; i++) { m_radiosondesColumnIndexes[i] = i; @@ -81,6 +89,14 @@ QByteArray RadiosondeSettings::serialize() const s.writeS32(12, m_workspaceIndex); s.writeBlob(13, m_geometryBytes); + s.writeBool(14, m_feedEnabled); + s.writeString(15, m_callsign); + s.writeString(16, m_antenna); + s.writeBool(17, m_displayPosition); + s.writeBool(18, m_mobile); + s.writeString(19, m_email); + + for (int i = 0; i < RADIOSONDES_COLUMNS; i++) { s.writeS32(300 + i, m_radiosondesColumnIndexes[i]); } @@ -137,6 +153,13 @@ bool RadiosondeSettings::deserialize(const QByteArray& data) d.readS32(12, &m_workspaceIndex, 0); d.readBlob(13, &m_geometryBytes); + d.readBool(14, &m_feedEnabled, false); + d.readString(15, &m_callsign, MainCore::instance()->getSettings().getStationName()); + d.readString(16, &m_antenna, ""); + d.readBool(17, &m_displayPosition, false); + d.readBool(18, &m_mobile, false); + d.readString(19, &m_email, ""); + for (int i = 0; i < RADIOSONDES_COLUMNS; i++) { d.readS32(300 + i, &m_radiosondesColumnIndexes[i], i); } @@ -183,6 +206,24 @@ void RadiosondeSettings::applySettings(const QStringList& settingsKeys, const Ra if (settingsKeys.contains("y2")) { m_y2 = settings.m_y2; } + if (settingsKeys.contains("feedEnabled")) { + m_feedEnabled = settings.m_feedEnabled; + } + if (settingsKeys.contains("callsign")) { + m_callsign = settings.m_callsign; + } + if (settingsKeys.contains("antenna")) { + m_antenna = settings.m_antenna; + } + if (settingsKeys.contains("displayPosition")) { + m_displayPosition = settings.m_displayPosition; + } + if (settingsKeys.contains("mobile")) { + m_mobile = settings.m_mobile; + } + if (settingsKeys.contains("email")) { + m_email = settings.m_email; + } if (settingsKeys.contains("workspaceIndex")) { m_workspaceIndex = settings.m_workspaceIndex; } @@ -233,6 +274,24 @@ QString RadiosondeSettings::getDebugString(const QStringList& settingsKeys, bool if (settingsKeys.contains("y2") || force) { ostr << " m_y2: " << m_y2; } + if (settingsKeys.contains("feedEnabled") || force) { + ostr << " m_feedEnabled: " << m_feedEnabled; + } + if (settingsKeys.contains("callsign") || force) { + ostr << " m_callsign: " << m_callsign.toStdString(); + } + if (settingsKeys.contains("antenna") || force) { + ostr << " m_antenna: " << m_antenna.toStdString(); + } + if (settingsKeys.contains("displayPosition") || force) { + ostr << " m_displayPosition: " << m_displayPosition; + } + if (settingsKeys.contains("mobile") || force) { + ostr << " m_mobile: " << m_mobile; + } + if (settingsKeys.contains("email") || force) { + ostr << " m_email: " << m_email.toStdString(); + } if (settingsKeys.contains("workspaceIndex") || force) { ostr << " m_workspaceIndex: " << m_workspaceIndex; } diff --git a/plugins/feature/radiosonde/radiosondesettings.h b/plugins/feature/radiosonde/radiosondesettings.h index b54de58e2..cc9ffc914 100644 --- a/plugins/feature/radiosonde/radiosondesettings.h +++ b/plugins/feature/radiosonde/radiosondesettings.h @@ -2,7 +2,7 @@ // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // // written by Christian Daniel // // Copyright (C) 2015-2020, 2022 Edouard Griffiths, F4EXB // -// Copyright (C) 2021-2022 Jon Beniston, M7RCE // +// Copyright (C) 2021-2024 Jon Beniston, M7RCE // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -29,7 +29,7 @@ class Serializable; // Number of columns in the table -#define RADIOSONDES_COLUMNS 16 +#define RADIOSONDES_COLUMNS 18 struct RadiosondeSettings { @@ -58,6 +58,13 @@ struct RadiosondeSettings ChartData m_y1; ChartData m_y2; + bool m_feedEnabled; + QString m_callsign; + QString m_antenna; + bool m_displayPosition; + bool m_mobile; + QString m_email; + int m_radiosondesColumnIndexes[RADIOSONDES_COLUMNS]; int m_radiosondesColumnSizes[RADIOSONDES_COLUMNS]; diff --git a/plugins/feature/radiosonde/readme.md b/plugins/feature/radiosonde/readme.md index 46812851a..e9fc443a3 100644 --- a/plugins/feature/radiosonde/readme.md +++ b/plugins/feature/radiosonde/readme.md @@ -9,6 +9,8 @@ The chart can plot two data series vs time for the radiosonde selected in the ta The Radiosonde feature can draw balloons objects on the [Map](../../feature/map/readme.md) feature in 2D and 3D. +Received data can be forwarded to [SondeHub](https://sondehub.org/). Your location can be displayed on the SondeHub map, as either a stationary receiver or chase car. +

Interface

![Radiosonde feature plugin GUI](../../../doc/img/Radiosonde_plugin.png) @@ -49,6 +51,20 @@ To centre the map on an item in the table, double click in the Lat or Lon column ![Radiosonde on map](../../../doc/img/Radiosonde_plugin_map.png) +

Feeding Data to SondeHub

+ +Received radiosonde frames can be forwarded to [SondeHub](https://sondehub.org/) by clicking the Feed button. + +Right clicking the feed button opens the SondeHub Feed Settings dialog: + +![SondeHub settings dialog](../../../doc/img/Radiosonde_plugin_sondehub_settings.png) + +* Callsign should be your amateur callsign and indicates who the frames have been received by. +* Enter your e-mail address. This isn't displayed on the SondeHub map. +* Check display position if you would like your position displayed on the SondeHub map. +* Check mobile to indicate that your receiver is mobile, and it will be displayed on the SondeHub map as a chase car. If unchecked, your receiver will be displayed as stationary with a green circle. +* Antenna is a free text string you can use to describe your antenna. This will be displayed on the SondeHub map. +

Attribution

* Hot-air-balloon icons created by Freepik - https://www.flaticon.com/free-icons/hot-air-balloon diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 8de90e62f..7f04a162e 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -269,6 +269,7 @@ set(sdrbase_SOURCES util/simpleserializer.cpp util/serialutil.cpp util/solardynamicsobservatory.cpp + util/sondehub.cpp #util/spinlock.cpp util/spyserverlist.cpp util/stix.cpp @@ -528,6 +529,7 @@ set(sdrbase_HEADERS util/simpleserializer.h util/serialutil.h util/solardynamicsobservatory.h + util/sondehub.h #util/spinlock.h util/spyserverlist.h util/stix.h diff --git a/sdrbase/util/radiosonde.cpp b/sdrbase/util/radiosonde.cpp index 0ac5037f8..37eef99c3 100644 --- a/sdrbase/util/radiosonde.cpp +++ b/sdrbase/util/radiosonde.cpp @@ -163,8 +163,8 @@ void RS41Frame::decodeGPSPos(const QByteArray ba) } } -// Find the water vapor saturation pressure for a given temperature. -float waterVapourSaturationPressure(float tCelsius) +// Find the water vapor saturation pressure for a given temperature (for tCelsius < 0C). +static float waterVapourSaturationPressure(float tCelsius) { // Convert to Kelvin float T = tCelsius + 273.15f; @@ -187,7 +187,7 @@ float waterVapourSaturationPressure(float tCelsius) return p / 100.0f; } -float calcT(int f, int f1, int f2, float r1, float r2, float *poly, float *cal) +static float calcT(int f, int f1, int f2, float r1, float r2, float *poly, float *cal) { /*float g = (float)(f2-f1) / (r2-r1); // gain float Rb = (f1*r2-f2*r1) / (float)(f2-f1); // offset @@ -219,11 +219,11 @@ float calcT(int f, int f1, int f2, float r1, float r2, float *poly, float *cal) return tCal; } -float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, float HT, float *capCal, float *matrixCal) +static float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, float HT, float *capCal, float *matrixCal, float height, float *vectorPCal, float *matrixPCal) { - //qDebug() << "cInt " << cInt << " cMin " << cMin << " cMax " << cMax << " c1 " << c1 << " c2 " << c2 << " T " << T << " HT " << HT << " capCal[0] " << capCal[0] << " capCal[1] " << capCal[1]; - /* - float a0 = 7.5f; + //qDebug() << "cInt " << cInt << " cMin " << cMin << " cMax " << cMax << " c1 " << c1 << " c2 " << c2 << " T " << T << " HT " << HT << " capCal[0] " << capCal[0] << " capCal[1] " << capCal[1] << "height" << height; + + /*float a0 = 7.5f; float a1 = 350.0f / capCal[0]; float fh = (cInt-cMin) / (float)(cMax-cMin); float rh = 100.0f * (a1*fh - a0); @@ -243,7 +243,7 @@ float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, float HT, rh = -1.0; } - qDebug() << "RH old method: " << rh; */ + qDebug() << "RH old method: " << rh;*/ // Convert integer measurement to scale factor @@ -252,8 +252,32 @@ float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, float HT, // Calculate capacitance (scale between two reference caps) float cUncal = c1 + (c2 - c1) * s; float cCal = (cUncal / capCal[0] - 1.0f) * capCal[1]; - float uUncal = 0.0f; + float t = (HT - 20.0f) / 180.0f; + + // Calculate standard pressure at given height in hPa + float pressure = 1013.25f * expf(-1.18575919e-4f * height); + + // Compensation for pressure + float p = pressure / 1000.0f; + float powc = 1.0f; + float sum = 0.0f; + for (int i = 0; i < 3; i++) + { + float l = 0.0f; + float powt = 1.0f; + for (int j = 0; j < 4; j++) + { + l += matrixPCal[4*i+j] * powt; + powt *= t; + } + float x = vectorPCal[i]; + sum += l * (x * p / (1.0f + x * p) - x * powc / (1.0f + x)); + powc *= cCal; + } + cCal -= sum; + + float uUncal = 0.0f; float f1 = 1.0f; for (int i = 0; i < 7; i++) { @@ -267,16 +291,18 @@ float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, float HT, } // Adjust for difference in outside air temperature and the humidty sensor temperature - float uCal = uUncal * waterVapourSaturationPressure(T) / waterVapourSaturationPressure(HT); + float uCal = uUncal * waterVapourSaturationPressure(HT) / waterVapourSaturationPressure(T); // Ensure within range of 0..100% uCal = std::min(100.0f, uCal); uCal = std::max(0.0f, uCal); + //qDebug() << "RH new method" << uCal; + return uCal; } -float calcP(int f, int f1, int f2, float pressureTemp, float *cal) +static float calcP(int f, int f1, int f2, float pressureTemp, float *cal) { // Convert integer measurement to scale factor float s = (f-f1) / (float)(f2-f1); @@ -434,6 +460,8 @@ void RS41Frame::calcHumidity(const RS41Subframe *subframe) float c1, c2; float capCal[2]; float calMatrix[7*6]; + float pCalMatrix[12]; + float pCalVector[3]; if (m_humidityMain == 0) { @@ -449,10 +477,13 @@ void RS41Frame::calcHumidity(const RS41Subframe *subframe) m_humidityCalibrated = m_temperatureCalibrated && m_humidityTemperatureCalibrated && humidityCalibrated; + subframe->getHumidityPressureCal(pCalVector, pCalMatrix); + m_humidity = calcU(m_humidityMain, m_humidityRef1, m_humidityRef2, c1, c2, temperature, humidityTemperature, - capCal, calMatrix); + capCal, calMatrix, + m_height, pCalVector, pCalMatrix); // RS41 humidity resolution of 0.1% m_humidityString = QString::number(m_humidity, 'f', 1); @@ -638,12 +669,51 @@ bool RS41Subframe::getPressureCal(float *cal) const } } +// Indicate if we have all the required humidity pressure calibration data +bool RS41Subframe::hasHumidityPressureCal() const +{ + return m_subframeValid[0x2a] && m_subframeValid[0x2b] && m_subframeValid[0x2c] + && m_subframeValid[0x2d] && m_subframeValid[0x2e] && m_subframeValid[0x2f]; +} + +bool RS41Subframe::getHumidityPressureCal(float *vec, float *mat) const +{ + if (hasHumidityPressureCal()) + { + for (int i = 0; i < 3; i++) { + vec[i] = getFloat(0x2a6 + i * 4); + } + for (int i = 0; i < 12; i++) { + mat[i] = getFloat(0x2ba + i * 4); + } + return true; + } + else + { + // Use default values - TODO: Need to obtain from inflight device + for (int i = 0; i < 3; i++) { + vec[i] = 0.0f; + } + for (int i = 0; i < 12; i++) { + mat[i] = 0.0f; + } + qDebug() << "hasHumidityPressureCal: false"; + return false; + } +} + // Get type of RS41. E.g. "RS41-SGP" QString RS41Subframe::getType() const { - if (m_subframeValid[0x21] & m_subframeValid[0x22]) + if (m_subframeValid[0x21] && m_subframeValid[0x22]) { - return QString(m_subframe.mid(0x218, 10)).trimmed(); + QByteArray bytes = m_subframe.mid(0x218, 10); + + while ((bytes.size() > 0) && (bytes.back() == 0)) { + bytes.removeLast(); + } + + return QString(bytes).trimmed(); } else { diff --git a/sdrbase/util/radiosonde.h b/sdrbase/util/radiosonde.h index 54914d7c3..5e75c2627 100644 --- a/sdrbase/util/radiosonde.h +++ b/sdrbase/util/radiosonde.h @@ -113,11 +113,14 @@ public: float getPressureFloat(const RS41Subframe *subframe); QString getPressureString(const RS41Subframe *subframe); + bool isPressureCalibrated() const { return m_pressureCalibrated; } float getTemperatureFloat(const RS41Subframe *subframe); QString getTemperatureString(const RS41Subframe *subframe); + bool isTemperatureCalibrated() const { return m_temperatureCalibrated; } float getHumidityTemperatureFloat(const RS41Subframe *subframe); float getHumidityFloat(const RS41Subframe *subframe); QString getHumidityString(const RS41Subframe *subframe); + bool isHumidityCalibrated() const { return m_humidityCalibrated; } static RS41Frame* decode(const QByteArray ba); static int getFrameLength(int frameType); @@ -162,6 +165,8 @@ public: bool getHumidityTempCal(float &r1, float &r2, float *poly, float *cal) const; bool hasPressureCal() const; bool getPressureCal(float *cal) const; + bool hasHumidityPressureCal() const; + bool getHumidityPressureCal(float *vec, float *mat) const; QString getType() const; QString getFrequencyMHz() const; QString getBurstKillStatus() const; diff --git a/sdrbase/util/sondehub.cpp b/sdrbase/util/sondehub.cpp new file mode 100644 index 000000000..4f8cf90f2 --- /dev/null +++ b/sdrbase/util/sondehub.cpp @@ -0,0 +1,202 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2024 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "sondehub.h" +#include "util/radiosonde.h" + +#include +#include +#include +#include +#include +#include + +SondeHub::SondeHub() +{ + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, &QNetworkAccessManager::finished, this, &SondeHub::handleReply); +} + +SondeHub::~SondeHub() +{ + disconnect(m_networkManager, &QNetworkAccessManager::finished, this, &SondeHub::handleReply); + delete m_networkManager; +} + +SondeHub* SondeHub::create() +{ + return new SondeHub(); +} + +void SondeHub::upload( + const QString uploaderCallsign, + QDateTime timeReceived, + RS41Frame *frame, + const RS41Subframe *subframe, + float uploaderLat, + float uploaderLon, + float uploaderAlt + ) +{ + // Check we have required data + if (!frame->m_statusValid || !frame->m_posValid) { + return; + } + + QJsonArray uploaderPos { + uploaderLat, uploaderLon, uploaderAlt + }; + + QJsonObject obj { + {"software_name", "SDRangel"}, + {"software_version", qApp->applicationVersion()}, + {"uploader_callsign", uploaderCallsign}, + {"time_received", timeReceived.toUTC().toString("yyyy-MM-ddTHH:mm:ss.zzz000Z")}, + {"manufacturer", "Vaisala"}, + {"type", "RS41"}, + {"uploader_position", uploaderPos} + }; + + if (frame->m_statusValid) + { + obj.insert("frame", frame->m_frameNumber); + obj.insert("serial", frame->m_serial); + obj.insert("batt", frame->m_batteryVoltage); + } + + if (frame->m_measValid) + { + // Don't upload uncalibrated measurements, as there can be a significant error + if (frame->isTemperatureCalibrated()) { + obj.insert("temp", frame->getTemperatureFloat(subframe)); + } + if (frame->isHumidityCalibrated()) + { + float humidity = frame->getHumidityFloat(subframe); + if (humidity != 0.0f) { + obj.insert("humidity", humidity); + } + } + if (frame->isPressureCalibrated()) + { + float pressure = frame->getPressureFloat(subframe); + if (pressure != 0.0f) { + obj.insert("pressure", pressure); + } + } + } + + if (frame->m_gpsInfoValid) + { + obj.insert("datetime", frame->m_gpsDateTime.toUTC().addSecs(18).toString("yyyy-MM-ddTHH:mm:ss.zzz000Z")); // +18 adjusts UTC to GPS time + } + + if (frame->m_posValid) + { + obj.insert("lat", frame->m_latitude); + obj.insert("lon", frame->m_longitude); + obj.insert("alt", frame->m_height); + obj.insert("vel_h", frame->m_speed); + obj.insert("vel_v", frame->m_verticalRate); + obj.insert("heading", frame->m_heading); + obj.insert("sats", frame->m_satellitesUsed); + } + + if (!subframe->getFrequencyMHz().isEmpty()) { + obj.insert("frequency", std::round(subframe->getFrequencyMHz().toFloat() * 100.0) / 100.0); + } + + if (subframe->getType() != "RS41") { + obj.insert("subtype", subframe->getType()); + } + + //qDebug() << obj; + QJsonArray payloads { + obj + }; + + QJsonDocument doc(payloads); + QByteArray data = doc.toJson(); + + QUrl url(QString("https://api.v2.sondehub.org/sondes/telemetry")); + + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + request.setHeader(QNetworkRequest::UserAgentHeader, "sdrangel"); + request.setRawHeader("Date", QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs).toLatin1()); + + m_networkManager->put(request, data); +} + +void SondeHub::updatePosition( + const QString& callsign, + float latitude, + float longitude, + float altitude, + const QString& radio, + const QString& antenna, + const QString& email, + bool mobile + ) +{ + QJsonArray position { + latitude, longitude, altitude + }; + + QJsonObject obj { + {"software_name", "SDRangel"}, + {"software_version", qApp->applicationVersion()}, + {"uploader_callsign", callsign}, + {"uploader_position", position}, + {"uploader_radio", radio}, + {"uploader_antenna", antenna}, + {"uploader_contact_email", email}, + {"mobile", mobile} + }; + + QJsonDocument doc(obj); + QByteArray data = doc.toJson(); + + QUrl url(QString("https://api.v2.sondehub.org/listeners")); + + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + request.setHeader(QNetworkRequest::UserAgentHeader, "sdrangel"); + + m_networkManager->put(request, data); +} + +void SondeHub::handleReply(QNetworkReply* reply) +{ + if (reply) + { + if (!reply->error()) + { + QByteArray bytes = reply->readAll(); + //qDebug() << bytes; + } + else + { + qDebug() << "SondeHub::handleReply: error: " << reply->error() << reply->readAll(); + } + reply->deleteLater(); + } + else + { + qDebug() << "SondeHub::handleReply: reply is null"; + } +} diff --git a/sdrbase/util/sondehub.h b/sdrbase/util/sondehub.h new file mode 100644 index 000000000..6ad663a6f --- /dev/null +++ b/sdrbase/util/sondehub.h @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2024 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SONDEHUB_H +#define INCLUDE_SONDEHUB_H + +#include +#include + +#include "export.h" + +class QNetworkAccessManager; +class QNetworkReply; +class RS41Frame; +class RS41Subframe; + +class SDRBASE_API SondeHub : public QObject +{ + Q_OBJECT +protected: + SondeHub(); + +public: + + static SondeHub* create(); + + ~SondeHub(); + + void upload( + const QString uploaderCallsign, + QDateTime timeReceived, + RS41Frame *frame, + const RS41Subframe *subframe, + float uploaderLat, + float uploaderLon, + float uploaderAlt + ); + + void updatePosition( + const QString& callsign, + float latitude, + float longitude, + float altitude, + const QString& radio, + const QString& antenna, + const QString& email, + bool mobile + ); + + +private slots: + void handleReply(QNetworkReply* reply); + +private: + + QNetworkAccessManager *m_networkManager; + +}; + +#endif /* INCLUDE_SONDEHUB_H */ From 54713ef67ca2a5d5649eb8a34cf03dc7bd6306e1 Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 01:32:19 +0100 Subject: [PATCH 2/9] Add DialogPositioner --- plugins/channelrx/demodadsb/adsbdemodgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index a5737f5cb..6088baa51 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -4671,6 +4671,7 @@ void ADSBDemodGUI::feedSelect(const QPoint& p) { ADSBDemodFeedDialog dialog(&m_settings); dialog.move(p); + new DialogPositioner(&dialog, false); if (dialog.exec() == QDialog::Accepted) { @@ -5040,6 +5041,7 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb ADSBDemodGUI::~ADSBDemodGUI() { + disconnect(&MainCore::instance()->getSettings(), &MainSettings::preferenceChanged, this, &ADSBDemodGUI::preferenceChanged); if (m_templateServer) { m_templateServer->close(); From 6979117b2646b9dabf692e6ef2d600dc6cf82487 Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 01:32:30 +0100 Subject: [PATCH 3/9] Fix channel marker --- plugins/channelrx/remotetcpsink/remotetcpsinkgui.cpp | 11 +++++++---- plugins/channelrx/remotetcpsink/remotetcpsinkgui.h | 5 +---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkgui.cpp b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.cpp index 4b69bd92d..34f87af36 100644 --- a/plugins/channelrx/remotetcpsink/remotetcpsinkgui.cpp +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.cpp @@ -282,6 +282,7 @@ void RemoteTCPSinkGUI::displayRateAndShift() { m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); m_channelMarker.setBandwidth(m_settings.m_channelSampleRate); + //m_channelMarker.setVisible(m_settings.m_channelSampleRate != m_basebandSampleRate); // Hide marker if it takes up full bandwidth } void RemoteTCPSinkGUI::leaveEvent(QEvent* event) @@ -389,17 +390,19 @@ void RemoteTCPSinkGUI::channelMarkerHighlightedByCursor() setHighlighted(m_channelMarker.getHighlighted()); } -void RemoteTCPSinkGUI::on_deltaFrequency_changed(int index) +void RemoteTCPSinkGUI::on_deltaFrequency_changed(qint64 value) { - m_settings.m_inputFrequencyOffset = index; + m_channelMarker.setCenterFrequency(value); + m_settings.m_inputFrequencyOffset = value; applySetting("inputFrequencyOffset"); } -void RemoteTCPSinkGUI::on_channelSampleRate_changed(int index) +void RemoteTCPSinkGUI::on_channelSampleRate_changed(int value) { - m_settings.m_channelSampleRate = index; + m_settings.m_channelSampleRate = value; m_bwAvg.reset(); applySetting("channelSampleRate"); + displayRateAndShift(); } void RemoteTCPSinkGUI::on_gain_valueChanged(int value) diff --git a/plugins/channelrx/remotetcpsink/remotetcpsinkgui.h b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.h index dc1fc9fce..1dc58bd61 100644 --- a/plugins/channelrx/remotetcpsink/remotetcpsinkgui.h +++ b/plugins/channelrx/remotetcpsink/remotetcpsinkgui.h @@ -103,12 +103,9 @@ private: void leaveEvent(QEvent*); void enterEvent(EnterEventType*); - void applyDecimation(); - void applyPosition(); - private slots: void handleSourceMessages(); - void on_deltaFrequency_changed(int index); + void on_deltaFrequency_changed(qint64 value); void on_channelSampleRate_changed(int value); void on_gain_valueChanged(int value); void on_sampleBits_currentIndexChanged(int index); From 5e79f06c0ec733d71ada8d5eb94b4aa1059a3b50 Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 01:33:42 +0100 Subject: [PATCH 4/9] Add parent to delegate constructors. --- sdrgui/gui/datetimedelegate.cpp | 5 +++-- sdrgui/gui/datetimedelegate.h | 4 ++-- sdrgui/gui/decimaldelegate.cpp | 8 +++++--- sdrgui/gui/decimaldelegate.h | 6 +++--- sdrgui/gui/frequencydelegate.cpp | 3 ++- sdrgui/gui/frequencydelegate.h | 2 +- sdrgui/gui/int64delegate.cpp | 6 ++++-- sdrgui/gui/int64delegate.h | 4 ++-- sdrgui/gui/nanosecondsdelegate.cpp | 3 ++- sdrgui/gui/nanosecondsdelegate.h | 2 +- sdrgui/gui/timedelegate.cpp | 3 ++- sdrgui/gui/timedelegate.h | 2 +- 12 files changed, 28 insertions(+), 20 deletions(-) diff --git a/sdrgui/gui/datetimedelegate.cpp b/sdrgui/gui/datetimedelegate.cpp index 870b96193..0bc1d211d 100644 --- a/sdrgui/gui/datetimedelegate.cpp +++ b/sdrgui/gui/datetimedelegate.cpp @@ -2,7 +2,7 @@ // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // // written by Christian Daniel // // Copyright (C) 2015-2020 Edouard Griffiths, F4EXB // -// Copyright (C) 2020-2022 Jon Beniston, M7RCE // +// Copyright (C) 2020-2024 Jon Beniston, M7RCE // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -22,7 +22,8 @@ #include "datetimedelegate.h" -DateTimeDelegate::DateTimeDelegate(QString format) : +DateTimeDelegate::DateTimeDelegate(QString format, QObject *parent) : + QStyledItemDelegate(parent), m_format(format) { } diff --git a/sdrgui/gui/datetimedelegate.h b/sdrgui/gui/datetimedelegate.h index d54ccafda..838979691 100644 --- a/sdrgui/gui/datetimedelegate.h +++ b/sdrgui/gui/datetimedelegate.h @@ -2,7 +2,7 @@ // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // // written by Christian Daniel // // Copyright (C) 2015-2019 Edouard Griffiths, F4EXB // -// Copyright (C) 2021-2022 Jon Beniston, M7RCE // +// Copyright (C) 2021-2024 Jon Beniston, M7RCE // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -29,7 +29,7 @@ class SDRGUI_API DateTimeDelegate : public QStyledItemDelegate { public: - DateTimeDelegate(QString format = "yyyy/MM/dd hh:mm:ss"); + DateTimeDelegate(QString format = "yyyy/MM/dd hh:mm:ss", QObject *parent = nullptr); virtual QString displayText(const QVariant &value, const QLocale &locale) const override; private: diff --git a/sdrgui/gui/decimaldelegate.cpp b/sdrgui/gui/decimaldelegate.cpp index 2b56b74cd..bc3f297a5 100644 --- a/sdrgui/gui/decimaldelegate.cpp +++ b/sdrgui/gui/decimaldelegate.cpp @@ -2,7 +2,7 @@ // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // // written by Christian Daniel // // Copyright (C) 2015-2020 Edouard Griffiths, F4EXB // -// Copyright (C) 2020-2022 Jon Beniston, M7RCE // +// Copyright (C) 2020-2024 Jon Beniston, M7RCE // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -41,14 +41,16 @@ public: } }; -DecimalDelegate::DecimalDelegate(int precision) : +DecimalDelegate::DecimalDelegate(int precision, QObject *parent) : + QStyledItemDelegate(parent), m_precision(precision), m_min(-std::numeric_limits::max()), m_max(std::numeric_limits::max()) { } -DecimalDelegate::DecimalDelegate(int precision, double min, double max) : +DecimalDelegate::DecimalDelegate(int precision, double min, double max, QObject *parent) : + QStyledItemDelegate(parent), m_precision(precision), m_min(min), m_max(max) diff --git a/sdrgui/gui/decimaldelegate.h b/sdrgui/gui/decimaldelegate.h index a88ec4dad..5ff79e57b 100644 --- a/sdrgui/gui/decimaldelegate.h +++ b/sdrgui/gui/decimaldelegate.h @@ -2,7 +2,7 @@ // Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // // written by Christian Daniel // // Copyright (C) 2015-2019 Edouard Griffiths, F4EXB // -// Copyright (C) 2021-2023 Jon Beniston, M7RCE // +// Copyright (C) 2021-2024 Jon Beniston, M7RCE // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -30,8 +30,8 @@ class SDRGUI_API DecimalDelegate : public QStyledItemDelegate { public: - DecimalDelegate(int precision = 2); - DecimalDelegate(int precision, double min, double max); + DecimalDelegate(int precision = 2, QObject *parent = nullptr); + DecimalDelegate(int precision, double min, double max, QObject *parent = nullptr); virtual QString displayText(const QVariant &value, const QLocale &locale) const override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; diff --git a/sdrgui/gui/frequencydelegate.cpp b/sdrgui/gui/frequencydelegate.cpp index 057f5d3a4..4953d2c72 100644 --- a/sdrgui/gui/frequencydelegate.cpp +++ b/sdrgui/gui/frequencydelegate.cpp @@ -23,7 +23,8 @@ #include "frequencydelegate.h" #include "int64validator.h" -FrequencyDelegate::FrequencyDelegate(const QString& units, int precision, bool group) : +FrequencyDelegate::FrequencyDelegate(const QString& units, int precision, bool group, QObject *parent) : + QStyledItemDelegate(parent), m_units(units), m_precision(precision), m_group(group) diff --git a/sdrgui/gui/frequencydelegate.h b/sdrgui/gui/frequencydelegate.h index ff7aa8d16..700b11e89 100644 --- a/sdrgui/gui/frequencydelegate.h +++ b/sdrgui/gui/frequencydelegate.h @@ -29,7 +29,7 @@ class SDRGUI_API FrequencyDelegate : public QStyledItemDelegate { public: - FrequencyDelegate(const QString& units = "kHz", int precision=1, bool group=true); + FrequencyDelegate(const QString& units = "kHz", int precision=1, bool group=true, QObject *parent = nullptr); QString displayText(const QVariant &value, const QLocale &locale) const override; protected: diff --git a/sdrgui/gui/int64delegate.cpp b/sdrgui/gui/int64delegate.cpp index c5a8e7781..4fff34890 100644 --- a/sdrgui/gui/int64delegate.cpp +++ b/sdrgui/gui/int64delegate.cpp @@ -20,13 +20,15 @@ #include "int64delegate.h" #include "int64validator.h" -Int64Delegate::Int64Delegate() : +Int64Delegate::Int64Delegate(QObject *parent) : + QStyledItemDelegate(parent), m_min(-std::numeric_limits::max()), m_max(std::numeric_limits::max()) { } -Int64Delegate::Int64Delegate(qint64 min, qint64 max) : +Int64Delegate::Int64Delegate(qint64 min, qint64 max, QObject *parent) : + QStyledItemDelegate(parent), m_min(min), m_max(max) { diff --git a/sdrgui/gui/int64delegate.h b/sdrgui/gui/int64delegate.h index e69688f07..9bea32ca4 100644 --- a/sdrgui/gui/int64delegate.h +++ b/sdrgui/gui/int64delegate.h @@ -27,8 +27,8 @@ class SDRGUI_API Int64Delegate : public QStyledItemDelegate { public: - Int64Delegate(); - Int64Delegate(qint64 min, qint64 max); + Int64Delegate(QObject *parent = nullptr); + Int64Delegate(qint64 min, qint64 max, QObject *parent = nullptr); virtual QString displayText(const QVariant &value, const QLocale &locale) const override; virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setMin(qint64 min) { m_min = min; } diff --git a/sdrgui/gui/nanosecondsdelegate.cpp b/sdrgui/gui/nanosecondsdelegate.cpp index afc107f81..5a1cb93ba 100644 --- a/sdrgui/gui/nanosecondsdelegate.cpp +++ b/sdrgui/gui/nanosecondsdelegate.cpp @@ -20,7 +20,8 @@ #include "nanosecondsdelegate.h" -NanoSecondsDelegate::NanoSecondsDelegate() +NanoSecondsDelegate::NanoSecondsDelegate(QObject *parent) : + QStyledItemDelegate(parent) { } diff --git a/sdrgui/gui/nanosecondsdelegate.h b/sdrgui/gui/nanosecondsdelegate.h index 5122bb69a..1914d5470 100644 --- a/sdrgui/gui/nanosecondsdelegate.h +++ b/sdrgui/gui/nanosecondsdelegate.h @@ -29,7 +29,7 @@ class SDRGUI_API NanoSecondsDelegate : public QStyledItemDelegate { public: - NanoSecondsDelegate(); + NanoSecondsDelegate(QObject *parent = nullptr); virtual QString displayText(const QVariant &value, const QLocale &locale) const override; }; diff --git a/sdrgui/gui/timedelegate.cpp b/sdrgui/gui/timedelegate.cpp index 0c2e50d63..b0a0560f0 100644 --- a/sdrgui/gui/timedelegate.cpp +++ b/sdrgui/gui/timedelegate.cpp @@ -22,7 +22,8 @@ #include "timedelegate.h" -TimeDelegate::TimeDelegate(QString format) : +TimeDelegate::TimeDelegate(QString format, QObject *parent) : + QStyledItemDelegate(parent), m_format(format) { } diff --git a/sdrgui/gui/timedelegate.h b/sdrgui/gui/timedelegate.h index adab31fdf..fb4cb2270 100644 --- a/sdrgui/gui/timedelegate.h +++ b/sdrgui/gui/timedelegate.h @@ -29,7 +29,7 @@ class SDRGUI_API TimeDelegate : public QStyledItemDelegate { public: - TimeDelegate(QString format = "hh:mm:ss"); + TimeDelegate(QString format = "hh:mm:ss", QObject *parent = nullptr); virtual QString displayText(const QVariant &value, const QLocale &locale) const override; private: From d09b3a2fb221f7d38eee0e289e65edc37e887423 Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 01:34:06 +0100 Subject: [PATCH 5/9] Use aApp application version instead of hardcoded value. --- plugins/feature/aprs/aprsworker.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/feature/aprs/aprsworker.cpp b/plugins/feature/aprs/aprsworker.cpp index 2a830dd7b..38d55a6dc 100644 --- a/plugins/feature/aprs/aprsworker.cpp +++ b/plugins/feature/aprs/aprsworker.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "webapi/webapiadapterinterface.h" #include "webapi/webapiutils.h" @@ -212,7 +213,12 @@ void APRSWorker::recv() if (!m_loggedIn) { // Log in with callsign and passcode - QString login = QString("user %1 pass %2 vers SDRangel 7.19.2%3\r\n").arg(m_settings.m_igateCallsign).arg(m_settings.m_igatePasscode).arg(m_settings.m_igateFilter.isEmpty() ? "" : QString(" filter %1").arg(m_settings.m_igateFilter)); + QString login = QString("user %1 pass %2 vers SDRangel %3%4\r\n") + .arg(m_settings.m_igateCallsign) + .arg(m_settings.m_igatePasscode) + .arg(qApp->applicationVersion()) + .arg(m_settings.m_igateFilter.isEmpty() ? "" : QString(" filter %1").arg(m_settings.m_igateFilter) + ); send(login.toLatin1(), login.length()); m_loggedIn = true; if (m_msgQueueToFeature) From 2973eff337d8743d47cd2dc02167c51281e8c92a Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 01:40:57 +0100 Subject: [PATCH 6/9] Preset: Copy and initialise all fields for #2061. --- sdrbase/settings/preset.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sdrbase/settings/preset.cpp b/sdrbase/settings/preset.cpp index ff57b4904..fce681fc7 100644 --- a/sdrbase/settings/preset.cpp +++ b/sdrbase/settings/preset.cpp @@ -31,10 +31,16 @@ Preset::Preset() } Preset::Preset(const Preset& other) : + m_presetType(other.m_presetType), m_group(other.m_group), m_description(other.m_description), m_centerFrequency(other.m_centerFrequency), m_spectrumConfig(other.m_spectrumConfig), + m_spectrumGeometry(other.m_spectrumGeometry), + m_spectrumWorkspaceIndex(other.m_spectrumWorkspaceIndex), + m_deviceGeometry(other.m_deviceGeometry), + m_deviceWorkspaceIndex(other.m_deviceWorkspaceIndex), + m_selectedDevice(other.m_selectedDevice), m_dcOffsetCorrection(other.m_dcOffsetCorrection), m_iqImbalanceCorrection(other.m_iqImbalanceCorrection), m_channelConfigs(other.m_channelConfigs), @@ -50,6 +56,14 @@ void Preset::resetToDefaults() m_description = "no name"; m_centerFrequency = 0; m_spectrumConfig.clear(); + m_spectrumGeometry.clear(); + m_spectrumWorkspaceIndex = 0; + m_selectedDevice.m_deviceId = ""; + m_selectedDevice.m_deviceSerial = ""; + m_selectedDevice.m_deviceSequence = 0; + m_selectedDevice.m_deviceItemIndex = 0; + m_deviceGeometry.clear(); + m_deviceWorkspaceIndex = 0; m_layout.clear(); m_channelConfigs.clear(); m_dcOffsetCorrection = false; From c3a1c8db0e53daa213def58dee23b1c45cd3a16d Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 01:41:35 +0100 Subject: [PATCH 7/9] Fix sdrbench compilation on Windows --- sdrbench/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdrbench/CMakeLists.txt b/sdrbench/CMakeLists.txt index 6a5546423..e6790b575 100644 --- a/sdrbench/CMakeLists.txt +++ b/sdrbench/CMakeLists.txt @@ -11,6 +11,7 @@ set(sdrbench_SOURCES test_ft8.cpp test_callsign.cpp test_ft8protocols.cpp + ../ft8/pack0.cpp ) set(sdrbench_HEADERS @@ -24,6 +25,7 @@ add_library(sdrbench SHARED include_directories( ${FFTW3F_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/exports ${CMAKE_SOURCE_DIR}/sdrbase ${CMAKE_SOURCE_DIR}/logging @@ -31,6 +33,7 @@ include_directories( ) target_link_libraries(sdrbench + Boost::disable_autolinking ${FFTW3F_LIBRARIES} Qt::Core Qt::Gui From 84960eff038e68426b6381c80b30907b285c28f5 Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 01:58:31 +0100 Subject: [PATCH 8/9] Fix Qt5 compilation --- sdrbase/util/radiosonde.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sdrbase/util/radiosonde.cpp b/sdrbase/util/radiosonde.cpp index 37eef99c3..34c2b0124 100644 --- a/sdrbase/util/radiosonde.cpp +++ b/sdrbase/util/radiosonde.cpp @@ -697,7 +697,6 @@ bool RS41Subframe::getHumidityPressureCal(float *vec, float *mat) const for (int i = 0; i < 12; i++) { mat[i] = 0.0f; } - qDebug() << "hasHumidityPressureCal: false"; return false; } } @@ -709,8 +708,8 @@ QString RS41Subframe::getType() const { QByteArray bytes = m_subframe.mid(0x218, 10); - while ((bytes.size() > 0) && (bytes.back() == 0)) { - bytes.removeLast(); + while ((bytes.size() > 0) && (bytes.back() == '\0')) { + bytes.remove(bytes.size() - 1, 1); } return QString(bytes).trimmed(); From 4439b435b7326dd8e206583fc9920a2c9fc2230f Mon Sep 17 00:00:00 2001 From: srcejon Date: Wed, 10 Apr 2024 10:50:45 +0100 Subject: [PATCH 9/9] Remove unneeded file --- sdrbench/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/sdrbench/CMakeLists.txt b/sdrbench/CMakeLists.txt index e6790b575..6aefbe4b8 100644 --- a/sdrbench/CMakeLists.txt +++ b/sdrbench/CMakeLists.txt @@ -11,7 +11,6 @@ set(sdrbench_SOURCES test_ft8.cpp test_callsign.cpp test_ft8protocols.cpp - ../ft8/pack0.cpp ) set(sdrbench_HEADERS