From 5e15edcbcf53c6e160d5ac5718f00f6d666477f4 Mon Sep 17 00:00:00 2001 From: srcejon Date: Thu, 4 Apr 2024 15:19:35 +0100 Subject: [PATCH] Combine results from worker. Automatically add / remove channels. --- doc/img/SID_plugin_settings_dialog.jpg | Bin 0 -> 39653 bytes plugins/feature/sid/readme.md | 20 +++-- plugins/feature/sid/sid.h | 18 ++--- plugins/feature/sid/sidgui.cpp | 94 +++++++++++++++++++++- plugins/feature/sid/sidgui.h | 4 +- plugins/feature/sid/sidsettings.cpp | 64 ++++++++++++++- plugins/feature/sid/sidsettings.h | 2 + plugins/feature/sid/sidsettingsdialog.cpp | 86 ++++++-------------- plugins/feature/sid/sidsettingsdialog.h | 9 ++- plugins/feature/sid/sidsettingsdialog.ui | 18 ++++- plugins/feature/sid/sidworker.cpp | 12 ++- 11 files changed, 236 insertions(+), 91 deletions(-) create mode 100644 doc/img/SID_plugin_settings_dialog.jpg diff --git a/doc/img/SID_plugin_settings_dialog.jpg b/doc/img/SID_plugin_settings_dialog.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dad6af8dc8f4ed1acf0257a5ec5fcc10c8f2e9fa GIT binary patch literal 39653 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 literal 0 HcmV?d00001 diff --git a/plugins/feature/sid/readme.md b/plugins/feature/sid/readme.md index 319c7e259..8a7d7759e 100644 --- a/plugins/feature/sid/readme.md +++ b/plugins/feature/sid/readme.md @@ -25,7 +25,9 @@ To help determine the cause of a SID, addtional data can be plotted from a varie * it can display GRB events on the chart, measured by satellites such as Fermi and Swift, * it can display solar flare events detected by the STIX X-ray instrument on the Solar Orbiter satellite, * it can display proton flux measured by the GOES satellites, -* it can control the time in a 3D Map, to see the corresponding effect on MUF (Maximum Usable Frequency) and foF2 (F2 layer critical frequency). +* it can control the time in a 3D [Map](../../feature/map/readme.md), to see the corresponding effect on MUF (Maximum Usable Frequency) and foF2 (F2 layer critical frequency). + +The SID feature can record power from any RX channel plugin that has a channelPowerDB value in its channel report, so can be used for recording and plotting power vs time for purposes other than SID monitoring. ![SID feature plugin](../../../doc/img/SID_plugin.jpg) @@ -88,9 +90,9 @@ Data from the secondary satellite may be useful when the primary is in eclipse.

11: Display Proton Flux

-Check to display 10 MeV and 100 MeV proton flux measurements from the primary GOES satellte on the chart. +Check to display 10 MeV and 100 MeV proton flux measurements from the primary GOES satellite on the chart. A peak in the proton flux can occur one to three days after a CME (Coronal Mass Ejection) is directed towards Earth. -Whereas X-rays from flares can impact any part of the ionosphere that are facing the sun, the Earth's magnetosphere typically directs +Whereas X-rays from flares can impact any part of the ionosphere that is facing the sun, the Earth's magnetosphere typically directs the particles in the CME towards the poles, so a corresponding SID is most likely to be detected if you are receiving a signal from a transmitter crossing the polar region. @@ -103,7 +105,7 @@ The context menu also has an item to display the location of the GRB in the [Sky

13: Display Solar Flares

-Check to display solar flares on the chart as record by the STIX X-ray instrument on the Solar Oribter satellite. +Check to display solar flares on the chart as record by the STIX X-ray instrument on the Solar Orbiter satellite. You can right click on a solar flare to display the context menu, which contains a number of links to additional data from the STIX instrument. The solar flare data is not realtime and can sometimes be delayed by 24 hours. @@ -123,18 +125,22 @@ Click to open the Settings Dialog. The settings dialog allows a user to: - Select which channels data is recorded from. - What colours are used for the data series. +- What labels will be used for the series. - Whether auto-save is enabled. When auto-save is enabled, data will be automatically saved as the specified interval. - Whether auto-load is enabled. When auto-load is enabled, auto-save data will be automatically loaded when the SID feature is opened. - The filename is use for auto-save. +- How often, in minutes, the data is auto-saved. - Where the chart legend should be positioned. +![SID settings dialog](../../../doc/img/SID_plugin_settings_dialog.png) +

17: Display SDO/SOHO Imagery

When checked, displays imagary from NASA's SDO (Solar Dynamic Observatory) and ESA/NASA's SOHO (Solar and Heliospheric Observatory) satellites. SDOs images the Sun in a variety of UV and EUV wavelengths. SOHO shows images of the solar corona. The images are near real-time, updated every 15 minutes. -Solar flares are particularly visibible in the AIA 131 Å images. +Solar flares are particularly visible in the AIA 131 Å images.

18: Image or Video Selection

@@ -215,12 +221,12 @@ When checked, the latest SDO imagery is displayed. When unchecked, you can enter

31: Date Time

-Specifies the date and time for which SDR imagery should be displayed. Images are updated every 15 minutes. The data and time can also be set by clicking on the chart. +Specifies the date and time for which SDO imagery should be displayed. Images are updated every 15 minutes. The date and time can also be set by clicking on the chart.

32: Map

Select a Map to link to the SID feature. When a time is selected on the SID charts, the [Map](../../feature/map/readme.md) feature will have it's time set accordingly. -This allows you, for example, to see the corresponding impact on MUF/foF2. +This allows you, for example, to see the corresponding impact on MUF/foF2 displayed on the 3D map.

Tips

diff --git a/plugins/feature/sid/sid.h b/plugins/feature/sid/sid.h index 0bdbccf54..8e0e08bbd 100644 --- a/plugins/feature/sid/sid.h +++ b/plugins/feature/sid/sid.h @@ -110,23 +110,23 @@ public: public: QDateTime getDateTime() const { return m_dateTime; } - QString getId() const { return m_id; } - double getMeasurement() const { return m_measurement; } + const QStringList& getIds() const { return m_ids; } + const QList& getMeasurements() const { return m_measurements; } - static MsgMeasurement* create(QDateTime dateTime, const QString& id, double measurement) { - return new MsgMeasurement(dateTime, id, measurement); + static MsgMeasurement* create(QDateTime dateTime, const QStringList& ids, const QList& measurements) { + return new MsgMeasurement(dateTime, ids, measurements); } private: QDateTime m_dateTime; - const QString m_id; - double m_measurement; + QStringList m_ids; + QList m_measurements; - MsgMeasurement(QDateTime dateTime, const QString& id, double measurement) : + MsgMeasurement(QDateTime dateTime, const QStringList& ids, const QList& measurements) : Message(), m_dateTime(dateTime), - m_id(id), - m_measurement(measurement) + m_ids(ids), + m_measurements(measurements) {} }; diff --git a/plugins/feature/sid/sidgui.cpp b/plugins/feature/sid/sidgui.cpp index 2b40f000b..aa15ec393 100644 --- a/plugins/feature/sid/sidgui.cpp +++ b/plugins/feature/sid/sidgui.cpp @@ -99,8 +99,15 @@ bool SIDGUI::handleMessage(const Message& message) } else if (SIDMain::MsgMeasurement::match(message)) { - const SIDMain::MsgMeasurement& measurement = (SIDMain::MsgMeasurement&) message; - addMeasurement(measurement.getId(), measurement.getDateTime(), measurement.getMeasurement()); + // Measurements from SIDWorker + const SIDMain::MsgMeasurement& measurementsMsg = (SIDMain::MsgMeasurement&) message; + QDateTime dt = measurementsMsg.getDateTime(); + const QStringList& ids = measurementsMsg.getIds(); + const QList& measurements = measurementsMsg.getMeasurements(); + + for (int i = 0; i < ids.size(); i++) { + addMeasurement(ids[i], dt, measurements[i]); + } return true; } @@ -151,7 +158,8 @@ SIDGUI::SIDGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_grbSeries(nullptr), m_stix(nullptr), m_stixSeries(nullptr), - m_availableFeatureHandler({"sdrangel.feature.map"}) + m_availableFeatureHandler({"sdrangel.feature.map"}), + m_availableChannelHandler({}, "RM") { m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); @@ -260,6 +268,13 @@ SIDGUI::SIDGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur &SIDGUI::featuresChanged ); m_availableFeatureHandler.scanAvailableChannelsAndFeatures(); + QObject::connect( + &m_availableChannelHandler, + &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged, + this, + &SIDGUI::channelsChanged + ); + m_availableChannelHandler.scanAvailableChannelsAndFeatures(); QObject::connect(ui->chartSplitter, &QSplitter::splitterMoved, this, &SIDGUI::chartSplitterMoved); QObject::connect(ui->sdoSplitter, &QSplitter::splitterMoved, this, &SIDGUI::sdoSplitterMoved); @@ -275,6 +290,11 @@ SIDGUI::~SIDGUI() this, &SIDGUI::featuresChanged ); + QObject::disconnect(&m_availableChannelHandler, + &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged, + this, + &SIDGUI::channelsChanged + ); disconnectDataUpdates(); if (m_grb) { disconnect(m_grb, &GRB::dataUpdated, this, &SIDGUI::grbDataUpdated); @@ -1413,6 +1433,14 @@ void SIDGUI::on_deleteAll_clicked() void SIDGUI::on_settings_clicked() { SIDSettingsDialog dialog(&m_settings); + + QObject::connect( + &dialog, + &SIDSettingsDialog::removeChannels, + this, + &SIDGUI::removeChannels + ); + if (dialog.exec() == QDialog::Accepted) { setAutosaveTimer(); @@ -1959,6 +1987,61 @@ void SIDGUI::featuresChanged(const QStringList& renameFrom, const QStringList& r } } +void SIDGUI::channelsChanged(const QStringList& renameFrom, const QStringList& renameTo, const QStringList& removed, const QStringList& added) +{ + removeChannels(removed); + + // Rename measurements and settings that have had their id changed + for (int i = 0; i < renameFrom.size(); i++) + { + for (int j = 0; j < m_channelMeasurements.size(); j++) + { + if (m_channelMeasurements[j].m_id == renameFrom[i]) { + m_channelMeasurements[j].m_id = renameTo[i]; + } + } + for (int j = 0; j < m_settings.m_channelSettings.size(); j++) + { + if (m_settings.m_channelSettings[j].m_id == renameFrom[i]) { + m_settings.m_channelSettings[j].m_id = renameTo[i]; + } + } + } + + // Create settings for any new channels + // Don't call createChannelSettings when channels are removed, as ids might not have been updated yet + if (added.size() > 0) + { + if (m_settings.createChannelSettings()) { + applySetting("channelSettings"); + } + } +} + +void SIDGUI::removeChannels(const QStringList& ids) +{ + for (int i = 0; i < ids.size(); i++) + { + for (int j = 0; j < m_channelMeasurements.size(); j++) + { + if (ids[i] == m_channelMeasurements[j].m_id) + { + m_channelMeasurements.removeAt(j); + break; + } + } + + for (int j = 0; j < m_settings.m_channelSettings.size(); j++) + { + if (ids[i] == m_settings.m_channelSettings[j].m_id) + { + m_settings.m_channelSettings.removeAt(j); + break; + } + } + } +} + void SIDGUI::autosave() { qDebug() << "SIDGUI::autosave start"; @@ -2122,6 +2205,7 @@ void SIDGUI::readCSV(const QString& filename, bool autoload) colors.removeAll(channelSettings.m_color.rgb()); } + bool channelSettingsChanged = false; QStringList colNames; if (CSV::readRow(in, &colNames)) { @@ -2185,6 +2269,7 @@ void SIDGUI::readCSV(const QString& filename, bool autoload) newSettings.m_label = name.mid(idx + 1); newSettings.m_color = colors.takeFirst(); m_settings.m_channelSettings.append(newSettings); + channelSettingsChanged = true; } } } @@ -2236,6 +2321,9 @@ void SIDGUI::readCSV(const QString& filename, bool autoload) plotChart(); connectDataUpdates(); getData(); + if (channelSettingsChanged) { + applySetting("channelSettings"); + } } } diff --git a/plugins/feature/sid/sidgui.h b/plugins/feature/sid/sidgui.h index 52446c4be..bf7d14af7 100644 --- a/plugins/feature/sid/sidgui.h +++ b/plugins/feature/sid/sidgui.h @@ -193,6 +193,7 @@ private: QScatterSeries *m_stixSeries; AvailableChannelOrFeatureHandler m_availableFeatureHandler; + AvailableChannelOrFeatureHandler m_availableChannelHandler; explicit SIDGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); virtual ~SIDGUI(); @@ -243,7 +244,6 @@ private: void connectDataUpdates(); void disconnectDataUpdates(); void getData(); - void openSkyMap(); static qreal pixelDistance(QChart *chart, QAbstractSeries *series, QPointF a, QPointF b); @@ -306,6 +306,8 @@ private slots: void onSatTrackerAdded(int featureSetIndex, Feature *feature); void on_map_currentTextChanged(const QString& text); void featuresChanged(const QStringList& renameFrom, const QStringList& renameTo); + void channelsChanged(const QStringList& renameFrom, const QStringList& renameTo, const QStringList& removed, const QStringList& added); + void removeChannels(const QStringList& ids); }; #endif // INCLUDE_FEATURE_SIDGUI_H_ diff --git a/plugins/feature/sid/sidsettings.cpp b/plugins/feature/sid/sidsettings.cpp index 3de9987d3..7b3dadc68 100644 --- a/plugins/feature/sid/sidsettings.cpp +++ b/plugins/feature/sid/sidsettings.cpp @@ -21,6 +21,10 @@ #include "util/simpleserializer.h" #include "settings/serializable.h" +#include "channel/channelwebapiutils.h" +#include "device/deviceset.h" +#include "device/deviceapi.h" +#include "maincore.h" #include "sidsettings.h" @@ -432,7 +436,7 @@ void SIDSettings::applySettings(const QStringList& settingsKeys, const SIDSettin if (settingsKeys.contains("chartSplitterSizes")) { m_chartSplitterSizes = settings.m_chartSplitterSizes; } - if (settingsKeys.contains("title")) { + if (settingsKeys.contains("title")) { m_title = settings.m_title; } if (settingsKeys.contains("rgbColor")) { @@ -453,7 +457,7 @@ void SIDSettings::applySettings(const QStringList& settingsKeys, const SIDSettin if (settingsKeys.contains("reverseAPIFeatureIndex")) { m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex; } - if (settingsKeys.contains("workspaceIndex")) { + if (settingsKeys.contains("workspaceIndex")) { m_workspaceIndex = settings.m_workspaceIndex; } } @@ -596,6 +600,62 @@ SIDSettings::ChannelSettings *SIDSettings::getChannelSettings(const QString& id) return nullptr; } +bool SIDSettings::createChannelSettings() +{ + bool settingsChanged = false; + QStringList ids; + QStringList titles; + + getChannels(ids, titles); + + // Create settings for channels we don't currently have settings for + for (int i = 0; i < ids.size(); i++) + { + SIDSettings::ChannelSettings *channelSettings = getChannelSettings(ids[i]); + if (!channelSettings) + { + SIDSettings::ChannelSettings newSettings; + newSettings.m_id = ids[i]; + newSettings.m_enabled = true; + newSettings.m_label = titles[i]; + newSettings.m_color = SIDSettings::m_defaultColors[i % SIDSettings::m_defaultColors.size()]; + m_channelSettings.append(newSettings); + settingsChanged = true; + } + } + + return settingsChanged; +} + +// Get channels that have channelPowerDB value in their report +void SIDSettings::getChannels(QStringList& ids, QStringList& titles) +{ + MainCore *mainCore = MainCore::instance(); + + std::vector deviceSets = mainCore->getDeviceSets(); + for (unsigned int deviceSetIndex = 0; deviceSetIndex < deviceSets.size(); deviceSetIndex++) + { + DeviceSet *deviceSet = deviceSets[deviceSetIndex]; + + for (int channelIndex = 0; channelIndex < deviceSet->getNumberOfChannels(); channelIndex++) + { + QString title; + ChannelWebAPIUtils::getChannelSetting(deviceSetIndex, channelIndex, "title", title); + + double power; + if (ChannelWebAPIUtils::getChannelReportValue(deviceSetIndex, channelIndex, "channelPowerDB", power)) + { + ChannelAPI *channel = mainCore->getChannel(deviceSetIndex, channelIndex); + + QString id = mainCore->getChannelId(channel); + + ids.append(id); + titles.append(title); + } + } + } +} + QByteArray SIDSettings::ChannelSettings::serialize() const { SimpleSerializer s(1); diff --git a/plugins/feature/sid/sidsettings.h b/plugins/feature/sid/sidsettings.h index 38b559b56..10414ee33 100644 --- a/plugins/feature/sid/sidsettings.h +++ b/plugins/feature/sid/sidsettings.h @@ -103,6 +103,8 @@ struct SIDSettings void applySettings(const QStringList& settingsKeys, const SIDSettings& settings); QString getDebugString(const QStringList& settingsKeys, bool force=false) const; ChannelSettings *getChannelSettings(const QString& id); + void getChannels(QStringList& ids, QStringList& titles); + bool createChannelSettings(); static const QList m_defaultColors; static const QList m_defaultXRayShortColors; diff --git a/plugins/feature/sid/sidsettingsdialog.cpp b/plugins/feature/sid/sidsettingsdialog.cpp index 5dfcf62cb..98aa21c64 100644 --- a/plugins/feature/sid/sidsettingsdialog.cpp +++ b/plugins/feature/sid/sidsettingsdialog.cpp @@ -16,12 +16,8 @@ /////////////////////////////////////////////////////////////////////////////////// #include "util/units.h" -#include "device/deviceset.h" -#include "device/deviceapi.h" -#include "channel/channelwebapiutils.h" #include "gui/colordialog.h" #include "gui/tablecolorchooser.h" -#include "maincore.h" #include "sidsettingsdialog.h" @@ -54,25 +50,7 @@ SIDSettingsDialog::SIDSettingsDialog(SIDSettings *settings, QWidget* parent) : ui->displayAxisTitles->setChecked(m_settings->m_displayAxisTitles); ui->displaySecondaryAxis->setChecked(m_settings->m_displaySecondaryAxis); - QStringList ids; - QStringList titles; - - getChannels(ids, titles); - - // Create settings for channels we don't currently have settings for - for (int i = 0; i < ids.size(); i++) - { - SIDSettings::ChannelSettings *channelSettings = m_settings->getChannelSettings(ids[i]); - if (!channelSettings) - { - SIDSettings::ChannelSettings newSettings; - newSettings.m_id = ids[i]; - newSettings.m_enabled = true; - newSettings.m_label = titles[i]; - newSettings.m_color = SIDSettings::m_defaultColors[i % SIDSettings::m_defaultColors.size()]; - m_settings->m_channelSettings.append(newSettings); - } - } + m_settings->createChannelSettings(); // Add settings to table for (int i = 0; i < m_settings->m_channelSettings.size(); i++) @@ -125,35 +103,6 @@ SIDSettingsDialog::~SIDSettingsDialog() qDeleteAll(m_colorGUIs); } -// Get channels that have channelPowerDB value in their report -void SIDSettingsDialog::getChannels(QStringList& ids, QStringList& titles) -{ - MainCore *mainCore = MainCore::instance(); - - std::vector deviceSets = mainCore->getDeviceSets(); - for (unsigned int deviceSetIndex = 0; deviceSetIndex < deviceSets.size(); deviceSetIndex++) - { - DeviceSet *deviceSet = deviceSets[deviceSetIndex]; - - for (int channelIndex = 0; channelIndex < deviceSet->getNumberOfChannels(); channelIndex++) - { - QString title; - ChannelWebAPIUtils::getChannelSetting(deviceSetIndex, channelIndex, "title", title); - - double power; - if (ChannelWebAPIUtils::getChannelReportValue(deviceSetIndex, channelIndex, "channelPowerDB", power)) - { - ChannelAPI *channel = mainCore->getChannel(deviceSetIndex, channelIndex); - - QString id = mainCore->getChannelId(channel); - - ids.append(id); - titles.append(title); - } - } - } -} - void SIDSettingsDialog::accept() { m_settings->m_period = ui->period->value(); @@ -179,6 +128,18 @@ void SIDSettingsDialog::accept() m_settings->m_displayAxisTitles = ui->displayAxisTitles->isChecked(); m_settings->m_displaySecondaryAxis = ui->displaySecondaryAxis->isChecked(); + m_settings->m_xrayLongColors[0] = m_colorGUIs[0]->m_color; + m_settings->m_xrayLongColors[1] = m_colorGUIs[1]->m_color; + m_settings->m_xrayShortColors[0] = m_colorGUIs[2]->m_color; + m_settings->m_xrayShortColors[1] = m_colorGUIs[3]->m_color; + m_settings->m_grbColor = m_colorGUIs[4]->m_color; + m_settings->m_stixColor = m_colorGUIs[5]->m_color; + m_settings->m_protonColors[0] = m_colorGUIs[6]->m_color; + m_settings->m_protonColors[2] = m_colorGUIs[7]->m_color; + + if (m_removeIds.size() > 0) { + emit removeChannels(m_removeIds); + } for (int i = 0; i < m_settings->m_channelSettings.size(); i++) { SIDSettings::ChannelSettings *channelSettings = &m_settings->m_channelSettings[i]; @@ -189,15 +150,6 @@ void SIDSettingsDialog::accept() channelSettings->m_color = m_channelColorGUIs[i]->m_color; } - m_settings->m_xrayLongColors[0] = m_colorGUIs[0]->m_color; - m_settings->m_xrayLongColors[1] = m_colorGUIs[1]->m_color; - m_settings->m_xrayShortColors[0] = m_colorGUIs[2]->m_color; - m_settings->m_xrayShortColors[1] = m_colorGUIs[3]->m_color; - m_settings->m_grbColor = m_colorGUIs[4]->m_color; - m_settings->m_stixColor = m_colorGUIs[5]->m_color; - m_settings->m_protonColors[0] = m_colorGUIs[6]->m_color; - m_settings->m_protonColors[2] = m_colorGUIs[7]->m_color; - QDialog::accept(); } @@ -212,3 +164,15 @@ void SIDSettingsDialog::on_browse_clicked() } } } + +void SIDSettingsDialog::on_remove_clicked() +{ + QItemSelectionModel *select = ui->channels->selectionModel(); + while (select->hasSelection()) + { + QModelIndexList list = select->selectedRows(); + int row = list[0].row(); + m_removeIds.append(ui->channels->item(row, CHANNELS_COL_ID)->text()); + ui->channels->removeRow(row); + } +} diff --git a/plugins/feature/sid/sidsettingsdialog.h b/plugins/feature/sid/sidsettingsdialog.h index b8a699880..4cc02ba33 100644 --- a/plugins/feature/sid/sidsettingsdialog.h +++ b/plugins/feature/sid/sidsettingsdialog.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2023 Jon Beniston, M7RCE // +// Copyright (C) 2023-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 // @@ -33,12 +33,12 @@ public: ~SIDSettingsDialog(); private: - void getChannels(QStringList& ids, QStringList& titles); void addColor(const QString& name, QRgb rgb); private slots: void accept(); void on_browse_clicked(); + void on_remove_clicked(); private: Ui::SIDSettingsDialog* ui; @@ -46,6 +46,7 @@ private: QList m_channelColorGUIs; QList m_colorGUIs; QFileDialog m_fileDialog; + QStringList m_removeIds; enum ChannelsRows { CHANNELS_COL_ID, @@ -58,6 +59,10 @@ private: COLORS_COL_NAME, COLORS_COL_COLOR }; + +signals: + void removeChannels(const QStringList& ids); + }; #endif // INCLUDE_SIDSETTINGSDIALOG_H diff --git a/plugins/feature/sid/sidsettingsdialog.ui b/plugins/feature/sid/sidsettingsdialog.ui index e5a9b6d28..499bdc6ff 100644 --- a/plugins/feature/sid/sidsettingsdialog.ui +++ b/plugins/feature/sid/sidsettingsdialog.ui @@ -17,7 +17,7 @@ - APRS Settings + SID Settings @@ -36,7 +36,7 @@ - QAbstractItemView::NoSelection + QAbstractItemView::MultiSelection QAbstractItemView::SelectRows @@ -66,7 +66,7 @@ - + @@ -79,7 +79,7 @@ - + Specifies the time period in seconds between each power measurement @@ -98,6 +98,16 @@ + + + + Remove selected channels and data + + + Remove + + + diff --git a/plugins/feature/sid/sidworker.cpp b/plugins/feature/sid/sidworker.cpp index 06fd2280e..ab7b234fb 100644 --- a/plugins/feature/sid/sidworker.cpp +++ b/plugins/feature/sid/sidworker.cpp @@ -112,6 +112,8 @@ void SIDWorker::update() { // Get powers from each channel QDateTime dateTime = QDateTime::currentDateTime(); + QStringList ids; + QList measurements; for (const auto& channelSettings : m_settings.m_channelSettings) { @@ -133,8 +135,8 @@ void SIDWorker::update() { if (getMessageQueueToGUI()) { - SIDMain::MsgMeasurement *msgToGUI = SIDMain::MsgMeasurement::create(dateTime, channelSettings.m_id, power); - getMessageQueueToGUI()->push(msgToGUI); + ids.append(channelSettings.m_id); + measurements.append(power); } } else @@ -150,4 +152,10 @@ void SIDWorker::update() } } } + + if (getMessageQueueToGUI() && (ids.size() > 0)) + { + SIDMain::MsgMeasurement *msgToGUI = SIDMain::MsgMeasurement::create(dateTime, ids, measurements); + getMessageQueueToGUI()->push(msgToGUI); + } }