mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 01:55:48 -05:00
Radiosonde: Support uploading to SondeHub. Improve humidity calculation. Fix a couple of bugs.
This commit is contained in:
parent
872bc8f13f
commit
a77b6f1b36
BIN
doc/img/Radiosonde_plugin_sondehub_settings.png
Normal file
BIN
doc/img/Radiosonde_plugin_sondehub_settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -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)
|
||||
|
48
plugins/feature/radiosonde/radiosondefeedsettingsdialog.cpp
Normal file
48
plugins/feature/radiosonde/radiosondefeedsettingsdialog.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#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();
|
||||
}
|
42
plugins/feature/radiosonde/radiosondefeedsettingsdialog.h
Normal file
42
plugins/feature/radiosonde/radiosondefeedsettingsdialog.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#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
|
155
plugins/feature/radiosonde/radiosondefeedsettingsdialog.ui
Normal file
155
plugins/feature/radiosonde/radiosondefeedsettingsdialog.ui
Normal file
@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RadiosondeFeedSettingsDialog</class>
|
||||
<widget class="QDialog" name="RadiosondeFeedSettingsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>441</width>
|
||||
<height>211</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SondeHub Feed Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>SondeHub Feed Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="callsignLabel">
|
||||
<property name="text">
|
||||
<string>Callsign</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="callsign">
|
||||
<property name="toolTip">
|
||||
<string>Callsign of feeder / uploader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="emailLabel">
|
||||
<property name="text">
|
||||
<string>E-mail</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="email">
|
||||
<property name="toolTip">
|
||||
<string>E-mail of feeder / uploader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="displayPositionLabel">
|
||||
<property name="text">
|
||||
<string>Display position</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="displayPosition">
|
||||
<property name="toolTip">
|
||||
<string>Check to publically display receiver position on SondeHub map</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="mobileLabel">
|
||||
<property name="text">
|
||||
<string>Mobile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="mobile">
|
||||
<property name="toolTip">
|
||||
<string>Check to indicate if receiver is mobile (E.g. chase car)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="antennaLabel">
|
||||
<property name="text">
|
||||
<string>Antenna</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="antenna">
|
||||
<property name="toolTip">
|
||||
<string>Description of antenna</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>RadiosondeFeedSettingsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>257</x>
|
||||
<y>31</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>RadiosondeFeedSettingsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>325</x>
|
||||
<y>31</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -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<int>(&QComboBox::currentIndexChanged), this, &RadiosondeGUI::on_y1_currentIndexChanged);
|
||||
QObject::connect(ui->y2, qOverload<int>(&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();
|
||||
}
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -399,6 +399,23 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="feed">
|
||||
<property name="toolTip">
|
||||
<string>Enable feeding of received frames to SondeHub. Right click for settings.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/txon.png</normaloff>:/txon.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@ -419,6 +436,11 @@
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ButtonSwitch</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>gui/buttonswitch.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RollupContents</class>
|
||||
<extends>QWidget</extends>
|
||||
|
@ -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 <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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;
|
||||
}
|
||||
|
@ -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 <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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];
|
||||
|
||||
|
@ -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.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![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)
|
||||
|
||||
<h3>Feeding Data to SondeHub</h3>
|
||||
|
||||
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.
|
||||
|
||||
<h2>Attribution</h2>
|
||||
|
||||
* Hot-air-balloon icons created by Freepik - https://www.flaticon.com/free-icons/hot-air-balloon
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
202
sdrbase/util/sondehub.cpp
Normal file
202
sdrbase/util/sondehub.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "sondehub.h"
|
||||
#include "util/radiosonde.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QNetworkReply>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
74
sdrbase/util/sondehub.h
Normal file
74
sdrbase/util/sondehub.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_SONDEHUB_H
|
||||
#define INCLUDE_SONDEHUB_H
|
||||
|
||||
#include <QtCore>
|
||||
#include <QDateTime>
|
||||
|
||||
#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 */
|
Loading…
Reference in New Issue
Block a user