1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-04-09 13:08:58 -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

View File

@ -9,12 +9,18 @@ on:
jobs:
build_mac_x64:
runs-on: macos-12
runs-on: macos-13
env:
MACOSX_DEPLOYMENT_TARGET: 12.0
steps:
- uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Update brew
run: brew update
- name: Install brew packages
run: brew install nasm subversion
- name: Install SDRplay API
run: |
wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.15.0.pkg
@ -40,7 +46,23 @@ jobs:
- name: Build SDRangel on Mac
run: |
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
id: get_version
run: |
@ -99,7 +121,23 @@ jobs:
- name: Build SDRangel on Mac
run: |
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
id: get_version
run: |

View File

@ -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
* Windows: upload signed releases rather than unsigned releases to Github releases page. PR #2347

View File

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

View File

@ -19,7 +19,11 @@ if(WIN32)
endif()
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()
# 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.

6
debian/changelog vendored
View File

@ -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
* Windows: upload signed releases rather than unsigned releases to Github releases page. PR #2347

View File

@ -420,6 +420,7 @@ if (NOT FFMPEG_FOUND AND NOT USE_PRECOMPILED_LIBS)
if (NOT X265_FOUND OR X265_EXTERNAL)
ExternalProject_Add(x265
GIT_REPOSITORY https://bitbucket.org/multicoreware/x265_git.git
GIT_TAG 4.1
PREFIX "${EXTERNAL_BUILD_LIBRARIES}/x265"
SOURCE_SUBDIR "source"
CMAKE_ARGS ${COMMON_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
@ -858,7 +859,7 @@ if(ENABLE_CHANNELRX_REMOTETCPSINK)
ExternalProject_Add(flac
GIT_REPOSITORY https://github.com/xiph/flac.git
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}"
INSTALL_COMMAND ""
TEST_COMMAND ""

View File

@ -4,6 +4,7 @@ PLUGINS=$(git diff --name-only ${1}..${2} | grep plugins/ | cut -d'/' -f2,3 | so
for plugin in $PLUGINS
do
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -157,6 +157,13 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
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
ui->chart->setRenderHint(QPainter::Antialiasing);
@ -190,6 +197,8 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F
// Get updated when position changes
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_LONGITUDE, new DecimalDelegate(5, ui->radiosondes));
@ -255,12 +264,14 @@ void RadiosondeGUI::displaySettings()
ui->y2->setCurrentIndex((int)m_settings.m_y2);
ui->feed->setChecked(m_settings.m_feedEnabled);
ui->showPredictedPaths->setChecked(m_settings.m_showPredictedPaths);
getRollupContents()->restoreState(m_rollupState);
blockApplySettings(false);
getRollupContents()->arrangeRollups();
updatePosition();
applyShowPredictedPaths();
}
void RadiosondeGUI::onMenuDialogCalled(const QPoint &p)
@ -671,6 +682,10 @@ void RadiosondeGUI::updateRadiosondes(RS41Frame *message, QDateTime dateTime)
MainCore::instance()->getSettings().getAltitude()
);
}
if (!found) {
requestPredictions();
}
}
void RadiosondeGUI::on_radiosondes_itemSelectionChanged()
@ -906,16 +921,38 @@ void RadiosondeGUI::on_deleteAll_clicked()
{
QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
// Remove from map
sendToMap(serial, "",
"", "",
"", 0.0f,
0.0f, 0.0f, 0.0f, QDateTime(),
0.0f);
clearFromMapFeature(serial, 0);
// Remove from table
ui->radiosondes->removeRow(row);
// Remove from hash and free memory
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()
@ -926,6 +963,7 @@ void RadiosondeGUI::makeUIConnections()
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);
QObject::connect(ui->showPredictedPaths, &ButtonSwitch::clicked, this, &RadiosondeGUI::on_showPredictedPaths_clicked);
}
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
QStringList RadiosondeGUI::getRadios()
{
@ -963,7 +1023,7 @@ QStringList RadiosondeGUI::getRadios()
for (const auto& channel : channels)
{
DeviceAPI *device = mainCore->getDevice(channel.m_index);
DeviceAPI *device = mainCore->getDevice(channel.m_superIndex);
if (device)
{
QString name = device->getHardwareId();
@ -979,22 +1039,106 @@ QStringList RadiosondeGUI::getRadios()
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)
{
float stationLatitude = MainCore::instance()->getSettings().getLatitude();
float stationLongitude = MainCore::instance()->getSettings().getLongitude();
float stationAltitude = MainCore::instance()->getSettings().getAltitude();
if (!m_lastPositionUpdate.isValid() || (m_lastPositionUpdate.secsTo(QDateTime::currentDateTime()) >= updateTime))
{
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
);
m_sondeHub->updatePosition(
m_settings.m_callsign,
stationLatitude,
stationLongitude,
stationAltitude,
getRadios().join(" "),
m_settings.m_antenna,
m_settings.m_email,
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);
}
}
}
}

View File

@ -103,6 +103,13 @@ private:
QMenu *radiosondesMenu; // Column select context menu
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);
virtual ~RadiosondeGUI();
@ -126,6 +133,9 @@ private:
float getData(RadiosondeSettings::ChartData dataType, RadiosondeData *radiosonde, RS41Frame *message);
void updatePosition();
QStringList getRadios();
void applyShowPredictedPaths();
void deletePredictedPaths();
void clearFromMapFeature(const QString& name, int type);
enum RadiosondeCol {
RADIOSONDE_COL_SERIAL,
@ -164,6 +174,9 @@ private slots:
void on_deleteAll_clicked();
void on_feed_clicked(bool checked);
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);
};

View File

@ -31,7 +31,7 @@
<string>Radiosonde</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<widget class="QWidget" name="tableContainer" native="true">
<property name="geometry">
@ -76,20 +76,20 @@
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<widget class="QTableWidget" name="radiosondes">
<property name="toolTip">
<string>Radiosondes</string>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
<enum>QAbstractItemView::SelectionMode::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property>
<column>
<property name="text">
@ -416,6 +416,23 @@
</property>
</widget>
</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>
</item>
<item>

View File

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

View File

@ -60,6 +60,7 @@ void RadiosondeSettings::resetToDefaults()
m_displayPosition = false;
m_mobile = false;
m_email = "";
m_showPredictedPaths = false;
for (int i = 0; i < RADIOSONDES_COLUMNS; i++)
{
@ -95,6 +96,7 @@ QByteArray RadiosondeSettings::serialize() const
s.writeBool(17, m_displayPosition);
s.writeBool(18, m_mobile);
s.writeString(19, m_email);
s.writeBool(20, m_showPredictedPaths);
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(18, &m_mobile, false);
d.readString(19, &m_email, "");
d.readBool(20, &m_showPredictedPaths, false);
for (int i = 0; i < RADIOSONDES_COLUMNS; 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")) {
m_email = settings.m_email;
}
if (settingsKeys.contains("showPredictedPaths")) {
m_showPredictedPaths = settings.m_showPredictedPaths;
}
if (settingsKeys.contains("workspaceIndex")) {
m_workspaceIndex = settings.m_workspaceIndex;
}
@ -292,6 +298,9 @@ QString RadiosondeSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("email") || force) {
ostr << " m_email: " << m_email.toStdString();
}
if (settingsKeys.contains("showPredictedPaths") || force) {
ostr << " m_showPredictedPaths: " << m_showPredictedPaths;
}
if (settingsKeys.contains("workspaceIndex") || force) {
ostr << " m_workspaceIndex: " << m_workspaceIndex;
}

View File

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

View File

@ -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 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.
@ -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.
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.
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)

View File

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

View File

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

View File

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

View File

@ -20,12 +20,42 @@
#include "SWGFeatureActions.h"
#include "SWGMapActions.h"
#include "SWGPERTesterActions.h"
#include "SWGDeviceState.h"
#include "maincore.h"
#include "feature/featureset.h"
#include "feature/feature.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
bool FeatureWebAPIUtils::mapFind(const QString& target, int featureSetIndex, int featureIndex)
{

View File

@ -47,6 +47,7 @@ private slots:
class SDRBASE_API FeatureWebAPIUtils
{
public:
static bool run(int featureSetIndex, int featureIndex);
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 skyMapFind(const QString& target, int featureSetIndex=-1, int featureIndex=-1);

View File

@ -42,7 +42,8 @@ MainParser::MainParser() :
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_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
@ -56,6 +57,7 @@ MainParser::MainParser() :
m_remoteTCPSinkHWType = "";
m_remoteTCPSinkSerial = "";
m_listDevices = false;
m_start = false;
m_parser.setApplicationDescription("Software Defined Radio application");
m_parser.addHelpOption();
@ -72,6 +74,7 @@ MainParser::MainParser() :
m_parser.addOption(m_remoteTCPSinkHWTypeOption);
m_parser.addOption(m_remoteTCPSinkSerialOption);
m_parser.addOption(m_listDevicesOption);
m_parser.addOption(m_startOption);
}
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";
exit (EXIT_FAILURE);
}
// Start devices and features
m_start = m_parser.isSet(m_startOption);
}

View File

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

View File

@ -76,6 +76,12 @@ QString RS41Frame::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
{
return (ba[offset] & 0xff)
@ -130,7 +136,7 @@ void RS41Frame::decodeMeas(const QByteArray ba)
m_pressureMain = getUInt24(ba, 0x1b);
m_pressureRef1 = getUInt24(ba, 0x1e);
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)
@ -187,7 +193,7 @@ static float waterVapourSaturationPressure(float tCelsius)
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 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;
}
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;
@ -302,7 +308,7 @@ static float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, fl
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
float s = (f-f1) / (float)(f2-f1);

View File

@ -124,6 +124,7 @@ public:
static int getFrameLength(int frameType);
protected:
int16_t getInt16(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 getUInt32(const QByteArray ba, int offset) const;

View File

@ -126,6 +126,7 @@ void SondeHub::upload(
obj.insert("subtype", subframe->getType());
}
//obj.insert("dev", true);
//qDebug() << obj;
QJsonArray payloads {
obj
@ -182,6 +183,17 @@ void SondeHub::updatePosition(
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)
{
if (reply)
@ -189,7 +201,89 @@ void SondeHub::handleReply(QNetworkReply* reply)
if (!reply->error())
{
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
{

View File

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

View File

@ -61,6 +61,7 @@
#include "feature/featureset.h"
#include "feature/feature.h"
#include "feature/featuregui.h"
#include "feature/featurewebapiutils.h"
#include "mainspectrum/mainspectrumgui.h"
#include "commands/commandkeyreceiver.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());
connect(fsm, &InitFSM::finished, fsm, &InitFSM::deleteLater);
connect(fsm, &InitFSM::finished, splash, &SDRangelSplash::deleteLater);
if (parser.getStart()) {
connect(fsm, &InitFSM::finished, this, &MainWindow::startAllAfterDelay);
}
fsm->start();
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
void MainWindow::startAllDevices(const Workspace *workspace) const
{

View File

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

View File

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

View File

@ -444,6 +444,7 @@ EMNR::G::G(
std::copy(Calculus::GG.begin(), Calculus::GG.end(), GG.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...
if ((fileb = fopen("calculus", "rb")))
{
@ -462,7 +463,7 @@ EMNR::G::G(
std::copy(ggs.begin(), ggs.end(), GGS.begin());
}
fclose(fileb);
}
}*/
}
void EMNR::G::calc_gamma0()