1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-06-18 14:22:30 -04:00

Merge branch 'fix_2283' of https://github.com/srcejon/sdrangel into fix_2283

This commit is contained in:
Jon Beniston 2025-03-06 17:12:01 +00:00
commit ad46defda9
39 changed files with 506 additions and 62 deletions

@ -9,12 +9,18 @@ on:
jobs: jobs:
build_mac_x64: build_mac_x64:
runs-on: macos-12 runs-on: macos-13
env:
MACOSX_DEPLOYMENT_TARGET: 12.0
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
fetch-depth: 0 fetch-depth: 0
- name: Update brew
run: brew update
- name: Install brew packages
run: brew install nasm subversion
- name: Install SDRplay API - name: Install SDRplay API
run: | run: |
wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.15.0.pkg wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.15.0.pkg
@ -40,7 +46,23 @@ jobs:
- name: Build SDRangel on Mac - name: Build SDRangel on Mac
run: | run: |
cd build cd build
make -j4 package make -j4
- name: Create dmg
run: |
cd build
RETRIES=5
COUNT=1
set +e
while [ $COUNT -lt $RETRIES ]; do
make package
if [ $? -eq 0 ]; then
RETRIES=0
break
fi
let COUNT=$COUNT+1
done
shell: bash
continue-on-error: true
- name: Get version - name: Get version
id: get_version id: get_version
run: | run: |
@ -99,7 +121,23 @@ jobs:
- name: Build SDRangel on Mac - name: Build SDRangel on Mac
run: | run: |
cd build cd build
make -j3 package make -j3
- name: Create dmg
run: |
cd build
RETRIES=5
COUNT=1
set +e
while [ $COUNT -lt $RETRIES ]; do
make package
if [ $? -eq 0 ]; then
RETRIES=0
break
fi
let COUNT=$COUNT+1
done
shell: bash
continue-on-error: true
- name: Get version - name: Get version
id: get_version id: get_version
run: | run: |

@ -1,3 +1,9 @@
sdrangel (7.22.6-1) unstable; urgency=medium
* See Github release
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 02 Feb 2025 18:08:11 +0100
sdrangel (7.22.5-1) unstable; urgency=medium sdrangel (7.22.5-1) unstable; urgency=medium
* Windows: upload signed releases rather than unsigned releases to Github releases page. PR #2347 * Windows: upload signed releases rather than unsigned releases to Github releases page. PR #2347

@ -20,7 +20,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# configure version # configure version
set(sdrangel_VERSION_MAJOR "7") set(sdrangel_VERSION_MAJOR "7")
set(sdrangel_VERSION_MINOR "22") set(sdrangel_VERSION_MINOR "22")
set(sdrangel_VERSION_PATCH "5") set(sdrangel_VERSION_PATCH "6")
set(sdrangel_VERSION_SUFFIX "") set(sdrangel_VERSION_SUFFIX "")
# SDRAngel cmake options # SDRAngel cmake options

@ -19,7 +19,11 @@ if(WIN32)
endif() endif()
if(NOT MSVC) if(NOT MSVC)
add_compile_options(-Wall -Wextra -Wvla -Woverloaded-virtual -Wno-inconsistent-missing-override -ffast-math -fno-finite-math-only -ftree-vectorize) add_compile_options(-Wall -Wextra -Wvla -ffast-math -fno-finite-math-only -ftree-vectorize)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Woverloaded-virtual>)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options(-Wno-inconsistent-missing-override)
endif()
else() else()
# Disable some warnings, so more useful warnings aren't hidden in the noise # Disable some warnings, so more useful warnings aren't hidden in the noise
# 4996 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. # 4996 'fopen': This function or variable may be unsafe. Consider using fopen_s instead.

6
debian/changelog vendored

@ -1,3 +1,9 @@
sdrangel (7.22.6-1) unstable; urgency=medium
* See Github release
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 02 Feb 2025 18:08:11 +0100
sdrangel (7.22.5-1) unstable; urgency=medium sdrangel (7.22.5-1) unstable; urgency=medium
* Windows: upload signed releases rather than unsigned releases to Github releases page. PR #2347 * Windows: upload signed releases rather than unsigned releases to Github releases page. PR #2347

@ -420,6 +420,7 @@ if (NOT FFMPEG_FOUND AND NOT USE_PRECOMPILED_LIBS)
if (NOT X265_FOUND OR X265_EXTERNAL) if (NOT X265_FOUND OR X265_EXTERNAL)
ExternalProject_Add(x265 ExternalProject_Add(x265
GIT_REPOSITORY https://bitbucket.org/multicoreware/x265_git.git GIT_REPOSITORY https://bitbucket.org/multicoreware/x265_git.git
GIT_TAG 4.1
PREFIX "${EXTERNAL_BUILD_LIBRARIES}/x265" PREFIX "${EXTERNAL_BUILD_LIBRARIES}/x265"
SOURCE_SUBDIR "source" SOURCE_SUBDIR "source"
CMAKE_ARGS ${COMMON_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> CMAKE_ARGS ${COMMON_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
@ -858,7 +859,7 @@ if(ENABLE_CHANNELRX_REMOTETCPSINK)
ExternalProject_Add(flac ExternalProject_Add(flac
GIT_REPOSITORY https://github.com/xiph/flac.git GIT_REPOSITORY https://github.com/xiph/flac.git
PREFIX "${EXTERNAL_BUILD_LIBRARIES}/flac" PREFIX "${EXTERNAL_BUILD_LIBRARIES}/flac"
CMAKE_ARGS ${COMMON_CMAKE_ARGS} -DINSTALL_MANPAGES=OFF -D=BUILD_SHARED_LIBS=ON -DWITH_FORTIFY_SOURCE=OFF -DWITH_STACK_PROTECTOR=PFF -DBUILD_PROGRAMS=OFF -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DWITH_OGG=OFF -DBUILD_DOCS=OFF CMAKE_ARGS ${COMMON_CMAKE_ARGS} -DINSTALL_MANPAGES=OFF -DBUILD_SHARED_LIBS=ON -DWITH_FORTIFY_SOURCE=OFF -DWITH_STACK_PROTECTOR=PFF -DBUILD_PROGRAMS=OFF -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DWITH_OGG=OFF -DBUILD_DOCS=OFF
BUILD_BYPRODUCTS "${FLAC_LIBRARIES}" BUILD_BYPRODUCTS "${FLAC_LIBRARIES}"
INSTALL_COMMAND "" INSTALL_COMMAND ""
TEST_COMMAND "" TEST_COMMAND ""

@ -4,6 +4,7 @@ PLUGINS=$(git diff --name-only ${1}..${2} | grep plugins/ | cut -d'/' -f2,3 | so
for plugin in $PLUGINS for plugin in $PLUGINS
do do
FILE=$(find $BASEDIR/plugins/$plugin -name "*plugin.cpp") FILE=$(find $BASEDIR/plugins/$plugin -name "*plugin.cpp")
sed -i -E "s/QStringLiteral\(\"7\.(.*)\"\)/QStringLiteral\(\"7\.22\.5\"\)/" $FILE echo $FILE
sed -i -E "s/QStringLiteral\(\"7\.(.*)\"\)/QStringLiteral\(\"7\.22\.6\"\)/" $FILE
done done

@ -30,7 +30,7 @@
const PluginDescriptor ADSBPlugin::m_pluginDescriptor = { const PluginDescriptor ADSBPlugin::m_pluginDescriptor = {
ADSBDemod::m_channelId, ADSBDemod::m_channelId,
QStringLiteral("ADS-B Demodulator"), QStringLiteral("ADS-B Demodulator"),
QStringLiteral("7.22.5"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -33,7 +33,7 @@
const PluginDescriptor DSCDemodPlugin::m_pluginDescriptor = { const PluginDescriptor DSCDemodPlugin::m_pluginDescriptor = {
DSCDemod::m_channelId, DSCDemod::m_channelId,
QStringLiteral("DSC Demodulator"), QStringLiteral("DSC Demodulator"),
QStringLiteral("7.22.5"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -107,7 +107,7 @@
<cursorShape>PointingHandCursor</cursorShape> <cursorShape>PointingHandCursor</cursorShape>
</property> </property>
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::StrongFocus</enum> <enum>Qt::FocusPolicy::StrongFocus</enum>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Demod shift frequency from center in Hz</string> <string>Demod shift frequency from center in Hz</string>
@ -124,7 +124,7 @@
<item> <item>
<widget class="Line" name="line"> <widget class="Line" name="line">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Orientation::Vertical</enum>
</property> </property>
</widget> </widget>
</item> </item>
@ -186,7 +186,7 @@
<item> <item>
<spacer name="horizontalSpacer_4"> <spacer name="horizontalSpacer_4">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
@ -211,7 +211,7 @@
<string>0000k</string> <string>0000k</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -230,7 +230,7 @@
<string>#000</string> <string>#000</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -265,7 +265,7 @@
<string>999.99M</string> <string>999.99M</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -298,7 +298,7 @@
<number>1</number> <number>1</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Orientation::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
@ -317,14 +317,14 @@
<string>000</string> <string>000</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="Line" name="line_2"> <widget class="Line" name="line_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Orientation::Vertical</enum>
</property> </property>
</widget> </widget>
</item> </item>
@ -368,6 +368,12 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="squelchLevelText"> <widget class="QLabel" name="squelchLevelText">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>Spectrum squelch level (dB)</string> <string>Spectrum squelch level (dB)</string>
</property> </property>
@ -397,6 +403,12 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="preRecordTimeText"> <widget class="QLabel" name="preRecordTimeText">
<property name="minimumSize">
<size>
<width>16</width>
<height>0</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>Squelched recoding pre-recording time (s)</string> <string>Squelched recoding pre-recording time (s)</string>
</property> </property>
@ -426,6 +438,12 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="postSquelchTimeText"> <widget class="QLabel" name="postSquelchTimeText">
<property name="minimumSize">
<size>
<width>16</width>
<height>0</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>Squelched recording post-recording time (s)</string> <string>Squelched recording post-recording time (s)</string>
</property> </property>
@ -506,7 +524,7 @@
<string>...</string> <string>...</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>

@ -33,7 +33,7 @@
const PluginDescriptor FileSinkPlugin::m_pluginDescriptor = { const PluginDescriptor FileSinkPlugin::m_pluginDescriptor = {
FileSink::m_channelId, FileSink::m_channelId,
QStringLiteral("File Sink"), QStringLiteral("File Sink"),
QStringLiteral("7.22.5"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"), QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -35,7 +35,7 @@
const PluginDescriptor RemoteTCPSinkPlugin::m_pluginDescriptor = { const PluginDescriptor RemoteTCPSinkPlugin::m_pluginDescriptor = {
RemoteTCPSink::m_channelId, RemoteTCPSink::m_channelId,
QStringLiteral("Remote TCP channel sink"), QStringLiteral("Remote TCP channel sink"),
QStringLiteral("7.22.5"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -32,7 +32,7 @@
const PluginDescriptor AMModPlugin::m_pluginDescriptor = { const PluginDescriptor AMModPlugin::m_pluginDescriptor = {
AMMod::m_channelId, AMMod::m_channelId,
QStringLiteral("AM Modulator"), QStringLiteral("AM Modulator"),
QStringLiteral("7.22.5"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"), QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -32,7 +32,7 @@
const PluginDescriptor NFMModPlugin::m_pluginDescriptor = { const PluginDescriptor NFMModPlugin::m_pluginDescriptor = {
NFMMod::m_channelId, NFMMod::m_channelId,
QStringLiteral("NFM Modulator"), QStringLiteral("NFM Modulator"),
QStringLiteral("7.22.5"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"), QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -68,6 +68,7 @@ SSBMod::SSBMod(DeviceAPI *deviceAPI) :
m_basebandSource->setSpectrumSink(&m_spectrumVis); m_basebandSource->setSpectrumSink(&m_spectrumVis);
m_basebandSource->setInputFileStream(&m_ifstream); m_basebandSource->setInputFileStream(&m_ifstream);
m_basebandSource->setChannel(this); m_basebandSource->setChannel(this);
m_basebandSource->setCWKeyer(&m_cwKeyer);
m_basebandSource->moveToThread(m_thread); m_basebandSource->moveToThread(m_thread);
applySettings(m_settings, true); applySettings(m_settings, true);

@ -32,7 +32,7 @@
const PluginDescriptor SSBModPlugin::m_pluginDescriptor = { const PluginDescriptor SSBModPlugin::m_pluginDescriptor = {
SSBMod::m_channelId, SSBMod::m_channelId,
QStringLiteral("SSB Modulator"), QStringLiteral("SSB Modulator"),
QStringLiteral("7.22.5"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"), QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -32,7 +32,7 @@
const PluginDescriptor WFMModPlugin::m_pluginDescriptor = { const PluginDescriptor WFMModPlugin::m_pluginDescriptor = {
WFMMod::m_channelId, WFMMod::m_channelId,
QStringLiteral("WFM Modulator"), QStringLiteral("WFM Modulator"),
QStringLiteral("7.22.5"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"), QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -157,6 +157,13 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_sondeHub = SondeHub::create(); m_sondeHub = SondeHub::create();
if (m_sondeHub)
{
connect(m_sondeHub, &SondeHub::prediction, this, &RadiosondeGUI::handlePrediction);
connect(&m_predicitionTimer, &QTimer::timeout, this, &RadiosondeGUI::requestPredictions);
m_predicitionTimer.setInterval(60 * 1000);
m_predicitionTimer.setSingleShot(false);
}
// Initialise chart // Initialise chart
ui->chart->setRenderHint(QPainter::Antialiasing); ui->chart->setRenderHint(QPainter::Antialiasing);
@ -190,6 +197,8 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F
// Get updated when position changes // Get updated when position changes
connect(&MainCore::instance()->getSettings(), &MainSettings::preferenceChanged, this, &RadiosondeGUI::preferenceChanged); connect(&MainCore::instance()->getSettings(), &MainSettings::preferenceChanged, this, &RadiosondeGUI::preferenceChanged);
connect(&m_positionUpdateTimer, &QTimer::timeout, this, &RadiosondeGUI::updatePosition);
m_positionUpdateTimer.setSingleShot(true);
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LATITUDE, new DecimalDelegate(5, ui->radiosondes)); 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_LONGITUDE, new DecimalDelegate(5, ui->radiosondes));
@ -255,12 +264,14 @@ void RadiosondeGUI::displaySettings()
ui->y2->setCurrentIndex((int)m_settings.m_y2); ui->y2->setCurrentIndex((int)m_settings.m_y2);
ui->feed->setChecked(m_settings.m_feedEnabled); ui->feed->setChecked(m_settings.m_feedEnabled);
ui->showPredictedPaths->setChecked(m_settings.m_showPredictedPaths);
getRollupContents()->restoreState(m_rollupState); getRollupContents()->restoreState(m_rollupState);
blockApplySettings(false); blockApplySettings(false);
getRollupContents()->arrangeRollups(); getRollupContents()->arrangeRollups();
updatePosition(); updatePosition();
applyShowPredictedPaths();
} }
void RadiosondeGUI::onMenuDialogCalled(const QPoint &p) void RadiosondeGUI::onMenuDialogCalled(const QPoint &p)
@ -671,6 +682,10 @@ void RadiosondeGUI::updateRadiosondes(RS41Frame *message, QDateTime dateTime)
MainCore::instance()->getSettings().getAltitude() MainCore::instance()->getSettings().getAltitude()
); );
} }
if (!found) {
requestPredictions();
}
} }
void RadiosondeGUI::on_radiosondes_itemSelectionChanged() void RadiosondeGUI::on_radiosondes_itemSelectionChanged()
@ -906,16 +921,38 @@ void RadiosondeGUI::on_deleteAll_clicked()
{ {
QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text(); QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
// Remove from map // Remove from map
sendToMap(serial, "", clearFromMapFeature(serial, 0);
"", "",
"", 0.0f,
0.0f, 0.0f, 0.0f, QDateTime(),
0.0f);
// Remove from table // Remove from table
ui->radiosondes->removeRow(row); ui->radiosondes->removeRow(row);
// Remove from hash and free memory // Remove from hash and free memory
delete m_radiosondes.take(serial); delete m_radiosondes.take(serial);
} }
deletePredictedPaths();
}
void RadiosondeGUI::deletePredictedPaths()
{
for (const auto& prediction : m_predictions) {
clearFromMapFeature(prediction, 3);
}
m_predictions.clear();
}
void RadiosondeGUI::clearFromMapFeature(const QString& name, int type)
{
QList<ObjectPipe*> mapPipes;
MainCore::instance()->getMessagePipes().getMessagePipes(m_radiosonde, "mapitems", mapPipes);
for (const auto& pipe : mapPipes)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
swgMapItem->setName(new QString(name));
swgMapItem->setImage(new QString(""));
swgMapItem->setType(type);
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_radiosonde, swgMapItem);
messageQueue->push(msg);
}
} }
void RadiosondeGUI::makeUIConnections() void RadiosondeGUI::makeUIConnections()
@ -926,6 +963,7 @@ void RadiosondeGUI::makeUIConnections()
QObject::connect(ui->y2, qOverload<int>(&QComboBox::currentIndexChanged), this, &RadiosondeGUI::on_y2_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->deleteAll, &QPushButton::clicked, this, &RadiosondeGUI::on_deleteAll_clicked);
QObject::connect(ui->feed, &ButtonSwitch::clicked, this, &RadiosondeGUI::on_feed_clicked); QObject::connect(ui->feed, &ButtonSwitch::clicked, this, &RadiosondeGUI::on_feed_clicked);
QObject::connect(ui->showPredictedPaths, &ButtonSwitch::clicked, this, &RadiosondeGUI::on_showPredictedPaths_clicked);
} }
void RadiosondeGUI::on_feed_clicked(bool checked) void RadiosondeGUI::on_feed_clicked(bool checked)
@ -954,6 +992,28 @@ void RadiosondeGUI::feedSelect(const QPoint& p)
} }
} }
void RadiosondeGUI::on_showPredictedPaths_clicked(bool checked)
{
m_settings.m_showPredictedPaths = checked;
m_settingsKeys.append("showPredictedPaths");
applySettings();
applyShowPredictedPaths();
}
void RadiosondeGUI::applyShowPredictedPaths()
{
if (m_settings.m_showPredictedPaths)
{
requestPredictions();
m_predicitionTimer.start();
}
else
{
m_predicitionTimer.stop();
deletePredictedPaths();
}
}
// Get names of devices with radiosonde demods, for SondeHub Radio string // Get names of devices with radiosonde demods, for SondeHub Radio string
QStringList RadiosondeGUI::getRadios() QStringList RadiosondeGUI::getRadios()
{ {
@ -963,7 +1023,7 @@ QStringList RadiosondeGUI::getRadios()
for (const auto& channel : channels) for (const auto& channel : channels)
{ {
DeviceAPI *device = mainCore->getDevice(channel.m_index); DeviceAPI *device = mainCore->getDevice(channel.m_superIndex);
if (device) if (device)
{ {
QString name = device->getHardwareId(); QString name = device->getHardwareId();
@ -979,22 +1039,106 @@ QStringList RadiosondeGUI::getRadios()
void RadiosondeGUI::updatePosition() void RadiosondeGUI::updatePosition()
{ {
// Limit number of position updates sent to SondeHub
const int updateTime = m_settings.m_mobile ? m_minMobilePositionUpdateTime : m_minFixedPositionUpdateTime;
if (m_sondeHub && m_settings.m_displayPosition) if (m_sondeHub && m_settings.m_displayPosition)
{ {
float stationLatitude = MainCore::instance()->getSettings().getLatitude(); if (!m_lastPositionUpdate.isValid() || (m_lastPositionUpdate.secsTo(QDateTime::currentDateTime()) >= updateTime))
float stationLongitude = MainCore::instance()->getSettings().getLongitude(); {
float stationAltitude = MainCore::instance()->getSettings().getAltitude(); float stationLatitude = MainCore::instance()->getSettings().getLatitude();
float stationLongitude = MainCore::instance()->getSettings().getLongitude();
float stationAltitude = MainCore::instance()->getSettings().getAltitude();
m_sondeHub->updatePosition( m_sondeHub->updatePosition(
m_settings.m_callsign, m_settings.m_callsign,
stationLatitude, stationLatitude,
stationLongitude, stationLongitude,
stationAltitude, stationAltitude,
getRadios().join(" "), getRadios().join(" "),
m_settings.m_antenna, m_settings.m_antenna,
m_settings.m_email, m_settings.m_email,
m_settings.m_mobile m_settings.m_mobile
); );
m_positionUpdateTimer.stop();
m_lastPositionUpdate = QDateTime::currentDateTime();
}
else
{
qint64 msecs = (updateTime * 1000) - m_lastPositionUpdate.msecsTo(QDateTime::currentDateTime());
if (msecs < 0) {
msecs = 0;
}
m_positionUpdateTimer.setInterval(msecs);
m_positionUpdateTimer.start();
}
}
}
void RadiosondeGUI::requestPredictions()
{
if (m_sondeHub && m_settings.m_showPredictedPaths)
{
for (int row = 0; row < ui->radiosondes->rowCount(); row++)
{
QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
m_sondeHub->getPrediction(serial);
}
}
}
void RadiosondeGUI::handlePrediction(const QString& serial, const QList<SondeHub::Position>& positions)
{
if (positions.size() < 2) {
return;
}
// Send to Map feature
QList<ObjectPipe*> mapPipes;
MainCore::instance()->getMessagePipes().getMessagePipes(m_radiosonde, "mapitems", mapPipes);
if (mapPipes.size() > 0)
{
QString name = QString("%1_prediction").arg(serial);
for (const auto& pipe : mapPipes)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
swgMapItem->setName(new QString(name));
swgMapItem->setLatitude(positions[0].m_latitude);
swgMapItem->setLongitude(positions[0].m_longitude);
swgMapItem->setAltitude(positions[0].m_altitude);
QString image = QString("none");
swgMapItem->setImage(new QString(image));
swgMapItem->setImageRotation(0);
swgMapItem->setFixedPosition(true);
swgMapItem->setLabel(new QString(serial));
swgMapItem->setAltitudeReference(0);
QList<SWGSDRangel::SWGMapCoordinate *> *coords = new QList<SWGSDRangel::SWGMapCoordinate *>();
for (const auto& position : positions)
{
SWGSDRangel::SWGMapCoordinate* c = new SWGSDRangel::SWGMapCoordinate();
c->setLatitude(position.m_latitude);
c->setLongitude(position.m_longitude);
c->setAltitude(position.m_altitude);
coords->append(c);
}
swgMapItem->setCoordinates(coords);
swgMapItem->setType(3);
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_radiosonde, swgMapItem);
messageQueue->push(msg);
if (!m_predictions.contains(name)) {
m_predictions.append(name);
}
}
} }
} }

@ -103,6 +103,13 @@ private:
QMenu *radiosondesMenu; // Column select context menu QMenu *radiosondesMenu; // Column select context menu
SondeHub *m_sondeHub; SondeHub *m_sondeHub;
QDateTime m_lastPositionUpdate;
QTimer m_positionUpdateTimer;
static const int m_minMobilePositionUpdateTime = 30; // In seconds
static const int m_minFixedPositionUpdateTime = 5 * 60;
QTimer m_predicitionTimer;
QStringList m_predictions;
explicit RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); explicit RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
virtual ~RadiosondeGUI(); virtual ~RadiosondeGUI();
@ -126,6 +133,9 @@ private:
float getData(RadiosondeSettings::ChartData dataType, RadiosondeData *radiosonde, RS41Frame *message); float getData(RadiosondeSettings::ChartData dataType, RadiosondeData *radiosonde, RS41Frame *message);
void updatePosition(); void updatePosition();
QStringList getRadios(); QStringList getRadios();
void applyShowPredictedPaths();
void deletePredictedPaths();
void clearFromMapFeature(const QString& name, int type);
enum RadiosondeCol { enum RadiosondeCol {
RADIOSONDE_COL_SERIAL, RADIOSONDE_COL_SERIAL,
@ -164,6 +174,9 @@ private slots:
void on_deleteAll_clicked(); void on_deleteAll_clicked();
void on_feed_clicked(bool checked); void on_feed_clicked(bool checked);
void feedSelect(const QPoint& p); void feedSelect(const QPoint& p);
void on_showPredictedPaths_clicked(bool checked);
void requestPredictions();
void handlePrediction(const QString& serial, const QList<SondeHub::Position>& positions);
void preferenceChanged(int elementType); void preferenceChanged(int elementType);
}; };

@ -31,7 +31,7 @@
<string>Radiosonde</string> <string>Radiosonde</string>
</property> </property>
<property name="layoutDirection"> <property name="layoutDirection">
<enum>Qt::LeftToRight</enum> <enum>Qt::LayoutDirection::LeftToRight</enum>
</property> </property>
<widget class="QWidget" name="tableContainer" native="true"> <widget class="QWidget" name="tableContainer" native="true">
<property name="geometry"> <property name="geometry">
@ -76,20 +76,20 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Orientation::Vertical</enum>
</property> </property>
<widget class="QTableWidget" name="radiosondes"> <widget class="QTableWidget" name="radiosondes">
<property name="toolTip"> <property name="toolTip">
<string>Radiosondes</string> <string>Radiosondes</string>
</property> </property>
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum> <enum>QAbstractItemView::SelectionMode::SingleSelection</enum>
</property> </property>
<property name="selectionBehavior"> <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property> </property>
<column> <column>
<property name="text"> <property name="text">
@ -416,6 +416,23 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="ButtonSwitch" name="showPredictedPaths">
<property name="toolTip">
<string>Show predicted paths on map</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/logarithmic.png</normaloff>:/logarithmic.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>

@ -33,7 +33,7 @@
const PluginDescriptor RadiosondePlugin::m_pluginDescriptor = { const PluginDescriptor RadiosondePlugin::m_pluginDescriptor = {
Radiosonde::m_featureId, Radiosonde::m_featureId,
QStringLiteral("Radiosonde"), QStringLiteral("Radiosonde"),
QStringLiteral("7.22.1"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -60,6 +60,7 @@ void RadiosondeSettings::resetToDefaults()
m_displayPosition = false; m_displayPosition = false;
m_mobile = false; m_mobile = false;
m_email = ""; m_email = "";
m_showPredictedPaths = false;
for (int i = 0; i < RADIOSONDES_COLUMNS; i++) for (int i = 0; i < RADIOSONDES_COLUMNS; i++)
{ {
@ -95,6 +96,7 @@ QByteArray RadiosondeSettings::serialize() const
s.writeBool(17, m_displayPosition); s.writeBool(17, m_displayPosition);
s.writeBool(18, m_mobile); s.writeBool(18, m_mobile);
s.writeString(19, m_email); s.writeString(19, m_email);
s.writeBool(20, m_showPredictedPaths);
for (int i = 0; i < RADIOSONDES_COLUMNS; i++) { for (int i = 0; i < RADIOSONDES_COLUMNS; i++) {
@ -159,6 +161,7 @@ bool RadiosondeSettings::deserialize(const QByteArray& data)
d.readBool(17, &m_displayPosition, false); d.readBool(17, &m_displayPosition, false);
d.readBool(18, &m_mobile, false); d.readBool(18, &m_mobile, false);
d.readString(19, &m_email, ""); d.readString(19, &m_email, "");
d.readBool(20, &m_showPredictedPaths, false);
for (int i = 0; i < RADIOSONDES_COLUMNS; i++) { for (int i = 0; i < RADIOSONDES_COLUMNS; i++) {
d.readS32(300 + i, &m_radiosondesColumnIndexes[i], i); d.readS32(300 + i, &m_radiosondesColumnIndexes[i], i);
@ -224,6 +227,9 @@ void RadiosondeSettings::applySettings(const QStringList& settingsKeys, const Ra
if (settingsKeys.contains("email")) { if (settingsKeys.contains("email")) {
m_email = settings.m_email; m_email = settings.m_email;
} }
if (settingsKeys.contains("showPredictedPaths")) {
m_showPredictedPaths = settings.m_showPredictedPaths;
}
if (settingsKeys.contains("workspaceIndex")) { if (settingsKeys.contains("workspaceIndex")) {
m_workspaceIndex = settings.m_workspaceIndex; m_workspaceIndex = settings.m_workspaceIndex;
} }
@ -292,6 +298,9 @@ QString RadiosondeSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("email") || force) { if (settingsKeys.contains("email") || force) {
ostr << " m_email: " << m_email.toStdString(); ostr << " m_email: " << m_email.toStdString();
} }
if (settingsKeys.contains("showPredictedPaths") || force) {
ostr << " m_showPredictedPaths: " << m_showPredictedPaths;
}
if (settingsKeys.contains("workspaceIndex") || force) { if (settingsKeys.contains("workspaceIndex") || force) {
ostr << " m_workspaceIndex: " << m_workspaceIndex; ostr << " m_workspaceIndex: " << m_workspaceIndex;
} }

@ -62,6 +62,7 @@ struct RadiosondeSettings
bool m_displayPosition; bool m_displayPosition;
bool m_mobile; bool m_mobile;
QString m_email; QString m_email;
bool m_showPredictedPaths;
int m_radiosondesColumnIndexes[RADIOSONDES_COLUMNS]; int m_radiosondesColumnIndexes[RADIOSONDES_COLUMNS];
int m_radiosondesColumnSizes[RADIOSONDES_COLUMNS]; int m_radiosondesColumnSizes[RADIOSONDES_COLUMNS];

@ -7,7 +7,7 @@ based on data received via [Radiosonde Demodulators](../../channelrx/demodradios
The chart can plot two data series vs time for the radiosonde selected in the table. The chart can plot two data series vs time for the radiosonde selected in the table.
The Radiosonde feature can draw balloons objects on the [Map](../../feature/map/readme.md) feature in 2D and 3D. The Radiosonde feature can draw balloons objects and predicted paths 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. 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.
@ -48,6 +48,7 @@ The Radiosonde feature can plot balloons (during ascent) and parachutes (during
To use, simply open a Map feature and the Radiosonde plugin will display objects based upon the data it receives from that point. To use, simply open a Map feature and the Radiosonde plugin will display objects based upon the data it receives from that point.
Selecting a radiosonde item on the map will display a text bubble containing information from the above table. Selecting a radiosonde item on the map will display a text bubble containing information from the above table.
To centre the map on an item in the table, double click in the Lat or Lon columns. To centre the map on an item in the table, double click in the Lat or Lon columns.
Predicted paths can be displayed by checking the Show Predicted Paths button. The path is predicted by SondeHub.
![Radiosonde on map](../../../doc/img/Radiosonde_plugin_map.png) ![Radiosonde on map](../../../doc/img/Radiosonde_plugin_map.png)

@ -29,7 +29,7 @@
const PluginDescriptor SIDPlugin::m_pluginDescriptor = { const PluginDescriptor SIDPlugin::m_pluginDescriptor = {
SIDMain::m_featureId, SIDMain::m_featureId,
QStringLiteral("SID"), QStringLiteral("SID"),
QStringLiteral("7.22.1"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -36,7 +36,7 @@
const PluginDescriptor USRPOutputPlugin::m_pluginDescriptor = { const PluginDescriptor USRPOutputPlugin::m_pluginDescriptor = {
QStringLiteral("USRP"), QStringLiteral("USRP"),
QStringLiteral("URSP Output"), QStringLiteral("URSP Output"),
QStringLiteral("7.22.3"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Jon Beniston, M7RCE and Edouard Griffiths, F4EXB"), QStringLiteral("(c) Jon Beniston, M7RCE and Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -36,7 +36,7 @@
const PluginDescriptor USRPInputPlugin::m_pluginDescriptor = { const PluginDescriptor USRPInputPlugin::m_pluginDescriptor = {
QStringLiteral("USRP"), QStringLiteral("USRP"),
QStringLiteral("USRP Input"), QStringLiteral("USRP Input"),
QStringLiteral("7.22.1"), QStringLiteral("7.22.6"),
QStringLiteral("(c) Jon Beniston, M7RCE and Edouard Griffiths, F4EXB"), QStringLiteral("(c) Jon Beniston, M7RCE and Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"), QStringLiteral("https://github.com/f4exb/sdrangel"),
true, true,

@ -20,12 +20,42 @@
#include "SWGFeatureActions.h" #include "SWGFeatureActions.h"
#include "SWGMapActions.h" #include "SWGMapActions.h"
#include "SWGPERTesterActions.h" #include "SWGPERTesterActions.h"
#include "SWGDeviceState.h"
#include "maincore.h" #include "maincore.h"
#include "feature/featureset.h" #include "feature/featureset.h"
#include "feature/feature.h" #include "feature/feature.h"
#include "featurewebapiutils.h" #include "featurewebapiutils.h"
// Start feature
bool FeatureWebAPIUtils::run(int featureSetIndex, int featureIndex)
{
Feature *feature = FeatureWebAPIUtils::getFeature(featureSetIndex, featureIndex, "");
if (feature != nullptr)
{
SWGSDRangel::SWGDeviceState runResponse;
QString errorResponse;
int httpRC;
runResponse.setState(new QString());
httpRC = feature->webapiRun(true, runResponse, errorResponse);
if (httpRC/100 != 2)
{
qWarning("FeatureWebAPIUtils::run: run error %d: %s",
httpRC, qPrintable(errorResponse));
return false;
}
return true;
}
else
{
qWarning("FeatureWebAPIUtils::run: no feature F%d:%d", featureSetIndex, featureIndex);
return false;
}
}
// Find the specified target on the map // Find the specified target on the map
bool FeatureWebAPIUtils::mapFind(const QString& target, int featureSetIndex, int featureIndex) bool FeatureWebAPIUtils::mapFind(const QString& target, int featureSetIndex, int featureIndex)
{ {

@ -47,6 +47,7 @@ private slots:
class SDRBASE_API FeatureWebAPIUtils class SDRBASE_API FeatureWebAPIUtils
{ {
public: public:
static bool run(int featureSetIndex, int featureIndex);
static bool mapFind(const QString& target, int featureSetIndex=-1, int featureIndex=-1); static bool mapFind(const QString& target, int featureSetIndex=-1, int featureIndex=-1);
static bool mapSetDateTime(const QDateTime& dateTime, int featureSetIndex=-1, int featureIndex=-1); static bool mapSetDateTime(const QDateTime& dateTime, int featureSetIndex=-1, int featureIndex=-1);
static bool skyMapFind(const QString& target, int featureSetIndex=-1, int featureIndex=-1); static bool skyMapFind(const QString& target, int featureSetIndex=-1, int featureIndex=-1);

@ -42,7 +42,8 @@ MainParser::MainParser() :
m_remoteTCPSinkPortOption("remote-tcp-port", "Remote TCP Sink port (Default 1234).", "port", "1234"), m_remoteTCPSinkPortOption("remote-tcp-port", "Remote TCP Sink port (Default 1234).", "port", "1234"),
m_remoteTCPSinkHWTypeOption("remote-tcp-hwtype", "Remote TCP Sink device hardware type (Optional. E.g. RTLSDR/SDRplayV3/AirspyHF).", "hwtype"), m_remoteTCPSinkHWTypeOption("remote-tcp-hwtype", "Remote TCP Sink device hardware type (Optional. E.g. RTLSDR/SDRplayV3/AirspyHF).", "hwtype"),
m_remoteTCPSinkSerialOption("remote-tcp-serial", "Remote TCP Sink device serial (Optional).", "serial"), m_remoteTCPSinkSerialOption("remote-tcp-serial", "Remote TCP Sink device serial (Optional).", "serial"),
m_listDevicesOption("list-devices", "List available physical devices.") m_listDevicesOption("list-devices", "List available physical devices."),
m_startOption("start", "Start all devices and features")
{ {
m_serverAddress = ""; // Bind to any address m_serverAddress = ""; // Bind to any address
@ -56,6 +57,7 @@ MainParser::MainParser() :
m_remoteTCPSinkHWType = ""; m_remoteTCPSinkHWType = "";
m_remoteTCPSinkSerial = ""; m_remoteTCPSinkSerial = "";
m_listDevices = false; m_listDevices = false;
m_start = false;
m_parser.setApplicationDescription("Software Defined Radio application"); m_parser.setApplicationDescription("Software Defined Radio application");
m_parser.addHelpOption(); m_parser.addHelpOption();
@ -72,6 +74,7 @@ MainParser::MainParser() :
m_parser.addOption(m_remoteTCPSinkHWTypeOption); m_parser.addOption(m_remoteTCPSinkHWTypeOption);
m_parser.addOption(m_remoteTCPSinkSerialOption); m_parser.addOption(m_remoteTCPSinkSerialOption);
m_parser.addOption(m_listDevicesOption); m_parser.addOption(m_listDevicesOption);
m_parser.addOption(m_startOption);
} }
MainParser::~MainParser() MainParser::~MainParser()
@ -154,4 +157,8 @@ void MainParser::parse(const QCoreApplication& app)
qCritical() << "You must specify a device with either --remote-tcp-hwtype or --remote-tcp-serial"; qCritical() << "You must specify a device with either --remote-tcp-hwtype or --remote-tcp-serial";
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
} }
// Start devices and features
m_start = m_parser.isSet(m_startOption);
} }

@ -45,6 +45,7 @@ public:
const QString& getRemoteTCPSinkHWType() const { return m_remoteTCPSinkHWType; } const QString& getRemoteTCPSinkHWType() const { return m_remoteTCPSinkHWType; }
const QString& getRemoteTCPSinkSerial() const { return m_remoteTCPSinkSerial; } const QString& getRemoteTCPSinkSerial() const { return m_remoteTCPSinkSerial; }
bool getListDevices() const { return m_listDevices; } bool getListDevices() const { return m_listDevices; }
bool getStart() const { return m_start; }
private: private:
QString m_serverAddress; QString m_serverAddress;
@ -58,6 +59,7 @@ private:
QString m_remoteTCPSinkHWType; QString m_remoteTCPSinkHWType;
QString m_remoteTCPSinkSerial; QString m_remoteTCPSinkSerial;
bool m_listDevices; bool m_listDevices;
bool m_start;
QCommandLineParser m_parser; QCommandLineParser m_parser;
QCommandLineOption m_serverAddressOption; QCommandLineOption m_serverAddressOption;
@ -71,6 +73,7 @@ private:
QCommandLineOption m_remoteTCPSinkHWTypeOption; QCommandLineOption m_remoteTCPSinkHWTypeOption;
QCommandLineOption m_remoteTCPSinkSerialOption; QCommandLineOption m_remoteTCPSinkSerialOption;
QCommandLineOption m_listDevicesOption; QCommandLineOption m_listDevicesOption;
QCommandLineOption m_startOption;
}; };

@ -76,6 +76,12 @@ QString RS41Frame::toHex()
return m_bytes.toHex(); return m_bytes.toHex();
} }
int16_t RS41Frame::getInt16(const QByteArray ba, int offset) const
{
return (ba[offset] & 0xff)
| ((ba[offset+1] & 0xff) << 8);
}
uint16_t RS41Frame::getUInt16(const QByteArray ba, int offset) const uint16_t RS41Frame::getUInt16(const QByteArray ba, int offset) const
{ {
return (ba[offset] & 0xff) return (ba[offset] & 0xff)
@ -130,7 +136,7 @@ void RS41Frame::decodeMeas(const QByteArray ba)
m_pressureMain = getUInt24(ba, 0x1b); m_pressureMain = getUInt24(ba, 0x1b);
m_pressureRef1 = getUInt24(ba, 0x1e); m_pressureRef1 = getUInt24(ba, 0x1e);
m_pressureRef2 = getUInt24(ba, 0x21); m_pressureRef2 = getUInt24(ba, 0x21);
m_pressureTemp = getUInt16(ba, 0x26) / 100.0f; m_pressureTemp = getInt16(ba, 0x26) / 100.0f;
} }
void RS41Frame::decodeGPSInfo(const QByteArray ba) void RS41Frame::decodeGPSInfo(const QByteArray ba)
@ -187,7 +193,7 @@ static float waterVapourSaturationPressure(float tCelsius)
return p / 100.0f; return p / 100.0f;
} }
static 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, const float *poly, const float *cal)
{ {
/*float g = (float)(f2-f1) / (r2-r1); // gain /*float g = (float)(f2-f1) / (r2-r1); // gain
float Rb = (f1*r2-f2*r1) / (float)(f2-f1); // offset float Rb = (f1*r2-f2*r1) / (float)(f2-f1); // offset
@ -219,7 +225,7 @@ static float calcT(int f, int f1, int f2, float r1, float r2, float *poly, float
return tCal; return tCal;
} }
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) static float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, float HT, const float *capCal, const float *matrixCal, float height, const float *vectorPCal, const 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] << "height" << height; //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;
@ -302,7 +308,7 @@ static float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, fl
return uCal; return uCal;
} }
static float calcP(int f, int f1, int f2, float pressureTemp, float *cal) static float calcP(int f, int f1, int f2, float pressureTemp, const float *cal)
{ {
// Convert integer measurement to scale factor // Convert integer measurement to scale factor
float s = (f-f1) / (float)(f2-f1); float s = (f-f1) / (float)(f2-f1);

@ -124,6 +124,7 @@ public:
static int getFrameLength(int frameType); static int getFrameLength(int frameType);
protected: protected:
int16_t getInt16(const QByteArray ba, int offset) const;
uint16_t getUInt16(const QByteArray ba, int offset) const; uint16_t getUInt16(const QByteArray ba, int offset) const;
uint32_t getUInt24(const QByteArray ba, int offset) const; uint32_t getUInt24(const QByteArray ba, int offset) const;
uint32_t getUInt32(const QByteArray ba, int offset) const; uint32_t getUInt32(const QByteArray ba, int offset) const;

@ -126,6 +126,7 @@ void SondeHub::upload(
obj.insert("subtype", subframe->getType()); obj.insert("subtype", subframe->getType());
} }
//obj.insert("dev", true);
//qDebug() << obj; //qDebug() << obj;
QJsonArray payloads { QJsonArray payloads {
obj obj
@ -182,6 +183,17 @@ void SondeHub::updatePosition(
m_networkManager->put(request, data); m_networkManager->put(request, data);
} }
void SondeHub::getPrediction(const QString& serial)
{
QUrl url(QString("https://api.v2.sondehub.org/predictions?vehicles=%1").arg(serial));
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setHeader(QNetworkRequest::UserAgentHeader, "sdrangel");
m_networkManager->get(request);
}
void SondeHub::handleReply(QNetworkReply* reply) void SondeHub::handleReply(QNetworkReply* reply)
{ {
if (reply) if (reply)
@ -189,7 +201,89 @@ void SondeHub::handleReply(QNetworkReply* reply)
if (!reply->error()) if (!reply->error())
{ {
QByteArray bytes = reply->readAll(); QByteArray bytes = reply->readAll();
//qDebug() << bytes; QJsonDocument document = QJsonDocument::fromJson(bytes);
if (document.isObject())
{
QJsonObject obj = document.object();
if (obj.contains(QStringLiteral("message")))
{
QString message = obj.value(QStringLiteral("message")).toString();
qWarning() << "SondeHub message:" << message;
}
if (obj.contains(QStringLiteral("errors")))
{
QJsonArray errors = obj.value(QStringLiteral("errors")).toArray();
for (auto errorObjRef : errors)
{
QJsonObject errorObj = errorObjRef.toObject();
if (errorObj.contains(QStringLiteral("error_message")))
{
QString errorMessage = errorObj.value(QStringLiteral("error_message")).toString();
qWarning() << "SondeHub error:" << errorMessage;
if (errorObj.contains(QStringLiteral("payload")))
{
QJsonObject payload = errorObj.value(QStringLiteral("payload")).toObject();
qWarning() << "SondeHub error:" << QJsonDocument(payload);
}
}
else
{
qWarning() << "SondeHub error:" << QJsonDocument(errorObj);
}
}
}
//qDebug() << "SondeHub::handleReply: obj" << QJsonDocument(obj);
}
else if (document.isArray())
{
QJsonArray array = document.array();
for (auto arrayRef : array)
{
if (arrayRef.isObject())
{
QJsonObject obj = arrayRef.toObject();
if (obj.contains(QStringLiteral("vehicle")) && obj.contains(QStringLiteral("data")))
{
QJsonArray data;
// Perhaps a bug that data is a string rather than an array?
if (obj.value(QStringLiteral("data")).isString())
{
QJsonDocument dataDocument = QJsonDocument::fromJson(obj.value(QStringLiteral("data")).toString().toUtf8());
data = dataDocument.array();
}
else
{
data = obj.value(QStringLiteral("data")).toArray();
}
QList<Position> positions;
for (auto dataObjRef : data)
{
QJsonObject positionObj = dataObjRef.toObject();
Position position;
position.m_dateTime = QDateTime::fromSecsSinceEpoch(positionObj.value(QStringLiteral("time")).toInt());
position.m_latitude = positionObj.value(QStringLiteral("lat")).toDouble();
position.m_longitude = positionObj.value(QStringLiteral("lon")).toDouble();
position.m_altitude = positionObj.value(QStringLiteral("alt")).toDouble();
positions.append(position);
}
emit prediction(obj.value("vehicle").toString(), positions);
}
}
else
{
qDebug() << "SondeHub::handleReply:" << bytes;
}
}
}
else
{
qDebug() << "SondeHub::handleReply:" << bytes;
}
} }
else else
{ {

@ -36,6 +36,13 @@ protected:
public: public:
struct Position {
float m_latitude;
float m_longitude;
float m_altitude;
QDateTime m_dateTime;
};
static SondeHub* create(); static SondeHub* create();
~SondeHub(); ~SondeHub();
@ -61,10 +68,14 @@ public:
bool mobile bool mobile
); );
void getPrediction(const QString& serial);
private slots: private slots:
void handleReply(QNetworkReply* reply); void handleReply(QNetworkReply* reply);
signals:
void prediction(const QString& serial, const QList<Position>& path);
private: private:
QNetworkAccessManager *m_networkManager; QNetworkAccessManager *m_networkManager;

@ -61,6 +61,7 @@
#include "feature/featureset.h" #include "feature/featureset.h"
#include "feature/feature.h" #include "feature/feature.h"
#include "feature/featuregui.h" #include "feature/featuregui.h"
#include "feature/featurewebapiutils.h"
#include "mainspectrum/mainspectrumgui.h" #include "mainspectrum/mainspectrumgui.h"
#include "commands/commandkeyreceiver.h" #include "commands/commandkeyreceiver.h"
#include "gui/presetitem.h" #include "gui/presetitem.h"
@ -275,6 +276,9 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse
InitFSM *fsm = new InitFSM(this, splash, !parser.getScratch()); InitFSM *fsm = new InitFSM(this, splash, !parser.getScratch());
connect(fsm, &InitFSM::finished, fsm, &InitFSM::deleteLater); connect(fsm, &InitFSM::finished, fsm, &InitFSM::deleteLater);
connect(fsm, &InitFSM::finished, splash, &SDRangelSplash::deleteLater); connect(fsm, &InitFSM::finished, splash, &SDRangelSplash::deleteLater);
if (parser.getStart()) {
connect(fsm, &InitFSM::finished, this, &MainWindow::startAllAfterDelay);
}
fsm->start(); fsm->start();
qDebug() << "MainWindow::MainWindow: end"; qDebug() << "MainWindow::MainWindow: end";
@ -3387,6 +3391,30 @@ void MainWindow::showAllChannels(int deviceSetIndex)
} }
} }
void MainWindow::startAllAfterDelay()
{
// Wait a little bit before starting all devices and features,
// as some devices, such as FileSinks, can fail to start if run before initialised
// Is there a better way than this arbitrary time?
QTimer::singleShot(1000, this, &MainWindow::startAll);
}
// Start all devices and features in all workspaces
void MainWindow::startAll()
{
// Start all devices
for (const auto& workspace : m_workspaces) {
startAllDevices(workspace);
}
// Start all features
for (int featureSetIndex = 0; featureSetIndex < m_featureUIs.size(); featureSetIndex++)
{
for (int featureIndex = 0; featureIndex < m_featureUIs[featureSetIndex]->getNumberOfFeatures(); featureIndex++) {
FeatureWebAPIUtils::run(featureSetIndex, featureIndex);
}
}
}
// Start all devices in the workspace // Start all devices in the workspace
void MainWindow::startAllDevices(const Workspace *workspace) const void MainWindow::startAllDevices(const Workspace *workspace) const
{ {

@ -458,6 +458,8 @@ private slots:
void featureMove(FeatureGUI *gui, int wsIndexDestnation); void featureMove(FeatureGUI *gui, int wsIndexDestnation);
void deviceStateChanged(DeviceAPI *deviceAPI); void deviceStateChanged(DeviceAPI *deviceAPI);
void openFeaturePresetsDialog(QPoint p, Workspace *workspace); void openFeaturePresetsDialog(QPoint p, Workspace *workspace);
void startAllAfterDelay();
void startAll();
void startAllDevices(const Workspace *workspace) const; void startAllDevices(const Workspace *workspace) const;
void stopAllDevices(const Workspace *workspace) const; void stopAllDevices(const Workspace *workspace) const;
void deviceMove(DeviceGUI *gui, int wsIndexDestnation); void deviceMove(DeviceGUI *gui, int wsIndexDestnation);

@ -564,7 +564,7 @@ parts:
plugin: cmake plugin: cmake
source: https://github.com/EttusResearch/uhd.git source: https://github.com/EttusResearch/uhd.git
source-type: git source-type: git
source-commit: v4.5.0.0 source-commit: v4.7.0.0
source-subdir: host source-subdir: host
build-packages: build-packages:
- libusb-1.0-0-dev - libusb-1.0-0-dev

@ -444,6 +444,7 @@ EMNR::G::G(
std::copy(Calculus::GG.begin(), Calculus::GG.end(), GG.begin()); std::copy(Calculus::GG.begin(), Calculus::GG.end(), GG.begin());
std::copy(Calculus::GGS.begin(), Calculus::GGS.end(), GGS.begin()); std::copy(Calculus::GGS.begin(), Calculus::GGS.end(), GGS.begin());
/* Removed as redundant and causes a stack overflow on Mac - see #2324
// We keep this pretty useless part just in case... // We keep this pretty useless part just in case...
if ((fileb = fopen("calculus", "rb"))) if ((fileb = fopen("calculus", "rb")))
{ {
@ -462,7 +463,7 @@ EMNR::G::G(
std::copy(ggs.begin(), ggs.end(), GGS.begin()); std::copy(ggs.begin(), ggs.end(), GGS.begin());
} }
fclose(fileb); fclose(fileb);
} }*/
} }
void EMNR::G::calc_gamma0() void EMNR::G::calc_gamma0()