diff --git a/doc/img/Radiosonde_plugin_sondehub_settings.png b/doc/img/Radiosonde_plugin_sondehub_settings.png new file mode 100644 index 000000000..102fb2e79 Binary files /dev/null and b/doc/img/Radiosonde_plugin_sondehub_settings.png differ 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(); 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); 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) 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/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; diff --git a/sdrbase/util/radiosonde.cpp b/sdrbase/util/radiosonde.cpp index 0ac5037f8..34c2b0124 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,50 @@ 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; + } + 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.remove(bytes.size() - 1, 1); + } + + 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 */ diff --git a/sdrbench/CMakeLists.txt b/sdrbench/CMakeLists.txt index 6a5546423..6aefbe4b8 100644 --- a/sdrbench/CMakeLists.txt +++ b/sdrbench/CMakeLists.txt @@ -24,6 +24,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 +32,7 @@ include_directories( ) target_link_libraries(sdrbench + Boost::disable_autolinking ${FFTW3F_LIBRARIES} Qt::Core Qt::Gui 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: