Combine results from worker. Automatically add / remove channels.

This commit is contained in:
srcejon 2024-04-04 15:19:35 +01:00
parent f9b43294a8
commit 5e15edcbcf
11 changed files with 236 additions and 91 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -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.
<h3>11: Display Proton Flux</h3>
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
<h3>13: Display Solar Flares</h3>
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)
<h3>17: Display SDO/SOHO Imagery</h3>
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.
<h3>18: Image or Video Selection</h3>
@ -215,12 +221,12 @@ When checked, the latest SDO imagery is displayed. When unchecked, you can enter
<h3>31: Date Time</h3>
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.
<h3>32: Map</h3>
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.
<h2>Tips</h2>

View File

@ -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<double>& 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<double>& measurements) {
return new MsgMeasurement(dateTime, ids, measurements);
}
private:
QDateTime m_dateTime;
const QString m_id;
double m_measurement;
QStringList m_ids;
QList<double> m_measurements;
MsgMeasurement(QDateTime dateTime, const QString& id, double measurement) :
MsgMeasurement(QDateTime dateTime, const QStringList& ids, const QList<double>& measurements) :
Message(),
m_dateTime(dateTime),
m_id(id),
m_measurement(measurement)
m_ids(ids),
m_measurements(measurements)
{}
};

View File

@ -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<double>& 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");
}
}
}

View File

@ -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_

View File

@ -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<DeviceSet*> 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);

View File

@ -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<QRgb> m_defaultColors;
static const QList<QRgb> m_defaultXRayShortColors;

View File

@ -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<DeviceSet*> 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);
}
}

View File

@ -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<TableColorChooser *> m_channelColorGUIs;
QList<TableColorChooser *> 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

View File

@ -17,7 +17,7 @@
</font>
</property>
<property name="windowTitle">
<string>APRS Settings</string>
<string>SID Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
@ -36,7 +36,7 @@
<item row="1" column="0" colspan="2">
<widget class="QTableWidget" name="channels">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
<enum>QAbstractItemView::MultiSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
@ -66,7 +66,7 @@
</column>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="periodLabel">
<property name="minimumSize">
<size>
@ -79,7 +79,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="period">
<property name="toolTip">
<string>Specifies the time period in seconds between each power measurement</string>
@ -98,6 +98,16 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="remove">
<property name="statusTip">
<string>Remove selected channels and data</string>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -112,6 +112,8 @@ void SIDWorker::update()
{
// Get powers from each channel
QDateTime dateTime = QDateTime::currentDateTime();
QStringList ids;
QList<double> 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);
}
}