Merge pull request #2052 from srcejon/freq_scanner

SID (Sudden Ionospheric Disturbance) Feature
pull/2055/head
Edouard Griffiths 2024-04-06 02:52:57 +02:00 zatwierdzone przez GitHub
commit b939a98f6b
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
145 zmienionych plików z 10653 dodań i 384 usunięć

Wyświetl plik

@ -144,6 +144,7 @@ option(ENABLE_FEATURE_PERTESTER "Enable feature pertester plugin" ON)
option(ENABLE_FEATURE_GS232CONTROLLER "Enable feature gs232controller plugin" ON)
option(ENABLE_FEATURE_REMOTECONTROL "Enable feature remote control plugin" ON)
option(ENABLE_FEATURE_SKYMAP "Enable feature sky map plugin" ON)
option(ENABLE_FEATURE_SID "Enable feature sid plugin" ON)
# on windows always build external libraries
if(WIN32)

1
debian/control vendored
Wyświetl plik

@ -69,6 +69,7 @@ Depends: ${shlibs:Depends},
qtspeech5-speechd-plugin,
pulseaudio,
ffmpeg,
gstreamer1.0-libav,
qml-module-qtlocation,
qml-module-qtpositioning,
qml-module-qtquick-window2,

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 9.5 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 641 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 26 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 935 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 16 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 39 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 18 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 578 KiB

Wyświetl plik

@ -1,6 +1,6 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// 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 //
@ -18,8 +18,6 @@
#include <QDebug>
#include "channelpowergui.h"
#include "device/deviceuiset.h"
#include "device/deviceapi.h"
#include "dsp/dspengine.h"
@ -36,6 +34,7 @@
#include "maincore.h"
#include "channelpower.h"
#include "channelpowergui.h"
ChannelPowerGUI* ChannelPowerGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
@ -62,11 +61,14 @@ QByteArray ChannelPowerGUI::serialize() const
bool ChannelPowerGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
if (m_settings.deserialize(data))
{
displaySettings();
applyAllSettings();
return true;
} else {
}
else
{
resetToDefaults();
return false;
}
@ -90,8 +92,7 @@ bool ChannelPowerGUI::handleMessage(const Message& message)
DSPSignalNotification& notif = (DSPSignalNotification&) message;
m_deviceCenterFrequency = notif.getCenterFrequency();
m_basebandSampleRate = notif.getSampleRate();
ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2);
ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2));
calcOffset();
ui->rfBW->setValueRange(floor(log10(m_basebandSampleRate))+1, 0, m_basebandSampleRate);
updateAbsoluteCenterFrequency();
return true;
@ -115,9 +116,23 @@ void ChannelPowerGUI::handleInputMessages()
void ChannelPowerGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySetting("inputFrequencyOffset");
m_settings.m_frequency = m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset;
qint64 value = 0;
if (m_settings.m_frequencyMode == ChannelPowerSettings::Offset) {
value = m_settings.m_inputFrequencyOffset;
} else if (m_settings.m_frequencyMode == ChannelPowerSettings::Absolute) {
value = m_settings.m_frequency;
}
ui->deltaFrequency->blockSignals(true);
ui->deltaFrequency->setValue(value);
ui->deltaFrequency->blockSignals(false);
updateAbsoluteCenterFrequency();
applySettings({"frequency", "inputFrequencyOffset"});
}
void ChannelPowerGUI::channelMarkerHighlightedByCursor()
@ -127,10 +142,23 @@ void ChannelPowerGUI::channelMarkerHighlightedByCursor()
void ChannelPowerGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
qint64 offset = 0;
if (m_settings.m_frequencyMode == ChannelPowerSettings::Offset)
{
offset = value;
m_settings.m_frequency = m_deviceCenterFrequency + offset;
}
else if (m_settings.m_frequencyMode == ChannelPowerSettings::Absolute)
{
m_settings.m_frequency = value;
offset = m_settings.m_frequency - m_deviceCenterFrequency;
}
m_channelMarker.setCenterFrequency(offset);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
updateAbsoluteCenterFrequency();
applySetting("inputFrequencyOffset");
applySettings({"frequency", "inputFrequencyOffset"});
}
void ChannelPowerGUI::on_rfBW_changed(qint64 value)
@ -255,7 +283,6 @@ ChannelPowerGUI::ChannelPowerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet,
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
@ -334,7 +361,8 @@ void ChannelPowerGUI::displaySettings()
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->frequencyMode->setCurrentIndex((int) m_settings.m_frequencyMode);
on_frequencyMode_currentIndexChanged((int) m_settings.m_frequencyMode);
ui->rfBW->setValue(m_settings.m_rfBandwidth);
@ -427,6 +455,47 @@ void ChannelPowerGUI::tick()
m_tickCount++;
}
void ChannelPowerGUI::on_frequencyMode_currentIndexChanged(int index)
{
m_settings.m_frequencyMode = (ChannelPowerSettings::FrequencyMode) index;
ui->deltaFrequency->blockSignals(true);
if (m_settings.m_frequencyMode == ChannelPowerSettings::Offset)
{
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
ui->deltaFrequency->setValue(m_settings.m_inputFrequencyOffset);
ui->deltaUnits->setText("Hz");
}
else if (m_settings.m_frequencyMode == ChannelPowerSettings::Absolute)
{
ui->deltaFrequency->setValueRange(true, 11, 0, 99999999999, 0);
ui->deltaFrequency->setValue(m_settings.m_frequency);
ui->deltaUnits->setText("Hz");
}
ui->deltaFrequency->blockSignals(false);
updateAbsoluteCenterFrequency();
applySetting("frequencyMode");
}
// Calculate input frequency offset, when device center frequency changes
void ChannelPowerGUI::calcOffset()
{
if (m_settings.m_frequencyMode == ChannelPowerSettings::Offset)
{
ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2);
}
else
{
qint64 offset = m_settings.m_frequency - m_deviceCenterFrequency;
m_channelMarker.setCenterFrequency(offset);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
updateAbsoluteCenterFrequency();
applySetting("inputFrequencyOffset");
}
}
void ChannelPowerGUI::on_clearMeasurements_clicked()
{
m_channelPower->resetMagLevels();
@ -434,6 +503,7 @@ void ChannelPowerGUI::on_clearMeasurements_clicked()
void ChannelPowerGUI::makeUIConnections()
{
QObject::connect(ui->frequencyMode, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ChannelPowerGUI::on_frequencyMode_currentIndexChanged);
QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &ChannelPowerGUI::on_deltaFrequency_changed);
QObject::connect(ui->rfBW, &ValueDial::changed, this, &ChannelPowerGUI::on_rfBW_changed);
QObject::connect(ui->pulseTH, QOverload<int>::of(&QDial::valueChanged), this, &ChannelPowerGUI::on_pulseTH_valueChanged);
@ -443,5 +513,12 @@ void ChannelPowerGUI::makeUIConnections()
void ChannelPowerGUI::updateAbsoluteCenterFrequency()
{
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
setStatusFrequency(m_settings.m_frequency);
if ( (m_basebandSampleRate > 1)
&& ( (m_settings.m_inputFrequencyOffset >= m_basebandSampleRate / 2)
|| (m_settings.m_inputFrequencyOffset < -m_basebandSampleRate / 2))) {
setStatusText("Frequency out of band");
} else {
setStatusText("");
}
}

Wyświetl plik

@ -92,6 +92,7 @@ private:
void displaySettings();
bool handleMessage(const Message& message);
void makeUIConnections();
void calcOffset();
void updateAbsoluteCenterFrequency();
void on_clearMeasurements_clicked();
@ -99,6 +100,7 @@ private:
void enterEvent(EnterEventType*);
private slots:
void on_frequencyMode_currentIndexChanged(int index);
void on_deltaFrequency_changed(qint64 value);
void on_rfBW_changed(qint64 value);
void on_clearChannelPower_clicked();

Wyświetl plik

@ -74,16 +74,32 @@
<number>2</number>
</property>
<item>
<widget class="QLabel" name="deltaFrequencyLabel">
<widget class="QComboBox" name="frequencyMode">
<property name="minimumSize">
<size>
<width>16</width>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Df</string>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select frequency entry mode.</string>
</property>
<item>
<property name="text">
<string>Δf</string>
</property>
</item>
<item>
<property name="text">
<string>f</string>
</property>
</item>
</widget>
</item>
<item>

Wyświetl plik

@ -36,6 +36,8 @@ void ChannelPowerSettings::resetToDefaults()
m_rfBandwidth = 10000.0f;
m_pulseThreshold= -50.0f;
m_averagePeriodUS = 100000;
m_frequencyMode = Offset;
m_frequency = 0;
m_rgbColor = QColor(102, 40, 220).rgb();
m_title = "Channel Power";
m_streamIndex = 0;
@ -56,6 +58,8 @@ QByteArray ChannelPowerSettings::serialize() const
s.writeFloat(2, m_rfBandwidth);
s.writeFloat(3, m_pulseThreshold);
s.writeS32(4, m_averagePeriodUS);
s.writeS32(5, (int) m_frequencyMode);
s.writeS64(6, m_frequency);
s.writeU32(21, m_rgbColor);
s.writeString(22, m_title);
@ -102,6 +106,8 @@ bool ChannelPowerSettings::deserialize(const QByteArray& data)
d.readFloat(2, &m_rfBandwidth, 10000.0f);
d.readFloat(3, &m_pulseThreshold, 50.0f);
d.readS32(4, &m_averagePeriodUS, 100000);
d.readS32(5, (int *) &m_frequencyMode, (int) Offset);
d.readS64(6, &m_frequency);
d.readU32(21, &m_rgbColor, QColor(102, 40, 220).rgb());
d.readString(22, &m_title, "Channel Power");
@ -161,6 +167,18 @@ void ChannelPowerSettings::applySettings(const QStringList& settingsKeys, const
if (settingsKeys.contains("averagePeriodUS")) {
m_averagePeriodUS = settings.m_averagePeriodUS;
}
if (settingsKeys.contains("frequencyMode")) {
m_frequencyMode = settings.m_frequencyMode;
}
if (settingsKeys.contains("frequency")) {
m_frequency = settings.m_frequency;
}
if (settingsKeys.contains("rgbColor")) {
m_rgbColor = settings.m_rgbColor;
}
if (settingsKeys.contains("title")) {
m_title = settings.m_title;
}
if (settingsKeys.contains("useReverseAPI")) {
m_useReverseAPI = settings.m_useReverseAPI;
}
@ -191,6 +209,12 @@ QString ChannelPowerSettings::getDebugString(const QStringList& settingsKeys, bo
if (settingsKeys.contains("averagePeriodUS") || force) {
ostr << " m_averagePeriodUS: " << m_averagePeriodUS;
}
if (settingsKeys.contains("frequencyMode") || force) {
ostr << " m_frequencyMode: " << m_frequencyMode;
}
if (settingsKeys.contains("frequency") || force) {
ostr << " m_frequency: " << m_frequency;
}
if (settingsKeys.contains("useReverseAPI") || force) {
ostr << " m_useReverseAPI: " << m_useReverseAPI;
}
@ -200,7 +224,7 @@ QString ChannelPowerSettings::getDebugString(const QStringList& settingsKeys, bo
if (settingsKeys.contains("reverseAPIPort") || force) {
ostr << " m_reverseAPIPort: " << m_reverseAPIPort;
}
if (settingsKeys.contains("everseAPIDeviceIndex") || force) {
if (settingsKeys.contains("reverseAPIDeviceIndex") || force) {
ostr << " m_reverseAPIDeviceIndex: " << m_reverseAPIDeviceIndex;
}

Wyświetl plik

@ -1,6 +1,6 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB. //
// 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 //
@ -32,6 +32,11 @@ struct ChannelPowerSettings
Real m_rfBandwidth;
float m_pulseThreshold;
int m_averagePeriodUS;
enum FrequencyMode {
Offset,
Absolute
} m_frequencyMode;
qint64 m_frequency;
quint32 m_rgbColor;
QString m_title;

Wyświetl plik

@ -413,7 +413,6 @@ void AISDemodSink::applySettings(const AISDemodSettings& settings, bool force)
m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) AISDemodSettings::AISDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
m_lowpass.create(301, AISDemodSettings::AISDEMOD_CHANNEL_SAMPLE_RATE, settings.m_rfBandwidth / 2.0f);
}
if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force)
{
@ -423,7 +422,7 @@ void AISDemodSink::applySettings(const AISDemodSettings& settings, bool force)
if ((settings.m_baud != m_settings.m_baud) || force)
{
m_samplesPerSymbol = AISDemodSettings::AISDEMOD_CHANNEL_SAMPLE_RATE / settings.m_baud;
qDebug() << "ISDemodSink::applySettings: m_samplesPerSymbol: " << m_samplesPerSymbol << " baud " << settings.m_baud;
qDebug() << "AISDemodSink::applySettings: m_samplesPerSymbol: " << m_samplesPerSymbol << " baud " << settings.m_baud;
m_pulseShape.create(0.5, 3, m_samplesPerSymbol);
// Recieve buffer, long enough for one max length message

Wyświetl plik

@ -113,7 +113,6 @@ private:
MovingAverageUtil<Real, double, 16> m_movingAverage;
Lowpass<Complex> m_lowpass; // RF input filter
PhaseDiscriminators m_phaseDiscri; // FM demodulator
Gaussian<Real> m_pulseShape; // Pulse shaping filter
Real *m_rxBuf; // Receive sample buffer, large enough for one max length messsage

Wyświetl plik

@ -268,7 +268,7 @@ void AMDemodGUI::channelMarkerChangedByCursor()
}
m_settings.m_frequency = m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset;
int value = 0;
qint64 value = 0;
if (m_settings.m_frequencyMode == AMDemodSettings::Offset) {
value = m_settings.m_inputFrequencyOffset;
@ -292,8 +292,6 @@ void AMDemodGUI::channelMarkerChangedByCursor()
void AMDemodGUI::on_deltaFrequency_changed(qint64 value)
{
(void) value;
qint64 offset = 0;
if (m_settings.m_frequencyMode == AMDemodSettings::Offset)

Wyświetl plik

@ -390,7 +390,7 @@ bool M17DemodProcessor::decode_packet(modemm17::M17FrameDecoder::packet_buffer_t
<< " Via: " << ax25.m_via
<< " Type: " << ax25.m_type
<< " PID: " << ax25.m_pid
<< " Data: " << ax25.m_dataASCII;
<< " Data: " << QString::fromUtf8(ax25.m_data);
if (m_demodInputMessageQueue)
{
@ -402,7 +402,7 @@ bool M17DemodProcessor::decode_packet(modemm17::M17FrameDecoder::packet_buffer_t
ax25.m_via,
ax25.m_type,
ax25.m_pid,
ax25.m_dataASCII
ax25.m_data
);
msg->getPacket() = packet;
m_demodInputMessageQueue->push(msg);

Wyświetl plik

@ -216,7 +216,7 @@ bool PacketDemod::handleMessage(const Message& cmd)
<< "\"" << ax25.m_via << "\","
<< ax25.m_type << ","
<< ax25.m_pid << ","
<< "\"" << ax25.m_dataASCII << "\","
<< "\"" << QString::fromUtf8(ax25.m_data) << "\","
<< "\"" << ax25.m_dataHex << "\"\n";
}
else
@ -348,7 +348,7 @@ void PacketDemod::applySettings(const PacketDemodSettings& settings, bool force)
if (newFile)
{
// Write header
m_logStream << "Date,Time,Data,From,To,Via,Type,PID,Data ASCII,Data Hex\n";
m_logStream << "Date,Time,Data,From,To,Via,Type,PID,Data UTF-8,Data Hex\n";
}
}
else

Wyświetl plik

@ -59,7 +59,7 @@ void PacketDemodGUI::resizeTable()
ui->packets->setItem(row, PACKET_COL_VIA, new QTableWidgetItem("123456-15-"));
ui->packets->setItem(row, PACKET_COL_TYPE, new QTableWidgetItem("Type-"));
ui->packets->setItem(row, PACKET_COL_PID, new QTableWidgetItem("PID-"));
ui->packets->setItem(row, PACKET_COL_DATA_ASCII, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ"));
ui->packets->setItem(row, PACKET_COL_DATA_STRING, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ"));
ui->packets->setItem(row, PACKET_COL_DATA_HEX, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ"));
ui->packets->resizeColumnsToContents();
ui->packets->removeRow(row);
@ -168,7 +168,7 @@ void PacketDemodGUI::packetReceived(const QByteArray& packet, QDateTime dateTime
QTableWidgetItem *viaItem = new QTableWidgetItem();
QTableWidgetItem *typeItem = new QTableWidgetItem();
QTableWidgetItem *pidItem = new QTableWidgetItem();
QTableWidgetItem *dataASCIIItem = new QTableWidgetItem();
QTableWidgetItem *dataStringItem = new QTableWidgetItem();
QTableWidgetItem *dataHexItem = new QTableWidgetItem();
ui->packets->setItem(row, PACKET_COL_DATE, dateItem);
ui->packets->setItem(row, PACKET_COL_TIME, timeItem);
@ -177,7 +177,7 @@ void PacketDemodGUI::packetReceived(const QByteArray& packet, QDateTime dateTime
ui->packets->setItem(row, PACKET_COL_VIA, viaItem);
ui->packets->setItem(row, PACKET_COL_TYPE, typeItem);
ui->packets->setItem(row, PACKET_COL_PID, pidItem);
ui->packets->setItem(row, PACKET_COL_DATA_ASCII, dataASCIIItem);
ui->packets->setItem(row, PACKET_COL_DATA_STRING, dataStringItem);
ui->packets->setItem(row, PACKET_COL_DATA_HEX, dataHexItem);
dateItem->setText(dateTime.date().toString());
timeItem->setText(dateTime.time().toString());
@ -186,7 +186,7 @@ void PacketDemodGUI::packetReceived(const QByteArray& packet, QDateTime dateTime
viaItem->setText(ax25.m_via);
typeItem->setText(ax25.m_type);
pidItem->setText(ax25.m_pid);
dataASCIIItem->setText(ax25.m_dataASCII);
dataStringItem->setText(QString::fromUtf8(ax25.m_data)); // Should possibly support different encodings here. PacketMod uses UTF8.
dataHexItem->setText(ax25.m_dataHex);
filterRow(row);
ui->packets->setSortingEnabled(true);

Wyświetl plik

@ -116,7 +116,7 @@ private:
PACKET_COL_VIA,
PACKET_COL_TYPE,
PACKET_COL_PID,
PACKET_COL_DATA_ASCII,
PACKET_COL_DATA_STRING,
PACKET_COL_DATA_HEX
};

Wyświetl plik

@ -736,10 +736,10 @@
</column>
<column>
<property name="text">
<string>Data (ASCII)</string>
<string>Data</string>
</property>
<property name="toolTip">
<string>Packet data as ASCII</string>
<string>Packet data as UTF-8 character string</string>
</property>
</column>
<column>
@ -757,9 +757,10 @@
</widget>
<customwidgets>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ValueDialZ</class>
@ -768,10 +769,9 @@
<container>1</container>
</customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>LevelMeterSignalDB</class>

Wyświetl plik

@ -92,5 +92,5 @@ The received packets table displays the contents of the packets that have been r
* Via - List of addresses of repeaters the packet has passed through or directed via.
* Type - The AX.25 frame type.
* PID - Protocol Identifier.
* Data (ASCII) - The AX.25 information field displayed as ASCII.
* Data - The AX.25 information field displayed as UTF-8 character string.
* Data (Hex) - The AX.25 information field displayed as hexadecimal.

Wyświetl plik

@ -879,7 +879,7 @@ void RadiosondeDemodGUI::on_logOpen_clicked()
QStringList cols;
QList<ObjectPipe*> radiosondePipes;
MainCore::instance()->getMessagePipes().getMessagePipes(this, "radiosonde", radiosondePipes);
MainCore::instance()->getMessagePipes().getMessagePipes(m_radiosondeDemod, "radiosonde", radiosondePipes);
while (!cancelled && CSV::readRow(in, &cols))
{

Wyświetl plik

@ -198,7 +198,7 @@ void HeatMapSink::applySettings(const HeatMapSettings& settings, bool force)
|| (settings.m_sampleRate != m_settings.m_sampleRate)
|| force)
{
m_averageCnt = (int)((settings.m_averagePeriodUS * settings.m_sampleRate / 1e6));
m_averageCnt = (int)((settings.m_averagePeriodUS * (qint64)settings.m_sampleRate / 1e6));
// For low sample rates, we want a small buffer, so scope update isn't too slow
if (settings.m_sampleRate < 100) {
m_sampleBufferSize = 1;

Wyświetl plik

@ -594,8 +594,9 @@ void PacketModSource::addTXPacket(QString callsign, QString to, QString via, QSt
// PID
*p++ = m_settings.m_ax25PID;
// Data
len = data.length();
memcpy(p, data.toUtf8(), len);
QByteArray dataBytes = data.toUtf8();
len = dataBytes.length();
memcpy(p, dataBytes, len);
p += len;
// CRC (do not include flags)
crc.calculate(crc_start, p-crc_start);

Wyświetl plik

@ -74,7 +74,7 @@ Enter the routing for the packet. To have the packet repeated by digipeaters, us
<h3>16: Data</h3>
The packet of data to send. To send an APRS status message, use the format <tt>>Status</tt>. The APRS specification can be found at: http://www.aprs.org/doc/APRS101.PDF. APRS messages can be tracked on https://aprs.fi
The packet of data to send. This is encoded using UTF-8. To send an APRS status message, use the format <tt>>Status</tt>. The APRS specification can be found at: http://www.aprs.org/doc/APRS101.PDF. APRS messages can be tracked on https://aprs.fi
<h3>17: TX</h3>

Wyświetl plik

@ -120,3 +120,9 @@ if (ENABLE_FEATURE_REMOTECONTROL)
else()
message(STATUS "Not building remotecontrol (ENABLE_FEATURE_REMOTECONTROL=${ENABLE_FEATURE_REMOTECONTROL})")
endif()
if (ENABLE_FEATURE_SID)
add_subdirectory(sid)
else()
message(STATUS "Not building SID (ENABLED_FEATURE_SID=${ENABLED_FEATURE_SID})")
endif()

Wyświetl plik

@ -408,7 +408,7 @@ bool APRSGUI::handleMessage(const Message& message)
else
{
qDebug() << "APRSGUI::handleMessage: Failed to decode as APRS";
qDebug() << ax25.m_from << " " << ax25.m_to << " " << ax25.m_via << " " << ax25.m_type << " " << ax25.m_pid << " "<< ax25.m_dataASCII;
qDebug() << "From:" << ax25.m_from << "To:" << ax25.m_to << "Via:" << ax25.m_via << "Type:" << ax25.m_type << "PID:" << ax25.m_pid << "Data:" << QString::fromLatin1(ax25.m_data);
}
}
else

Wyświetl plik

@ -102,20 +102,25 @@ bool APRSWorker::handleMessage(const Message& cmd)
{
MainCore::MsgPacket& report = (MainCore::MsgPacket&) cmd;
AX25Packet ax25;
APRSPacket *aprs = new APRSPacket();
if (ax25.decode(report.getPacket()))
{
if (aprs->decode(ax25))
APRSPacket aprs;
// #2029 - Forward data even if we can't decode it fully
aprs.decode(ax25);
if (!aprs.m_data.isEmpty())
{
// See: http://www.aprs-is.net/IGateDetails.aspx for gating rules
if (!aprs->m_via.contains("TCPIP")
&& !aprs->m_via.contains("TCPXX")
&& !aprs->m_via.contains("NOGATE")
&& !aprs->m_via.contains("RFONLY"))
if (!aprs.m_via.contains("TCPIP")
&& !aprs.m_via.contains("TCPXX")
&& !aprs.m_via.contains("NOGATE")
&& !aprs.m_via.contains("RFONLY"))
{
aprs->m_dateTime = report.getDateTime();
QString igateMsg = aprs->toTNC2(m_settings.m_igateCallsign);
send(igateMsg.toUtf8(), igateMsg.length());
aprs.m_dateTime = report.getDateTime();
QByteArray igateMsg = aprs.toTNC2(m_settings.m_igateCallsign);
send(igateMsg.data(), igateMsg.length());
}
}
}
@ -207,7 +212,7 @@ void APRSWorker::recv()
if (!m_loggedIn)
{
// Log in with callsign and passcode
QString login = QString("user %1 pass %2 vers SDRangel 6.4.0%3\r\n").arg(m_settings.m_igateCallsign).arg(m_settings.m_igatePasscode).arg(m_settings.m_igateFilter.isEmpty() ? "" : QString(" filter %1").arg(m_settings.m_igateFilter));
QString login = QString("user %1 pass %2 vers SDRangel 7.19.2%3\r\n").arg(m_settings.m_igateCallsign).arg(m_settings.m_igatePasscode).arg(m_settings.m_igateFilter.isEmpty() ? "" : QString(" filter %1").arg(m_settings.m_igateFilter));
send(login.toLatin1(), login.length());
m_loggedIn = true;
if (m_msgQueueToFeature)

Wyświetl plik

@ -54,7 +54,6 @@ if(NOT SERVER_MODE)
mapibpbeacondialog.ui
mapradiotimedialog.cpp
mapradiotimedialog.ui
mapcolordialog.cpp
mapmodel.cpp
mapitem.cpp
mapwebsocketserver.cpp
@ -75,7 +74,6 @@ if(NOT SERVER_MODE)
mapbeacondialog.h
mapibpbeacon.h
mapradiotimedialog.h
mapcolordialog.h
mapmodel.h
mapitem.h
mapwebsocketserver.h

Wyświetl plik

@ -275,3 +275,13 @@ void CesiumInterface::setPosition(const QGeoCoordinate& position)
{
m_czml.setPosition(position);
}
void CesiumInterface::save(const QString& filename, const QString& dataDir)
{
QJsonObject obj {
{"command", "save"},
{"filename", filename},
{"dataDir", dataDir}
};
send(obj);
}

Wyświetl plik

@ -48,7 +48,6 @@ public:
bool m_reverse;
bool m_loop;
bool m_stop; // Stop looped animation
float m_delay; // Delay in seconds before animation starts
float m_startOffset; // [0..1] What point to start playing animation
float m_duration; // How long to play animation for
float m_multiplier; // Speed to play animation at
@ -80,6 +79,7 @@ public:
void update(PolygonMapItem *mapItem);
void update(PolylineMapItem *mapItem);
void setPosition(const QGeoCoordinate& position);
void save(const QString& filename, const QString& dataDir);
protected:

Wyświetl plik

@ -264,7 +264,9 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
const QStringList heightReferences = {"NONE", "CLAMP_TO_GROUND", "RELATIVE_TO_GROUND", "NONE"};
QString dt;
if (mapItem->m_takenTrackDateTimes.size() > 0) {
if (mapItem->m_availableFrom.isValid()) {
dt = mapItem->m_availableFrom.toString(Qt::ISODateWithMs);
} else if (mapItem->m_takenTrackDateTimes.size() > 0) {
dt = mapItem->m_takenTrackDateTimes.last()->toString(Qt::ISODateWithMs);
} else {
dt = QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs);
@ -580,6 +582,14 @@ QJsonObject CZML::update(ObjectMapItem *mapItem, bool isTarget, bool isSelected)
}
}
}
else
{
if (mapItem->m_availableUntil.isValid())
{
QString period = QString("%1/%2").arg(m_ids[id]).arg(mapItem->m_availableUntil.toString(Qt::ISODateWithMs));
obj.insert("availability", period);
}
}
m_lastPosition.insert(id, coords);
}
else

Wyświetl plik

@ -364,6 +364,52 @@
["railways", railwaysLayer]
]);
function downloadBlob(filename, blob) {
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
} else {
const elem = window.document.createElement("a");
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
function downloadText(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
var dataDir = ""; // Directory where 3D models are stored
function modelCallback(modelGraphics, time, externalFiles) {
const resource = modelGraphics.uri.getValue(time);
console.log("modelcallback " + resource);
const regex = /http:\/\/127.0.0.1:\d+/;
var file = resource.url.replace(regex, dataDir);
// KML only supports Collada files. User will have to convert the models if required
file = file.replace(/glb$/, "dae");
file = file.replace(/gltf$/, "dae");
if (navigator.platform.indexOf('Win') > -1) {
file = file.replace(/\//g, "\\");
}
return file;
}
// Use WebSockets for handling commands from MapPlugin
// (CZML doesn't support camera control, for example)
// and sending events back to it
@ -469,30 +515,40 @@
viewer.scene.postProcessStages.fxaa.enabled = false;
}
} else if (command.command == "showMUF") {
if (mufGeoJSONStream != null) {
viewer.dataSources.remove(mufGeoJSONStream, true);
mufGeoJSONStream = null;
}
if (command.show == true) {
viewer.dataSources.add(
Cesium.GeoJsonDataSource.load(
"muf.geojson",
{ describe: describeMUF }
)
).then(function (dataSource) { mufGeoJSONStream = dataSource; });
).then(function (dataSource) {
if (mufGeoJSONStream != null) {
viewer.dataSources.remove(mufGeoJSONStream, true);
mufGeoJSONStream = null;
}
mufGeoJSONStream = dataSource;
});
} else {
viewer.dataSources.remove(mufGeoJSONStream, true);
mufGeoJSONStream = null;
}
} else if (command.command == "showfoF2") {
if (foF2GeoJSONStream != null) {
viewer.dataSources.remove(foF2GeoJSONStream, true);
foF2GeoJSONStream = null;
}
if (command.show == true) {
viewer.dataSources.add(
Cesium.GeoJsonDataSource.load(
"fof2.geojson",
{ describe: describefoF2 }
)
).then(function (dataSource) { foF2GeoJSONStream = dataSource; });
).then(function (dataSource) {
if (foF2GeoJSONStream != null) {
viewer.dataSources.remove(foF2GeoJSONStream, true);
foF2GeoJSONStream = null;
}
foF2GeoJSONStream = dataSource;
});
} else {
viewer.dataSources.remove(foF2GeoJSONStream, true);
foF2GeoJSONStream = null;
}
} else if (command.command == "showLayer") {
layers.get(command.layer).show = command.show;
@ -639,7 +695,7 @@
czmlStream.process(command);
} else {
var promise = Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [position]);
Cesium.when(promise, function(updatedPositions) {
Cesium.when(promise, function (updatedPositions) {
if (height < updatedPositions[0].height) {
if (size == 3) {
command.position.cartographicDegrees[2] = updatedPositions[0].height;
@ -648,7 +704,7 @@
}
}
czmlStream.process(command);
}, function() {
}, function () {
console.log(`Terrain doesn't support sampleTerrainMostDetailed`);
czmlStream.process(command);
});
@ -657,47 +713,47 @@
console.log(`Can't currently use altitudeReference when more than one position`);
czmlStream.process(command);
}
} else if ( (command.hasOwnProperty('polygon') && command.polygon.hasOwnProperty('altitudeReference'))
|| (command.hasOwnProperty('polyline') && command.polyline.hasOwnProperty('altitudeReference'))) {
} else if ((command.hasOwnProperty('polygon') && command.polygon.hasOwnProperty('altitudeReference'))
|| (command.hasOwnProperty('polyline') && command.polyline.hasOwnProperty('altitudeReference'))) {
// Support per vertex height reference in polygons and CLIP_TO_GROUND in polylines
var prim = command.hasOwnProperty('polygon') ? command.polygon : command.polyline;
var clipToGround = prim.altitudeReference == "CLIP_TO_GROUND";
var clampToGround = prim.altitudeReference == "CLAMP_TO_GROUND";
var size = prim.positions.cartographicDegrees.length;
var positionCount = size/3;
var positionCount = size / 3;
var positions = new Array(positionCount);
if (viewer.terrainProvider instanceof Cesium.EllipsoidTerrainProvider) {
if (clampToGround) {
for (let i = 0; i < positionCount; i++) {
prim.positions.cartographicDegrees[i*3+2] = 0;
prim.positions.cartographicDegrees[i * 3 + 2] = 0;
}
} else if (clipToGround) {
for (let i = 0; i < positionCount; i++) {
if (prim.positions.cartographicDegrees[i*3+2] < 0) {
prim.positions.cartographicDegrees[i*3+2] = 0;
if (prim.positions.cartographicDegrees[i * 3 + 2] < 0) {
prim.positions.cartographicDegrees[i * 3 + 2] = 0;
}
}
}
czmlStream.process(command);
} else {
for (let i = 0; i < positionCount; i++) {
positions[i] = Cesium.Cartographic.fromDegrees(prim.positions.cartographicDegrees[i*3+0], prim.positions.cartographicDegrees[i*3+1]);
positions[i] = Cesium.Cartographic.fromDegrees(prim.positions.cartographicDegrees[i * 3 + 0], prim.positions.cartographicDegrees[i * 3 + 1]);
}
var promise = Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions);
Cesium.when(promise, function(updatedPositions) {
Cesium.when(promise, function (updatedPositions) {
if (clampToGround) {
for (let i = 0; i < positionCount; i++) {
prim.positions.cartographicDegrees[i*3+2] = updatedPositions[i].height;
prim.positions.cartographicDegrees[i * 3 + 2] = updatedPositions[i].height;
}
} else if (clipToGround) {
for (let i = 0; i < positionCount; i++) {
if (prim.positions.cartographicDegrees[i*3+2] < updatedPositions[i].height) {
prim.positions.cartographicDegrees[i*3+2] = updatedPositions[i].height;
if (prim.positions.cartographicDegrees[i * 3 + 2] < updatedPositions[i].height) {
prim.positions.cartographicDegrees[i * 3 + 2] = updatedPositions[i].height;
}
}
}
czmlStream.process(command);
}, function() {
}, function () {
console.log(`Terrain doesn't support sampleTerrainMostDetailed`);
czmlStream.process(command);
});
@ -705,7 +761,20 @@
} else {
czmlStream.process(command);
}
} else if (command.command == "save") {
// Export to kml/kmz
dataDir = command.dataDir;
Cesium.exportKml({
entities: czmlStream.entities,
kmz: command.filename.endsWith("kmz"),
modelCallback: modelCallback
}).then(function (result) {
if (command.filename.endsWith("kmz")) {
downloadBlob(command.filename, result.kmz);
} else {
downloadText(command.filename, result.kml);
}
});
} else {
console.log(`Unknown command ${command.command}`);
}
@ -759,10 +828,13 @@
Cesium.knockout.getObservable(viewer.clockViewModel, 'multiplier').subscribe(function(multiplier) {
reportClock();
});
// This is called every frame
// This is called every frame, which is too fast, so instead use setInterval with 1 second period
//Cesium.knockout.getObservable(viewer.clockViewModel, 'currentTime').subscribe(function(currentTime) {
//reportClock();
//});
setInterval(function () {
reportClock();
}, 1000);
viewer.timeline.addEventListener('settime', reportClock, false);
socket.onopen = () => {

Wyświetl plik

@ -48,6 +48,7 @@
#include "util/maidenhead.h"
#include "util/morse.h"
#include "util/navtex.h"
#include "util/vlftransmitters.h"
#include "maplocationdialog.h"
#include "mapmaidenheaddialog.h"
#include "mapsettingsdialog.h"
@ -306,6 +307,9 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
connect(ui->web->page(), &QWebEnginePage::loadingChanged, this, &MapGUI::loadingChanged);
connect(ui->web, &QWebEngineView::renderProcessTerminated, this, &MapGUI::renderProcessTerminated);
#endif
QWebEngineProfile *profile = QWebEngineProfile::defaultProfile();
connect(profile, &QWebEngineProfile::downloadRequested, this, &MapGUI::downloadRequested);
#endif
// Get station position
@ -489,41 +493,24 @@ void MapGUI::addIBPBeacons()
}
}
// https://sidstation.loudet.org/stations-list-en.xhtml
// https://core.ac.uk/download/pdf/224769021.pdf -- Table 1
// GQD/GQZ callsigns: https://groups.io/g/VLF/message/19212?p=%2C%2C%2C20%2C0%2C0%2C0%3A%3Arecentpostdate%2Fsticky%2C%2C19.6%2C20%2C2%2C0%2C38924431
const QList<RadioTimeTransmitter> MapGUI::m_vlfTransmitters = {
// Other signals possibly seen: 13800, 19000
{"VTX2", 17000, 8.387015, 77.752762, -1}, // South Vijayanarayanam, India
{"GQD", 19580, 54.911643, -3.278456, 100}, // Anthorn, UK, Often referred to as GBZ
{"NWC", 19800, -21.816325, 114.16546, 1000}, // Exmouth, Aus
{"ICV", 20270, 40.922946, 9.731881, 50}, // Isola di Tavolara, Italy (Can be distorted on 3D map if terrain used)
{"FTA", 20900, 48.544632, 2.579429, 50}, // Sainte-Assise, France (Satellite imagary obfuscated)
{"NPM", 21400, 21.420166, -158.151140, 600}, // Pearl Harbour, Lualuahei, USA (Not seen?)
{"HWU", 21750, 46.713129, 1.245248, 200}, // Rosnay, France
{"GQZ", 22100, 54.731799, -2.883033, 100}, // Skelton, UK (GVT in paper)
{"DHO38", 23400, 53.078900, 7.615000, 300}, // Rhauderfehn, Germany - Off air 7-8 UTC - Not seen on air!
{"NAA", 24000, 44.644506, -67.284565, 1000}, // Cutler, Maine, USA
{"TFK/NRK", 37500, 63.850365, -22.466773, 100}, // Grindavik, Iceland
{"SRC/SHR", 38000, 57.120328, 16.153083, -1}, // Ruda, Sweden
};
void MapGUI::addVLF()
{
for (int i = 0; i < m_vlfTransmitters.size(); i++)
for (int i = 0; i < VLFTransmitters::m_transmitters.size(); i++)
{
SWGSDRangel::SWGMapItem vlfMapItem;
// Need to suffix frequency, as there are multiple becaons with same callsign at different locations
QString name = QString("%1").arg(m_vlfTransmitters[i].m_callsign);
QString name = QString("%1").arg(VLFTransmitters::m_transmitters[i].m_callsign);
vlfMapItem.setName(new QString(name));
vlfMapItem.setLatitude(m_vlfTransmitters[i].m_latitude);
vlfMapItem.setLongitude(m_vlfTransmitters[i].m_longitude);
vlfMapItem.setLatitude(VLFTransmitters::m_transmitters[i].m_latitude);
vlfMapItem.setLongitude(VLFTransmitters::m_transmitters[i].m_longitude);
vlfMapItem.setAltitude(0.0);
vlfMapItem.setImage(new QString("antenna.png"));
vlfMapItem.setImageRotation(0);
QString text = QString("VLF Transmitter\nCallsign: %1\nFrequency: %2 kHz")
.arg(m_vlfTransmitters[i].m_callsign)
.arg(m_vlfTransmitters[i].m_frequency/1000.0);
.arg(VLFTransmitters::m_transmitters[i].m_callsign)
.arg(VLFTransmitters::m_transmitters[i].m_frequency/1000.0);
if (VLFTransmitters::m_transmitters[i].m_power > 0) {
text.append(QString("\nPower: %1 kW").arg(VLFTransmitters::m_transmitters[i].m_power));
}
vlfMapItem.setText(new QString(text));
vlfMapItem.setModel(new QString("antenna.glb"));
vlfMapItem.setFixedPosition(true);
@ -535,7 +522,6 @@ void MapGUI::addVLF()
}
}
const QList<RadioTimeTransmitter> MapGUI::m_radioTimeTransmitters = {
{"MSF", 60000, 54.9075f, -3.27333f, 17}, // UK
{"DCF77", 77500, 50.01611111f, 9.00805556f, 50}, // Germany
@ -722,12 +708,18 @@ void MapGUI::addIonosonde()
m_giro = GIRO::create();
if (m_giro)
{
connect(m_giro, &GIRO::indexUpdated, this, &MapGUI::giroIndexUpdated);
connect(m_giro, &GIRO::dataUpdated, this, &MapGUI::giroDataUpdated);
connect(m_giro, &GIRO::mufUpdated, this, &MapGUI::mufUpdated);
connect(m_giro, &GIRO::foF2Updated, this, &MapGUI::foF2Updated);
}
}
void MapGUI::giroIndexUpdated(const QList<GIRO::DataSet>& data)
{
(void) data;
}
void MapGUI::giroDataUpdated(const GIRO::GIROStationData& data)
{
if (!data.m_station.isEmpty())
@ -761,6 +753,8 @@ void MapGUI::giroDataUpdated(const GIRO::GIROStationData& data)
ionosondeStationMapItem.setLabel(new QString(station->m_label));
ionosondeStationMapItem.setLabelAltitudeOffset(4.5);
ionosondeStationMapItem.setAltitudeReference(1);
ionosondeStationMapItem.setAvailableFrom(new QString(data.m_dateTime.toString(Qt::ISODateWithMs)));
ionosondeStationMapItem.setAvailableUntil(new QString(data.m_dateTime.addDays(3).toString(Qt::ISODateWithMs))); // Remove after data is too old
update(m_map, &ionosondeStationMapItem, "Ionosonde Stations");
}
}
@ -786,6 +780,24 @@ void MapGUI::foF2Updated(const QJsonDocument& document)
}
}
void MapGUI::updateGIRO(const QDateTime& mapDateTime)
{
if (m_giro)
{
if (m_settings.m_displayMUF || m_settings.m_displayfoF2)
{
QString giroRunId = m_giro->getRunId(mapDateTime);
if (m_giroRunId.isEmpty() || (!giroRunId.isEmpty() && (giroRunId != m_giroRunId)))
{
m_giro->getMUF(giroRunId);
m_giro->getMUF(giroRunId);
m_giroRunId = giroRunId;
m_giroDateTime = mapDateTime;
}
}
}
}
void MapGUI::pathUpdated(const QString& radarPath, const QString& satellitePath)
{
m_radarPath = radarPath;
@ -1683,6 +1695,7 @@ void MapGUI::displayToolbar()
ui->displayNASAGlobalImagery->setVisible(overlayButtons);
ui->displayMUF->setVisible(!narrow && m_settings.m_map3DEnabled);
ui->displayfoF2->setVisible(!narrow && m_settings.m_map3DEnabled);
ui->save->setVisible(m_settings.m_map3DEnabled);
}
void MapGUI::setEnableOverlay()
@ -1803,11 +1816,10 @@ void MapGUI::applyMap3DSettings(bool reloadMap)
m_polylineMapModel.allUpdated();
}
MapSettings::MapItemSettings *ionosondeItemSettings = getItemSettings("Ionosonde Stations");
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
if (ionosondeItemSettings) {
m_giro->getDataPeriodically(ionosondeItemSettings->m_enabled ? 2 : 0);
}
m_giro->getMUFPeriodically(m_settings.m_displayMUF ? 15 : 0);
m_giro->getfoF2Periodically(m_settings.m_displayfoF2 ? 15 : 0);
#else
ui->displayMUF->setVisible(false);
ui->displayfoF2->setVisible(false);
@ -2211,7 +2223,7 @@ void MapGUI::on_displayMUF_clicked(bool checked)
m_settings.m_displayMUF = checked;
// Only call show if disabling, so we don't get two updates
// (as getMUFPeriodically results in a call to showMUF when the data is available)
m_giro->getMUFPeriodically(m_settings.m_displayMUF ? 15 : 0);
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
if (m_cesium && !m_settings.m_displayMUF) {
m_cesium->showMUF(m_settings.m_displayMUF);
}
@ -2226,7 +2238,7 @@ void MapGUI::on_displayfoF2_clicked(bool checked)
m_displayfoF2->setChecked(checked);
}
m_settings.m_displayfoF2 = checked;
m_giro->getfoF2Periodically(m_settings.m_displayfoF2 ? 15 : 0);
m_giro->getIndexPeriodically((m_settings.m_displayMUF || m_settings.m_displayfoF2) ? 15 : 0);
if (m_cesium && !m_settings.m_displayfoF2) {
m_cesium->showfoF2(m_settings.m_displayfoF2);
}
@ -2442,6 +2454,22 @@ void MapGUI::track3D(const QString& target)
}
}
void MapGUI::on_save_clicked()
{
if (m_cesium)
{
m_fileDialog.setAcceptMode(QFileDialog::AcceptSave);
m_fileDialog.setNameFilter("*.kml *.kmz");
if (m_fileDialog.exec())
{
QStringList fileNames = m_fileDialog.selectedFiles();
if (fileNames.size() > 0) {
m_cesium->save(fileNames[0], getDataDir());
}
}
}
}
void MapGUI::on_deleteAll_clicked()
{
m_objectMapModel.removeAll();
@ -2543,6 +2571,7 @@ void MapGUI::receivedCesiumEvent(const QJsonObject &obj)
bool canAnimate = obj.value("canAnimate").toBool();
bool shouldAnimate = obj.value("shouldAnimate").toBool();
m_map->setMapDateTime(mapDateTime, systemDateTime, canAnimate && shouldAnimate ? multiplier : 0.0);
updateGIRO(mapDateTime);
}
}
else if (event == "link")
@ -2715,6 +2744,17 @@ void MapGUI::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest)
ui->splitter->addWidget(ui->web);
}
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
void MapGUI::downloadRequested(QWebEngineDownloadRequest *download)
{
download->accept();
}
#else
void MapGUI::downloadRequested(QWebEngineDownloadItem *download)
{
download->accept();
}
#endif
#endif
void MapGUI::preferenceChanged(int elementType)
@ -2787,6 +2827,7 @@ void MapGUI::makeUIConnections()
QObject::connect(ui->displayfoF2, &ButtonSwitch::clicked, this, &MapGUI::on_displayfoF2_clicked);
QObject::connect(ui->find, &QLineEdit::returnPressed, this, &MapGUI::on_find_returnPressed);
QObject::connect(ui->maidenhead, &QToolButton::clicked, this, &MapGUI::on_maidenhead_clicked);
QObject::connect(ui->save, &QToolButton::clicked, this, &MapGUI::on_save_clicked);
QObject::connect(ui->deleteAll, &QToolButton::clicked, this, &MapGUI::on_deleteAll_clicked);
QObject::connect(ui->displaySettings, &QToolButton::clicked, this, &MapGUI::on_displaySettings_clicked);
QObject::connect(ui->mapTypes, qOverload<int>(&QComboBox::currentIndexChanged), this, &MapGUI::on_mapTypes_currentIndexChanged);

Wyświetl plik

@ -24,6 +24,7 @@
#include <QQuickWidget>
#include <QTextEdit>
#include <QJsonObject>
#include <QFileDialog>
#ifdef QT_WEBENGINE_FOUND
#include <QWebEngineFullScreenRequest>
#include <QWebEnginePage>
@ -194,6 +195,7 @@ private:
RollupState m_rollupState;
bool m_doApplySettings;
AvailableChannelOrFeatureList m_availableChannelOrFeatures;
QFileDialog m_fileDialog;
Map* m_map;
MessageQueue m_inputMessageQueue;
@ -217,6 +219,8 @@ private:
MapTileServer *m_mapTileServer;
QTimer m_redrawMapTimer;
GIRO *m_giro;
QDateTime m_giroDateTime;
QString m_giroRunId;
QHash<QString, IonosondeStation *> m_ionosondeStations;
QSharedPointer<const QList<NavAid *>> m_navAids;
QSharedPointer<const QList<Airspace *>> m_airspaces;
@ -279,6 +283,7 @@ private:
void openKiwiSDR(const QString& url);
void openSpyServer(const QString& url);
QString formatFrequency(qint64 frequency) const;
void updateGIRO(const QDateTime& mapDateTime);
static QString getDataDir();
static const QList<RadioTimeTransmitter> m_radioTimeTransmitters;
@ -315,6 +320,7 @@ private slots:
void on_layersMenu_clicked();
void on_find_returnPressed();
void on_maidenhead_clicked();
void on_save_clicked();
void on_deleteAll_clicked();
void on_displaySettings_clicked();
void on_mapTypes_currentIndexChanged(int index);
@ -330,10 +336,14 @@ private slots:
void renderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode);
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
void loadingChanged(const QWebEngineLoadingInfo &loadingInfo);
void downloadRequested(QWebEngineDownloadRequest *download);
#else
void downloadRequested(QWebEngineDownloadItem *download);
#endif
#endif
void statusChanged(QQuickWidget::Status status);
void preferenceChanged(int elementType);
void giroIndexUpdated(const QList<GIRO::DataSet>& data);
void giroDataUpdated(const GIRO::GIROStationData& data);
void mufUpdated(const QJsonDocument& document);
void foF2Updated(const QJsonDocument& document);

Wyświetl plik

@ -424,6 +424,20 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="save">
<property name="toolTip">
<string>Save to .kml</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/save.png</normaloff>:/save.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="deleteAll">
<property name="toolTip">

Wyświetl plik

@ -96,6 +96,11 @@ void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
updateTrack(mapItem->getTrack());
updatePredictedTrack(mapItem->getPredictedTrack());
}
if (mapItem->getAvailableFrom()) {
m_availableFrom = QDateTime::fromString(*mapItem->getAvailableFrom(), Qt::ISODateWithMs);
} else {
m_availableFrom = QDateTime();
}
if (mapItem->getAvailableUntil()) {
m_availableUntil = QDateTime::fromString(*mapItem->getAvailableUntil(), Qt::ISODateWithMs);
} else {

Wyświetl plik

@ -61,6 +61,7 @@ protected:
float m_latitude; // Position for label
float m_longitude;
float m_altitude; // In metres
QDateTime m_availableFrom; // Date & time this item is visible from. Invalid date/time is forever
QDateTime m_availableUntil; // Date & time this item is visible until (for 3D map). Invalid date/time is forever
};

Wyświetl plik

@ -41,6 +41,7 @@ const QStringList MapSettings::m_pipeTypes = {
QStringLiteral("Radiosonde"),
QStringLiteral("StarTracker"),
QStringLiteral("SatelliteTracker"),
QStringLiteral("SID"),
QStringLiteral("VORLocalizer")
};
@ -57,6 +58,7 @@ const QStringList MapSettings::m_pipeURIs = {
QStringLiteral("sdrangel.feature.radiosonde"),
QStringLiteral("sdrangel.feature.startracker"),
QStringLiteral("sdrangel.feature.satellitetracker"),
QStringLiteral("sdrangel.feature.sid"),
QStringLiteral("sdrangel.feature.vorlocalizer")
};
@ -125,6 +127,7 @@ MapSettings::MapSettings() :
stationSettings->m_display3DTrack = false;
m_itemSettings.insert("Station", stationSettings);
m_itemSettings.insert("VORLocalizer", new MapItemSettings("VORLocalizer", true, QColor(255, 255, 0), false, true, 11));
m_itemSettings.insert("SID", new MapItemSettings("SID", true, QColor(255, 255, 0), false, true, 3));
MapItemSettings *ionosondeItemSettings = new MapItemSettings("Ionosonde Stations", true, QColor(255, 255, 0), false, true, 4);
ionosondeItemSettings->m_display2DIcon = false;

Wyświetl plik

@ -28,66 +28,10 @@
#endif
#include "util/units.h"
#include "gui/colordialog.h"
#include "mapsettingsdialog.h"
#include "maplocationdialog.h"
#include "mapcolordialog.h"
static QString rgbToColor(quint32 rgb)
{
QColor color = QColor::fromRgba(rgb);
return QString("%1,%2,%3").arg(color.red()).arg(color.green()).arg(color.blue());
}
static QString backgroundCSS(quint32 rgb)
{
// Must specify a border, otherwise we end up with a gradient instead of solid background
return QString("QToolButton { background-color: rgb(%1); border: none; }").arg(rgbToColor(rgb));
}
static QString noColorCSS()
{
return "QToolButton { background-color: black; border: none; }";
}
MapColorGUI::MapColorGUI(QTableWidget *table, int row, int col, bool noColor, quint32 color) :
m_noColor(noColor),
m_color(color)
{
m_colorButton = new QToolButton(table);
m_colorButton->setFixedSize(22, 22);
if (!m_noColor)
{
m_colorButton->setStyleSheet(backgroundCSS(m_color));
}
else
{
m_colorButton->setStyleSheet(noColorCSS());
m_colorButton->setText("-");
}
table->setCellWidget(row, col, m_colorButton);
connect(m_colorButton, &QToolButton::clicked, this, &MapColorGUI::on_color_clicked);
}
void MapColorGUI::on_color_clicked()
{
MapColorDialog dialog(QColor::fromRgba(m_color), m_colorButton);
if (dialog.exec() == QDialog::Accepted)
{
m_noColor = dialog.noColorSelected();
if (!m_noColor)
{
m_colorButton->setText("");
m_color = dialog.selectedColor().rgba();
m_colorButton->setStyleSheet(backgroundCSS(m_color));
}
else
{
m_colorButton->setText("-");
m_colorButton->setStyleSheet(noColorCSS());
}
}
}
MapItemSettingsGUI::MapItemSettingsGUI(QTableWidget *table, int row, MapSettings::MapItemSettings *settings) :
m_track2D(table, row, MapSettingsDialog::COL_2D_TRACK, !settings->m_display2DTrack, settings->m_2DTrackColor),

Wyświetl plik

@ -27,6 +27,7 @@
#include <QProgressDialog>
#include "gui/httpdownloadmanagergui.h"
#include "gui/tablecolorchooser.h"
#include "util/openaip.h"
#include "util/ourairportsdb.h"
#include "util/waypoints.h"
@ -34,34 +35,15 @@
#include "ui_mapsettingsdialog.h"
#include "mapsettings.h"
class MapColorGUI : public QObject {
Q_OBJECT
public:
MapColorGUI(QTableWidget *table, int row, int col, bool noColor, quint32 color);
public slots:
void on_color_clicked();
private:
QToolButton *m_colorButton;
public:
// Have copies of settings, so we don't change unless main dialog is accepted
bool m_noColor;
quint32 m_color;
};
class MapItemSettingsGUI : public QObject {
Q_OBJECT
public:
MapItemSettingsGUI(QTableWidget *table, int row, MapSettings::MapItemSettings *settings);
MapColorGUI m_track2D;
MapColorGUI m_point3D;
MapColorGUI m_track3D;
TableColorChooser m_track2D;
TableColorChooser m_point3D;
TableColorChooser m_track3D;
QSpinBox *m_minZoom;
QSpinBox *m_minPixels;
QDoubleSpinBox *m_labelScale;

Wyświetl plik

@ -14,10 +14,11 @@ On top of this, it can plot data from other plugins, such as:
* Weather balloons from the Radiosonde feature,
* RF Heat Maps from the Heap Map channel,
* Radials and estimated position from the VOR localizer feature,
* ILS course line and glide path from the ILS Demodulator.
* DSC geographic call areas.
* ILS course line and glide path from the ILS Demodulator,
* DSC geographic call areas,
* SID paths.
As well as internet data sources:
As well as internet and built-in data sources:
* AM, FM and DAB transmitters in the UK and DAB transmitters in France,
* Airports, NavAids and airspaces,
@ -40,7 +41,7 @@ It can also create tracks showing the path aircraft, ships, radiosondes and APRS
![3D Map feature](../../../doc/img/Map_plugin_apt.png)
3D Models are not included with SDRangel. They must be downloaded by pressing the Download 3D Models button in the Display Settings dialog (20).
3D Models are not included with SDRangel. They must be downloaded by pressing the Download 3D Models button in the Display Settings dialog (21).
<h2>Interface</h2>
@ -143,14 +144,14 @@ This is only supported on 2D raster maps and the 3D map.
<h3>11: Display MUF Contours</h3>
When checked, contours will be downloaded and displayed on the 3D map, showing the MUF (Maximum Usable Frequency) for a 3000km path that reflects off the ionosphere.
The contours will be updated every 15 minutes. The latest contour data will always be displayed, irrespective of the time set on the 3D Map.
The contours will be updated every 15 minutes. MUF contour data is available for the preceeding 5 days.
![MUF contours](../../../doc/img/Map_plugin_muf.png)
<h3>12: Display coF2 Contours</h3>
When checked, contours will be downloaded and displayed on the 3D map, showing coF2 (F2 layer critical frequency), the maximum frequency at which radio waves will be reflected vertically from the F2 region of the ionosphere.
The contours will be updated every 15 minutes. The latest contour data will always be displayed, irrespective of the time set on the 3D Map.
The contours will be updated every 15 minutes. coF2 contour data is available for the preceeding 5 days.
<h3>13: Display NASA GIBS Data</h3>
@ -185,11 +186,19 @@ When checked, displays the track (taken or predicted) for the selected object.
When checked, displays the track (taken or predicted) for the all objects.
<h3>19: Delete</h3>
<h3>19: Save to .kml</h3>
When clicked, items and tracks on the map will be saved to a [KML](https://en.wikipedia.org/wiki/Keyhole_Markup_Language) (.kml or .kmz) file, for use in other applications.
Note that the KML format requires 3D models in the Collada (.dae) format. However, SDRangel's models are in glTF (.glb or .gltf) format.
If you wish to view the models in a KML viewer, you will need to manually convert them. Note that you should still be able to view tracks without the models.
Note that the .glbe files cannot be converted to .dae.
<h3>20: Delete</h3>
When clicked, all items will be deleted from the map.
<h3>20: Display settings</h3>
<h3>21: Display settings</h3>
When clicked, opens the Map Display Settings dialog:
@ -281,6 +290,17 @@ MUF and foF2 can be displayed as contours:
The contours can be clicked on which will display the data for that contour in the info box.
<h4>VLF Transmitters</h4>
The Map contains a built-in list of VLF transmitters. This can be overridden by a user-defined list contained in a file `vlftransmitters.csv` in the application data directory.
The file must have the following columns:
```
Callsign,Frequency,Latitude,Longitude,Power
GQD,19580,54.911643,-3.278456,10
```
<h2>Attribution</h2>
IARU Region 1 beacon list used with permission from: https://iaru-r1-c5-beacons.org/ To add or update a beacon, see: https://iaru-r1-c5-beacons.org/index.php/beacon-update/

Wyświetl plik

@ -53,7 +53,7 @@ void WebServer::incomingConnection(qintptr socket)
// Don't include leading or trailing / in from
void WebServer::addPathSubstitution(const QString &from, const QString &to)
{
qDebug() << "Mapping " << from << " to " << to;
//qDebug() << "Mapping " << from << " to " << to;
m_pathSubstitutions.insert(from, to);
}
@ -125,7 +125,7 @@ void WebServer::readClient()
if (socket->canReadLine())
{
QString line = socket->readLine();
qDebug() << "WebServer HTTP Request: " << line;
//qDebug() << "WebServer HTTP Request: " << line;
QStringList tokens = QString(line).split(QRegularExpression("[ \r\n][ \r\n]*"));
if (tokens[0] == "GET")

Wyświetl plik

@ -197,6 +197,13 @@ void SatelliteSelectionDialog::displaySatInfo(const QString& name)
{
SatNogsSatellite *sat = m_satellites[name];
m_satInfo = sat;
if (!sat)
{
// Might not be null if satellite name entered via API
ui->satInfo->setText("");
ui->satImage->setPixmap(QPixmap());
return;
}
QStringList info;
info.append(QString("Name: %1").arg(sat->m_name));
if (sat->m_names.size() > 0)

Wyświetl plik

@ -26,7 +26,7 @@
#include "satellitetrackersettings.h"
#define DEAFULT_TARGET "ISS"
#define DEFAULT_TLES {"https://db.satnogs.org/api/tle/", "https://www.amsat.org/tle/current/nasabare.txt", "http://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=tle", "https://celestrak.org/NORAD/elements/gp.php?GROUP=gps-ops&FORMAT=tle"}
#define DEFAULT_TLES {"https://db.satnogs.org/api/tle/", "https://www.amsat.org/tle/current/nasabare.txt", "http://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=tle", "https://celestrak.org/NORAD/elements/gp.php?GROUP=gps-ops&FORMAT=tle", "https://celestrak.org/NORAD/elements/gp.php?CATNR=36395&FORMAT=tle"}
#define DEFAULT_DATE_FORMAT "yyyy/MM/dd"
#define DEFAULT_AOS_SPEECH "${name} is visible for ${duration} minutes. Max elevation, ${elevation} degrees."
#define DEFAULT_LOS_SPEECH "${name} is no longer visible."

Wyświetl plik

@ -0,0 +1,72 @@
project(sid)
set(sid_SOURCES
sid.cpp
sidsettings.cpp
sidplugin.cpp
sidwebapiadapter.cpp
sidworker.cpp
)
set(sid_HEADERS
sid.h
sidsettings.h
sidplugin.h
sidwebapiadapter.h
sidworker.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(sid_SOURCES
${sid_SOURCES}
sidgui.cpp
sidgui.ui
sidsettingsdialog.cpp
sidsettingsdialog.ui
icons.qrc
)
set(sid_HEADERS
${sid_HEADERS}
sidgui.h
sidsettingsdialog.h
)
set(TARGET_NAME featuresid)
set(TARGET_LIB Qt::Widgets Qt::Charts Qt::Multimedia Qt::MultimediaWidgets)
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME featuresidsrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${sid_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
if(WIN32)
# Run deployqt for MultimediaWidgets, which isn't used in other plugins
include(DeployQt)
windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} "")
endif()
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

Wyświetl plik

@ -0,0 +1,16 @@
<RCC>
<qresource prefix="/sid/">
<file>icons/sun.png</file>
<file>icons/chartcombined.png</file>
<file>icons/chartseparate.png</file>
<file>icons/legend.png</file>
<file>icons/xlp.svg</file>
<file>icons/xls.svg</file>
<file>icons/xsp.svg</file>
<file>icons/xss.svg</file>
<file>icons/delta.svg</file>
<file>icons/gamma.svg</file>
<file>icons/proton.svg</file>
<file>icons/solar-orbiter.svg</file>
</qresource>
</RCC>

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 209 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 252 B

Wyświetl plik

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 15.1 13" style="enable-background:new 0 0 15.1 13;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<path id="MJX-66-TEX-N-394" class="st0" d="M0.9-13c-0.1,0-0.1,0.1-0.1,0.1c0,0,1,2.1,3.1,6.3S7-0.2,7-0.1C7.1,0,7.2,0,7.5,0
C7.8,0,8,0,8-0.1c0,0,1.1-2.2,3.1-6.4s3.1-6.3,3.1-6.3c0,0,0-0.1-0.1-0.1H0.9z M9.2-6.7L7-2.2l-4.5-9.1l4.5,0h4.5
C11.4-11.3,10.7-9.8,9.2-6.7z"/>
</g>
</g>
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 779 B

Wyświetl plik

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 11.7 13.8" style="enable-background:new 0 0 11.7 13.8;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<path id="MJX-56-TEX-I-1D6FE" class="st0" d="M1.3-4.4c-0.2,0-0.4,0.1-0.4,0.2C0.9-4,1-3.7,1.2-3.4s0.4,0.7,0.7,1.1
s0.7,0.7,1.1,1s1,0.4,1.4,0.4c0.3,0,0.5,0,0.6,0c0.6-0.1,1.1-0.5,1.4-1s0.7-1.3,1-2.4C7.6-5,7.7-5.5,7.7-5.8c0-0.1,0-0.1,0-0.1
l0.2,0.5c0.2,0.6,0.5,1.3,0.8,1.9s0.6,1.2,0.8,1.6s0.3,0.7,0.4,0.7c0,0,0.1,0,0.3,0h0.2c0.1-0.1,0.1-0.1,0.1-0.2
c0,0-0.1-0.3-0.4-0.8c-0.2-0.3-0.6-1.1-1-2.1c-0.5-1-0.9-2.1-1.3-3.3C7.7-7.8,7.6-8.2,7.5-8.8C7.4-9.4,7.3-10,7.2-10.7
C7-11.5,6.8-12,6.7-12.3s-0.3-0.5-0.5-0.5s-0.3,0.2-0.3,0.5c0,0.6,0.3,1.9,1,4L7-8v0.3c0,0.1,0,0.3,0,0.6c0,1.9-0.3,3.2-1,3.9
C5.5-2.7,4.9-2.5,4.2-2.5c-0.5,0-1.1-0.1-1.5-0.4s-0.8-0.7-1-1.2c0-0.1,0-0.2-0.1-0.2C1.5-4.4,1.4-4.4,1.3-4.4z"/>
</g>
</g>
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.2 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 265 B

Wyświetl plik

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20.6 18.4" style="enable-background:new 0 0 20.6 18.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-5-TEX-I-1D45D" class="st0" d="M0.4-9.7c0,0,0,0.1,0,0.1c0,0.1,0,0.2,0.1,0.4c0,0.2,0.1,0.4,0.2,0.6S0.9-8.3,1-8
c0.1,0.2,0.2,0.4,0.4,0.5s0.3,0.3,0.5,0.4C2-7,2.2-6.9,2.4-6.9c0.9,0,1.5-0.4,1.7-1.2l0.2,0.2c0.7,0.7,1.5,1,2.1,1
c0.8,0,1.4-0.3,1.8-0.9C8.8-8.4,9-9,9-9.9c0-1.3-0.5-2.5-1.4-3.6s-2-1.7-3.1-1.7c-0.3,0-0.5,0-0.7,0.1
c-0.1,0.1-0.3,0.2-0.4,0.3c-0.1,0.1-0.3,0.2-0.3,0.3l-0.1,0.1c0,0-0.1-0.5-0.4-1.6s-0.4-1.6-0.4-1.6c0-0.1,0-0.1,0.1-0.1
c0.1,0,0.3,0,0.7-0.1h0.5c0.1-0.1,0.1-0.1,0.1-0.2c0,0,0-0.1-0.1-0.3c0-0.1-0.1-0.2-0.1-0.3c0,0-0.1-0.1-0.2-0.1
c0,0-0.1,0-0.2,0s-0.3,0-0.6,0s-0.7,0-1.1,0c-0.8,0-1.4,0-1.6,0h-0.1c-0.1,0.1-0.1,0.2-0.1,0.2c0,0.3,0.1,0.5,0.2,0.6h0.4
c0.4,0,0.6,0.1,0.7,0.2c0,0.1,0.4,1.5,1.1,4.3s1.1,4.3,1.1,4.5c0,0.1,0,0.1,0,0.3c0,0.5-0.2,0.7-0.5,0.7
c-0.3,0-0.5-0.2-0.7-0.5C1.5-8.5,1.4-8.8,1.3-9.2C1.2-9.6,1.1-9.8,1.1-9.9c0,0-0.1,0-0.3,0H0.5C0.5-9.8,0.4-9.8,0.4-9.7z
M3.2-13.1c0.3-0.9,0.7-1.4,1.3-1.4c0.4,0,0.7,0.1,1,0.4c0.3,0.3,0.6,0.6,0.8,1c0.2,0.4,0.4,1.1,0.7,2s0.3,1.6,0.3,2v0.1
c0,0.9-0.4,1.3-1.1,1.3c-0.1,0-0.3,0-0.4-0.1c-0.1,0-0.3-0.1-0.4-0.2C5.4-7.9,5.3-8,5.2-8.1C5.1-8.1,5-8.2,4.9-8.3
C4.8-8.4,4.7-8.5,4.6-8.6C4.5-8.7,4.4-8.8,4.4-8.9S4.3-9,4.3-9L4.2-9.1c0,0,0-0.1-0.1-0.3c0-0.2-0.1-0.4-0.2-0.8
c-0.1-0.4-0.2-0.7-0.3-0.9C3.4-12.4,3.2-13,3.2-13.1z"/>
</g>
</g>
<g transform="translate(536,413) scale(0.707)">
<g>
<path id="MJX-5-TEX-N-2B" class="st0" d="M-743.4-590.4c0,0,0,0.1,0,0.2c0,0.2,0.1,0.3,0.3,0.4h5.4v2.7l0,2.7
c0.1,0.2,0.2,0.2,0.3,0.2c0.2,0,0.3-0.1,0.4-0.3v-5.4h5.4c0.2-0.1,0.3-0.2,0.3-0.4s-0.1-0.3-0.3-0.4h-5.4v-5.4
c-0.1-0.2-0.2-0.3-0.3-0.3h0h0c-0.1,0-0.3,0.1-0.3,0.3v5.4h-5.4c-0.2,0.1-0.3,0.2-0.3,0.4V-590.4z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.2 KiB

Wyświetl plik

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 89.49 83.83">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #fff;
stroke-miterlimit: 10;
stroke-width: 3px;
}
.cls-2 {
fill: #fff;
stroke-width: 0px;
}
</style>
</defs>
<circle class="cls-1" cx="35.5" cy="48.81" r="22.2"/>
<circle class="cls-2" cx="69.18" cy="16.79" r="6.33"/>
<rect class="cls-2" x="48.53" y="4.39" width="16.03" height="4.81" transform="translate(15.91 -33.1) rotate(37.62)"/>
<rect class="cls-2" x="73.67" y="24.72" width="16.03" height="4.81" transform="translate(33.54 -44.22) rotate(37.62)"/>
<rect class="cls-2" x="52.72" y="69.63" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(63.37 -20.38) rotate(42.8)"/>
<rect class="cls-2" x="10.15" y="25.57" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(22.11 -3.19) rotate(42.8)"/>
<rect class="cls-2" x="8.33" y="68.52" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(-45.29 29.78) rotate(-45)"/>
<rect class="cls-2" x="52.72" y="27.33" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(-3.17 49.11) rotate(-45)"/>
<rect class="cls-2" x="0" y="47.77" width="9.94" height="2.08" rx="1.04" ry="1.04"/>
<rect class="cls-2" x="60.18" y="47.77" width="9.94" height="2.08" rx="1.04" ry="1.04"/>
<rect class="cls-2" x="32.21" y="17.72" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(18.42 55.94) rotate(-90)"/>
<rect class="cls-2" x="31.17" y="77.82" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(-42.72 115) rotate(-90)"/>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.7 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 414 B

Wyświetl plik

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 25.2 15.1" style="enable-background:new 0 0 25.2 15.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-38-TEX-I-1D44B" class="st0" d="M0.8-12.4L0.8-12.4c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0.1,0.3
c0,0.2,0.1,0.3,0.1,0.3c0,0,0.2,0,0.3,0.1c1,0,1.9,0.3,2.4,0.9c0.1,0.1,0.8,0.9,2.1,2.3s1.9,2.1,1.9,2.1
c-1.3,3.4-2,5.1-2.1,5.1C5.3-0.9,4.9-0.8,4.2-0.8H3.7C3.7-0.8,3.6-0.7,3.6-0.7s0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.3
c0.4,0,1.1,0,2.2,0C6.7,0,7,0,7.4,0S8,0,8.2,0s0.3,0,0.4,0c0.2,0,0.3-0.1,0.3-0.2c0,0,0-0.1,0-0.2c0-0.2-0.1-0.3-0.1-0.3
c0,0-0.1-0.1-0.3-0.1C8-0.9,7.7-1,7.4-1.1l1.4-3.5l1,1.1c1.3,1.4,1.9,2.1,1.9,2.3c0,0.2-0.1,0.4-0.4,0.5c-0.1,0-0.1,0-0.2,0
c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.2c0,0,0.2,0,0.5,0c0.3,0,0.6,0,1,0s0.6,0,0.7,0
c1.1,0,1.7,0,1.8,0.1h0.1c0.1-0.1,0.1-0.2,0.1-0.2c0-0.3-0.1-0.5-0.2-0.6h-0.3c-0.4,0-0.8-0.1-1.1-0.2s-0.6-0.2-0.7-0.3
c-0.2-0.1-0.3-0.2-0.4-0.3l-0.2-0.2c0,0-0.6-0.6-1.7-1.9L9.1-5.4c0,0,0.2-0.5,0.6-1.4s0.8-1.9,1.2-2.9c0.4-1,0.6-1.5,0.7-1.6
c0.1-0.1,0.5-0.2,1.1-0.2c0.4,0,0.6-0.1,0.6-0.2c0,0,0-0.1,0-0.3c0-0.2-0.1-0.3-0.1-0.3s-0.1-0.1-0.3-0.1c0,0-0.2,0-0.6,0
s-0.9,0-1.6,0c-0.7,0-1.3,0-1.7,0s-0.6,0-0.6,0c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.1,0,0.2,0.1,0.3s0.1,0.1,0.1,0.1
s0.1,0,0.2,0c0.1,0,0.2,0,0.3,0c0.1,0,0.3,0,0.5,0.1c0.2,0.1,0.3,0.2,0.3,0.2c0,0-0.3,0.7-0.9,2.1L7.8-7
c-2.3-2.5-3.4-3.8-3.5-3.9c0-0.1-0.1-0.2-0.1-0.2c0-0.2,0.2-0.4,0.5-0.5c0,0,0,0,0.1,0s0.1,0,0.1,0c0,0,0.1,0,0.1,0s0,0,0.1,0
c0,0,0,0,0.1-0.1s0-0.1,0-0.1c0-0.1,0-0.2,0-0.3c0-0.2-0.1-0.2-0.1-0.3c0,0-0.1,0-0.3-0.1c0,0-0.1,0-0.3,0s-0.4,0-0.8,0
s-0.7,0-1.1,0C1.6-12.3,1-12.3,0.8-12.4z"/>
</g>
</g>
<g transform="translate(861,-150) scale(0.707)">
<g>
<path id="MJX-38-TEX-I-1D43F" class="st0" d="M-1191.6,202.4c-0.4,0-0.6,0-0.7,0.1c0,0,0,0.1,0,0.1c0,0.3,0.1,0.5,0.2,0.6
c0,0,0.1,0,0.3,0c0.7,0,1.4-0.1,2.3-0.1c1.7,0,2.7,0,2.9,0.1h0.2c0.1-0.1,0.1-0.1,0.1-0.2c0,0,0-0.1,0-0.3
c-0.1-0.2-0.1-0.3-0.2-0.3h-0.7c-0.8,0-1.3-0.1-1.5-0.2c-0.1,0-0.2-0.2-0.3-0.5c-0.1-0.3-0.5-1.9-1.2-4.8
c-0.2-0.8-0.4-1.6-0.6-2.5c-0.2-0.9-0.4-1.5-0.5-2l-0.2-0.7c0,0,0.1-0.1,0.2-0.1c0.1,0,0.5,0,1.1,0h0.3c0.4,0,0.7,0,0.9,0
c0.2,0,0.5,0.1,0.9,0.1c0.4,0.1,0.7,0.2,0.9,0.3c0.2,0.1,0.5,0.3,0.8,0.6s0.5,0.6,0.8,1c0.2,0.4,0.4,0.8,0.5,1.2
s0.3,0.7,0.3,0.8c0.1,0,0.2,0.1,0.3,0.1h0.2c0.1-0.1,0.1-0.2,0.1-0.2s-0.1-0.4-0.4-1.1c-0.2-0.7-0.5-1.4-0.8-2.2
s-0.4-1.2-0.5-1.3c0-0.1,0-0.1-0.1-0.1s-0.1,0-0.3-0.1s-0.4,0-0.8,0c-0.1,0-0.6,0-1.4,0s-1.5,0-2.2,0h-3.2
c-1,0-1.5,0.1-1.5,0.2c0,0.1,0,0.2,0,0.3c0.1,0.2,0.1,0.4,0.2,0.4c0,0,0.1,0,0.3,0h0.1c0.3,0,0.7,0,1.1,0.1
c0.2,0,0.3,0.1,0.3,0.2c0,0,0.5,1.7,1.3,5c0.8,3.3,1.2,5.1,1.2,5.2C-1190.6,202.3-1191,202.3-1191.6,202.4z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.2 KiB

Wyświetl plik

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 25.2 19.1" style="enable-background:new 0 0 25.2 19.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-39-TEX-I-1D44B" class="st0" d="M0.8-14.6L0.8-14.6c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0.1,0.3
c0,0.2,0.1,0.3,0.1,0.3c0,0,0.2,0,0.3,0.1c1,0,1.9,0.3,2.4,0.9c0.1,0.1,0.8,0.9,2.1,2.3s1.9,2.1,1.9,2.1
c-1.3,3.4-2,5.1-2.1,5.1C5.3-3.2,4.9-3.1,4.2-3.1H3.7C3.7-3,3.6-3,3.6-3c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.3
c0.4,0,1.1,0,2.2,0c0.4,0,0.8,0,1.1,0s0.6,0,0.8,0c0.2,0,0.3,0,0.4,0c0.2,0,0.3-0.1,0.3-0.2c0,0,0-0.1,0-0.2
c0-0.2-0.1-0.3-0.1-0.3c0,0-0.1-0.1-0.3-0.1c-0.4,0-0.7-0.1-1-0.3l1.4-3.5l1,1.1c1.3,1.4,1.9,2.1,1.9,2.3
c0,0.2-0.1,0.4-0.4,0.5c-0.1,0-0.1,0-0.2,0c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.2c0,0,0.2,0,0.5,0
c0.3,0,0.6,0,1,0c0.4,0,0.6,0,0.7,0c1.1,0,1.7,0,1.8,0.1h0.1c0.1-0.1,0.1-0.2,0.1-0.2c0-0.3-0.1-0.5-0.2-0.6h-0.3
c-0.4,0-0.8-0.1-1.1-0.2c-0.3-0.1-0.6-0.2-0.7-0.3c-0.2-0.1-0.3-0.2-0.4-0.3l-0.2-0.2c0,0-0.6-0.6-1.7-1.9L9.1-7.7
c0,0,0.2-0.5,0.6-1.4c0.4-1,0.8-1.9,1.2-2.9s0.6-1.5,0.7-1.6c0.1-0.1,0.5-0.2,1.1-0.2c0.4,0,0.6-0.1,0.6-0.2c0,0,0-0.1,0-0.3
c0-0.2-0.1-0.3-0.1-0.3c0,0-0.1-0.1-0.3-0.1c0,0-0.2,0-0.6,0c-0.4,0-0.9,0-1.6,0c-0.7,0-1.3,0-1.7,0c-0.4,0-0.6,0-0.6,0
c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.1,0,0.2,0.1,0.3c0,0,0.1,0.1,0.1,0.1c0,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3,0
c0.1,0,0.3,0,0.5,0.1c0.2,0.1,0.3,0.2,0.3,0.2c0,0-0.3,0.7-0.9,2.1L7.8-9.2c-2.3-2.5-3.4-3.8-3.5-3.9c0-0.1-0.1-0.2-0.1-0.2
c0-0.2,0.2-0.4,0.5-0.5c0,0,0,0,0.1,0s0.1,0,0.1,0c0,0,0.1,0,0.1,0s0,0,0.1,0c0,0,0,0,0.1-0.1c0,0,0-0.1,0-0.1
c0-0.1,0-0.2,0-0.3c0-0.2-0.1-0.2-0.1-0.3c0,0-0.1,0-0.3-0.1c0,0-0.1,0-0.3,0c-0.2,0-0.4,0-0.8,0c-0.3,0-0.7,0-1.1,0
C1.6-14.6,1-14.6,0.8-14.6z"/>
</g>
</g>
<g transform="translate(936.2,413) scale(0.707)">
<g>
<path id="MJX-39-TEX-V-2032" class="st0" d="M-1298.8-593.5c-0.1,0-0.2,0-0.5,0.1c-0.3,0.1-0.4,0.1-0.4,0.2
c0,0.1,0.3,1.5,1,4.2c0.7,2.7,1,4.1,1.1,4.3c0.2,0.4,0.5,0.6,0.9,0.6c0.2,0,0.5-0.1,0.8-0.3s0.4-0.4,0.4-0.8c0-0.1,0-0.2,0-0.3
c0-0.1-0.5-1.4-1.6-4C-1298.1-592.1-1298.7-593.5-1298.8-593.5C-1298.7-593.5-1298.8-593.5-1298.8-593.5z"/>
</g>
</g>
<g transform="translate(861,-247) scale(0.707)">
<g>
<path id="MJX-39-TEX-I-1D43F" class="st0" d="M-1191.7,333.9c-0.4,0-0.6,0-0.7,0.1c0,0,0,0.1,0,0.1c0,0.3,0.1,0.5,0.2,0.6
c0,0,0.1,0,0.3,0c0.7,0,1.4-0.1,2.3-0.1c1.7,0,2.7,0,2.9,0.1h0.2c0.1-0.1,0.1-0.1,0.1-0.2s0-0.1,0-0.3
c-0.1-0.2-0.1-0.3-0.2-0.3h-0.7c-0.8,0-1.3-0.1-1.5-0.2c-0.1,0-0.2-0.2-0.3-0.5c-0.1-0.3-0.5-1.9-1.2-4.8
c-0.2-0.8-0.4-1.6-0.6-2.5c-0.2-0.9-0.4-1.5-0.5-2l-0.2-0.7c0,0,0.1-0.1,0.2-0.1c0.1,0,0.5,0,1.1,0h0.3c0.4,0,0.7,0,0.9,0
c0.2,0,0.5,0.1,0.9,0.1s0.7,0.2,0.9,0.3c0.2,0.1,0.5,0.3,0.8,0.6s0.5,0.6,0.8,1c0.2,0.4,0.4,0.8,0.5,1.2
c0.2,0.4,0.3,0.7,0.3,0.8c0.1,0,0.2,0.1,0.3,0.1h0.2c0.1-0.1,0.1-0.2,0.1-0.2s-0.1-0.4-0.4-1.1s-0.5-1.4-0.8-2.2
s-0.4-1.2-0.5-1.3c0-0.1,0-0.1-0.1-0.1c0,0-0.1,0-0.3-0.1c-0.2,0-0.4,0-0.8,0c-0.1,0-0.6,0-1.4,0c-0.8,0-1.5,0-2.2,0h-3.2
c-1,0-1.5,0.1-1.5,0.2c0,0.1,0,0.2,0,0.3c0.1,0.2,0.1,0.4,0.2,0.4c0,0,0.1,0,0.3,0h0.1c0.3,0,0.7,0,1.1,0.1
c0.2,0,0.3,0.1,0.3,0.2c0,0,0.5,1.7,1.3,5c0.8,3.3,1.2,5,1.2,5.2C-1190.6,333.8-1191,333.8-1191.7,333.9z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.7 KiB

Wyświetl plik

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24.7 15.4" style="enable-background:new 0 0 24.7 15.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-40-TEX-I-1D44B" class="st0" d="M0.8-12.4L0.8-12.4c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0.1,0.3
c0,0.2,0.1,0.3,0.1,0.3c0,0,0.2,0,0.3,0.1c1,0,1.9,0.3,2.4,0.9c0.1,0.1,0.8,0.9,2.1,2.3s1.9,2.1,1.9,2.1
c-1.3,3.4-2,5.1-2.1,5.1C5.3-0.9,4.9-0.8,4.2-0.8H3.7C3.7-0.8,3.6-0.7,3.6-0.7c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.3
c0.4,0,1.1,0,2.2,0C6.7,0,7,0,7.4,0C7.7,0,8,0,8.2,0c0.2,0,0.3,0,0.4,0c0.2,0,0.3-0.1,0.3-0.2c0,0,0-0.1,0-0.2
c0-0.2-0.1-0.3-0.1-0.3S8.6-0.8,8.4-0.8C8-0.9,7.7-1,7.4-1.1l1.4-3.5l1,1.1c1.3,1.4,1.9,2.1,1.9,2.3c0,0.2-0.1,0.4-0.4,0.5
c-0.1,0-0.1,0-0.2,0c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.2c0,0,0.2,0,0.5,0c0.3,0,0.6,0,1,0
c0.4,0,0.6,0,0.7,0c1.1,0,1.7,0,1.8,0.1h0.1c0.1-0.1,0.1-0.2,0.1-0.2c0-0.3-0.1-0.5-0.2-0.6h-0.3c-0.4,0-0.8-0.1-1.1-0.2
s-0.6-0.2-0.7-0.3c-0.2-0.1-0.3-0.2-0.4-0.3l-0.2-0.2c0,0-0.6-0.6-1.7-1.9L9.1-5.4c0,0,0.2-0.5,0.6-1.4s0.8-1.9,1.2-2.9
c0.4-1,0.6-1.5,0.7-1.6c0.1-0.1,0.5-0.2,1.1-0.2c0.4,0,0.6-0.1,0.6-0.2c0,0,0-0.1,0-0.3c0-0.2-0.1-0.3-0.1-0.3
c0,0-0.1-0.1-0.3-0.1c0,0-0.2,0-0.6,0c-0.4,0-0.9,0-1.6,0c-0.7,0-1.3,0-1.7,0c-0.4,0-0.6,0-0.6,0c-0.2,0-0.3,0.1-0.3,0.2
c0,0,0,0.1,0,0.3c0,0.1,0,0.2,0.1,0.3c0,0,0.1,0.1,0.1,0.1c0,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3,0c0.1,0,0.3,0,0.5,0.1
c0.2,0.1,0.3,0.2,0.3,0.2c0,0-0.3,0.7-0.9,2.1L7.8-7c-2.3-2.5-3.4-3.8-3.5-3.9c0-0.1-0.1-0.2-0.1-0.2c0-0.2,0.2-0.4,0.5-0.5
c0,0,0,0,0.1,0s0.1,0,0.1,0c0,0,0.1,0,0.1,0s0,0,0.1,0c0,0,0,0,0.1-0.1s0-0.1,0-0.1c0-0.1,0-0.2,0-0.3c0-0.2-0.1-0.2-0.1-0.3
c0,0-0.1,0-0.3-0.1c0,0-0.1,0-0.3,0c-0.2,0-0.4,0-0.8,0c-0.3,0-0.7,0-1.1,0C1.6-12.3,1-12.3,0.8-12.4z"/>
</g>
</g>
<g transform="translate(861,-150) scale(0.707)">
<g>
<path id="MJX-40-TEX-I-1D446" class="st0" d="M-1190.2,191.3c0.7,0,1.4,0.3,2,0.9c0.6,0.6,0.9,1.4,0.9,2.2
c0,0.8-0.3,1.3-0.9,1.6c-1.3,0.3-2.1,0.5-2.5,0.7c-0.4,0.1-0.6,0.2-0.8,0.4c-0.7,0.5-1.1,1.2-1.1,2.2c0,0.7,0.2,1.4,0.6,2
c0.4,0.6,0.8,1.1,1.2,1.4c0.3,0.3,0.8,0.5,1.3,0.7c0.5,0.2,0.9,0.3,1.2,0.3h0.2h0.1c1.2,0,2-0.4,2.5-1.2c0,0,0.1,0.1,0.2,0.2
s0.3,0.3,0.5,0.5s0.3,0.3,0.4,0.4c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0.2,0,0.2,0,0.2-0.1c0-0.1-0.2-0.8-0.5-2.2
c-0.3-1.4-0.5-2.1-0.6-2.1c0,0-0.1-0.1-0.3-0.1c-0.2,0-0.4,0.1-0.4,0.2c0,0.1,0,0.1,0,0.2c0,0,0,0.2,0,0.3c0,0.2,0,0.3,0,0.5
c0,0.4-0.1,0.8-0.2,1.1c-0.1,0.3-0.2,0.5-0.3,0.7c-0.1,0.1-0.3,0.3-0.5,0.5c-0.4,0.3-0.9,0.4-1.5,0.4c-0.7,0-1.3-0.3-1.9-0.8
c-0.6-0.6-0.9-1.2-0.9-1.9c0-0.4,0.1-0.7,0.3-0.9c0.2-0.3,0.4-0.5,0.7-0.6c0,0,0.5-0.1,1.3-0.3c0.8-0.2,1.2-0.3,1.2-0.3
c0.4-0.1,0.8-0.4,1.2-0.9s0.6-1,0.6-1.8c0-0.3,0-0.7-0.1-1c-0.1-0.3-0.2-0.6-0.3-0.9c-0.2-0.3-0.4-0.7-0.7-1.1
c-0.3-0.4-0.7-0.7-1.2-1.1c-0.5-0.3-1.1-0.6-1.7-0.7c-0.1,0-0.3,0-0.6,0c-1.2,0-2.1,0.3-2.8,1l-0.2,0.2l-0.5-0.6
c-0.3-0.3-0.4-0.5-0.5-0.6s-0.1-0.1-0.2-0.1c-0.2,0-0.2,0-0.2,0.1c0,0,0.3,1.5,1,4.3c0,0.1,0.1,0.1,0.4,0.1h0.2
c0.1-0.1,0.1-0.1,0.1-0.2c0,0,0-0.1,0-0.2c0-0.1,0-0.2-0.1-0.4c0-0.2,0-0.4,0-0.6c0-0.5,0.1-0.9,0.3-1.2
c0.2-0.3,0.5-0.6,0.8-0.7c0.3-0.2,0.6-0.3,0.9-0.3C-1190.9,191.3-1190.5,191.3-1190.2,191.3z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.6 KiB

Wyświetl plik

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24.7 19.4" style="enable-background:new 0 0 24.7 19.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-41-TEX-I-1D44B" class="st0" d="M0.8-14.6L0.8-14.6c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0.1,0.3
c0,0.2,0.1,0.3,0.1,0.3s0.2,0,0.3,0.1c1,0,1.9,0.3,2.4,0.9c0.1,0.1,0.8,0.9,2.1,2.3s1.9,2.1,1.9,2.1c-1.3,3.4-2,5.1-2.1,5.1
C5.3-3.2,4.9-3.1,4.2-3.1H3.7C3.7-3,3.6-3,3.6-3c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.3c0.4,0,1.1,0,2.2,0
c0.4,0,0.8,0,1.1,0s0.6,0,0.8,0c0.2,0,0.3,0,0.4,0c0.2,0,0.3-0.1,0.3-0.2c0,0,0-0.1,0-0.2c0-0.2-0.1-0.3-0.1-0.3
S8.6-3.1,8.4-3.1c-0.4,0-0.7-0.1-1-0.3l1.4-3.5l1,1.1c1.3,1.4,1.9,2.1,1.9,2.3c0,0.2-0.1,0.4-0.4,0.5c-0.1,0-0.1,0-0.2,0
c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.2c0,0,0.2,0,0.5,0s0.6,0,1,0c0.4,0,0.6,0,0.7,0
c1.1,0,1.7,0,1.8,0.1h0.1c0.1-0.1,0.1-0.2,0.1-0.2c0-0.3-0.1-0.5-0.2-0.6h-0.3c-0.4,0-0.8-0.1-1.1-0.2
c-0.3-0.1-0.6-0.2-0.7-0.3c-0.2-0.1-0.3-0.2-0.4-0.3l-0.2-0.2c0,0-0.6-0.6-1.7-1.9L9.1-7.7c0,0,0.2-0.5,0.6-1.4
c0.4-1,0.8-1.9,1.2-2.9c0.4-1,0.6-1.5,0.7-1.6c0.1-0.1,0.5-0.2,1.1-0.2c0.4,0,0.6-0.1,0.6-0.2c0,0,0-0.1,0-0.3
c0-0.2-0.1-0.3-0.1-0.3c0,0-0.1-0.1-0.3-0.1c0,0-0.2,0-0.6,0c-0.4,0-0.9,0-1.6,0c-0.7,0-1.3,0-1.7,0s-0.6,0-0.6,0
c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.1,0,0.2,0.1,0.3c0,0,0.1,0.1,0.1,0.1c0,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3,0
s0.3,0,0.5,0.1c0.2,0.1,0.3,0.2,0.3,0.2c0,0-0.3,0.7-0.9,2.1L7.8-9.2c-2.3-2.5-3.4-3.8-3.5-3.9c0-0.1-0.1-0.2-0.1-0.2
c0-0.2,0.2-0.4,0.5-0.5c0,0,0,0,0.1,0c0.1,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0c0,0,0,0,0.1-0.1c0,0,0-0.1,0-0.1
c0-0.1,0-0.2,0-0.3c0-0.2-0.1-0.2-0.1-0.3c0,0-0.1,0-0.3-0.1c0,0-0.1,0-0.3,0c-0.2,0-0.4,0-0.8,0c-0.3,0-0.7,0-1.1,0
C1.6-14.6,1-14.6,0.8-14.6z"/>
</g>
</g>
<g transform="translate(936.2,413) scale(0.707)">
<g>
<path id="MJX-41-TEX-V-2032" class="st0" d="M-1298.8-593.5c-0.1,0-0.2,0-0.5,0.1c-0.3,0.1-0.4,0.1-0.4,0.2
c0,0.1,0.3,1.5,1,4.2c0.7,2.7,1,4.1,1.1,4.3c0.2,0.4,0.5,0.6,0.9,0.6c0.2,0,0.5-0.1,0.8-0.3c0.3-0.2,0.4-0.4,0.4-0.8
c0-0.1,0-0.2,0-0.3c0-0.1-0.5-1.4-1.6-4C-1298.1-592.1-1298.7-593.5-1298.8-593.5C-1298.7-593.5-1298.8-593.5-1298.8-593.5z"/>
</g>
</g>
<g transform="translate(861,-247) scale(0.707)">
<g>
<path id="MJX-41-TEX-I-1D446" class="st0" d="M-1190.2,322.8c0.7,0,1.4,0.3,2,0.9c0.6,0.6,0.9,1.4,0.9,2.2
c0,0.8-0.3,1.3-0.9,1.6c-1.3,0.3-2.1,0.5-2.5,0.7c-0.4,0.1-0.6,0.2-0.8,0.4c-0.7,0.5-1.1,1.2-1.1,2.2c0,0.7,0.2,1.4,0.6,2
c0.4,0.6,0.8,1.1,1.2,1.4c0.3,0.3,0.8,0.5,1.3,0.7c0.5,0.2,0.9,0.3,1.2,0.3h0.2h0.1c1.2,0,2-0.4,2.5-1.2c0,0,0.1,0.1,0.2,0.2
c0.1,0.2,0.3,0.3,0.5,0.5c0.2,0.2,0.3,0.3,0.4,0.4c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0.2,0,0.2,0,0.2-0.1c0-0.1-0.2-0.8-0.5-2.2
c-0.3-1.4-0.5-2.1-0.6-2.1c0,0-0.1-0.1-0.3-0.1c-0.2,0-0.4,0.1-0.4,0.2c0,0.1,0,0.1,0,0.2c0,0,0,0.2,0,0.3s0,0.3,0,0.5
c0,0.4-0.1,0.8-0.2,1.1c-0.1,0.3-0.2,0.5-0.3,0.7c-0.1,0.1-0.3,0.3-0.5,0.5c-0.4,0.3-0.9,0.4-1.5,0.4c-0.7,0-1.3-0.3-1.9-0.8
c-0.6-0.6-0.9-1.2-0.9-1.9c0-0.4,0.1-0.7,0.3-0.9c0.2-0.3,0.4-0.5,0.7-0.6c0,0,0.5-0.1,1.3-0.3c0.8-0.2,1.2-0.3,1.2-0.3
c0.4-0.1,0.8-0.4,1.2-0.9c0.4-0.4,0.6-1,0.6-1.8c0-0.3,0-0.7-0.1-1c-0.1-0.3-0.2-0.6-0.3-0.9c-0.2-0.3-0.4-0.7-0.7-1.1
c-0.3-0.4-0.7-0.7-1.2-1.1s-1.1-0.6-1.7-0.7c-0.1,0-0.3,0-0.6,0c-1.2,0-2.1,0.3-2.8,1l-0.2,0.2l-0.5-0.6
c-0.3-0.3-0.4-0.5-0.5-0.6c-0.1,0-0.1-0.1-0.2-0.1c-0.2,0-0.2,0-0.2,0.1c0,0,0.3,1.5,1,4.3c0,0.1,0.1,0.1,0.4,0.1h0.2
c0.1-0.1,0.1-0.1,0.1-0.2c0,0,0-0.1,0-0.2c0-0.1,0-0.2-0.1-0.4c0-0.2,0-0.4,0-0.6c0-0.5,0.1-0.9,0.3-1.2
c0.2-0.3,0.5-0.6,0.8-0.7c0.3-0.2,0.6-0.3,0.9-0.3C-1190.9,322.8-1190.5,322.8-1190.2,322.8z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 4.1 KiB

Wyświetl plik

@ -0,0 +1,274 @@
<h1>SID Feature Plugin</h1>
<h2>Introduction</h2>
The SID feature displays a chart that plots channel power vs time, which can be useful for detecting solar flares, CMEs (Coronal Mass Ejections)
and GRBs (Gamma Ray Bursts) via SIDs (Sudden Ionospheric Disturbances).
The signal source for which power is measured should typically be a VLF signal from a distant transmitter with near constant day-time power output,
such as the VLF transmitters that are used by navies to communicate with submarines
(E.g. [GQD](https://en.wikipedia.org/wiki/Anthorn_Radio_Station) / [HWU](https://en.wikipedia.org/wiki/HWU_transmitter) / [NAA](https://en.wikipedia.org/wiki/VLF_Transmitter_Cutler)).
This can be measured within SDRangel using the [Channel Power](../../channelrx/channelpower/readme.md) plugin.
When a solar flare occurs, EUV (Extreme Ultraviolet) and X-ray radiation is emitted from the Sun. When the radiation reaches the Earth's atmosphere (after ~8 minutes),
it can increase the ionization of the D and E regions in the ionosphere, enhancing VLF propagation. Gamma-rays from powerful GRBs can also have a similar effect on the ionosphere.
The enhancement of the VLF sky-wave can interfere with the ground-wave, causing constructive or destructive interference depending on the phase. If strong enough, this can be observed
in the plot of received power vs time.
The SID chart can plot multiple series, allowing different signals from different transmitters to be monitored.
This can be useful as SIDs can be localized to specific regions in the atmosphere, thus not all signals may be affected.
Data can come from multiple [Channel Power](../../channelrx/channelpower/readme.md) plugins within a single device, or separate devices.
To help determine the cause of a SID, addtional data can be plotted from a variety of sources:
* the chart can plot X-ray data from the GOES satellites, to allow visual correlation of spikes in the X-ray flux measurement with spikes in the VLF power measurements,
* it can display images and video from the Solar Dynamics Observatory at EUV wavelengths, which may visually show the solar flare,
* 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](../../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)
<h2>Interface</h2>
![SID feature plugin GUI](../../../doc/img/SID_plugin_settings.png)
<h3>1: Start/stop</h3>
Press to start/stop collection and plotting of data.
<h3>2: Open .csv</h3>
Press to open a .csv file to read data from.
<h3>3: Save to .csv</h3>
Press to select a .csv file to write data to.
<h3>4: Save chart to image</h3>
Press to save the chart to a .png or .jpg file.
<h3>5: Clear all data</h3>
Press to clear all data.
<h3>6: Average</h3>
Number of samples to use in a moving average filter that can be applied to the data. Set to 1 for no filtering.
<h3>7: Display Primary Long Wavelength X-Ray Data</h3>
Check to display long wavelength (0.1-0.8nm) X-Ray data from the primary GOES satellite (Currently GOES 16) on the chart.
This is probably the most useful data in order to see when a solar flare has occured, as there will typically be a sharp peak.
The GOES satellites are in a geostationary orbit around the Earth, so the measured increase in X-ray flux from a flare will be approximately 8 minutes
after it has occured.
The Y-axis indicates the flare classification. M and X class flares are those most likely to have a measurable impact on the ionosphere.
![X-Ray data showing M class flare](../../../doc/img/SID_plugin_xray.png)
<h3>8: Display Secondary Long Wavelength X-Ray Data</h3>
Check to display long wavelength (0.1-0.8nm) X-Ray data from the secondary GOES satellite (Currently GOES 18) on the chart.
Data from the secondary satellite may be useful when the primary is unavailable, such as when it is in eclipse.
In the following plot we can see the primary and secondary data is nearly identical, apart from where there are dropouts
while in eclipse:
![X-Ray data during eclipse](../../../doc/img/SID_plugin_eclipse.png)
<h3>9: Display Primary Short Wavelength X-Ray Data</h3>
Check to display short wavelength (0.1-0.8nm) X-Ray data from the primary GOES satellite (Currently GOES 16) on the chart.
<h3>10: Display Secondary Short Wavelength X-Ray Data</h3>
Check to display short wavelength (0.05-0.4nm) X-Ray data from the secondary GOES satellite (Currently GOES 18) on the chart.
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 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 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.
<h3>12: Display GRBs</h3>
Check to display Gamma Ray Bursts (GRB) on the chart. GRBs are plotted as a scatter plot. You can right click on a GRB to display the context
menu, which contains a number of links to additional data from the Fermi satellite for the GRB. The GRB data is not realtime, and it may take
up to 7 days for a GRB to appear in the data, so this is typically only useful for the analysis of historical data.
The context menu also has an item to display the location of the GRB in the [Sky Map](../../feature/skymap/readme.md) feature.
<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 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.
<h3>14: Combined or Separate Charts</h3>
When unchecked, data from [Channel Power](../../channelrx/channelpower/readme.md) plugins is displayed on a separate chart to other data such as X-ray and proton flux and GRBs.
When checked, all data is displayed on a single combined chart.
<h3>15: Display Legend</h3>
Check to display a legend on the chart. When unchecked the legend will be hidden. You can click on items in the legend to temporarily hide and then show the corresponding series on the chart.
The position of the legend can be set in the Settings Dialog.
<h3>16: Open Settings Dialog</h3>
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 visible in the AIA 131 Å images.
<h3>18: Image or Video Selection</h3>
Selects whether to display images (unchecked) or video (checked).
<h3>19: Image/Wavelength Selection</h3>
Selects which image / wavelength to view.
* AIA 94 Å to 1700 Å - The AIA (Atmospheric Imaging Assembly) images the solar atmosphere at multiple EUV (Extreme Ultraviolet) and UV (Ultraviolet) wavelengths:
| Band | Region |
|---------|-----------------------------------------|
| 94 Å | Flaring |
| 131 Å | Flaring |
| 171 Å | Quiet corona, upper transition region |
| 193 Å | Corona and hot flare plasma |
| 211 Å | Active corona |
| 304 Å | Chromosphere, transition region |
| 335 Å | Active corona |
| 1600 Å | Transition region, upper photoshere |
| 1700 Å | Temperature minimum, photosphere |
[Ref](https://sdo.gsfc.nasa.gov/data/channels.php)
* MHI Magnetogram - HMI (Helioseismic and Magnetic Imager) Magnetogram shows the magnetic field in the photosphere, with black and white indicating opposite polarities.
* MHI Intensitygram - Brightness in a visible light band (6173 Å - Red - Iron spectral line), useful for observing sun spots.
* Dopplergram - Shows velocities along the line-of-sight.
* LASCO (Large Angle Spectrometric Coronagraph) shows solar corona. C2 shows corona up to 8.4Mkm. C3 shows corona up to 23Mkm.
<h3>20: Show GOES 16, 18 and SDO</h3>
When checked, opens a [Satellite Tracker](../../feature/satellitetracker/readme.md) feature and sets it to display data for the GOES 16, GOES 18 and SDO satellites.
The position and tracks of the satellites will then be visible on a [Map](../../feature/map/readme.md) feature.
<h3>21: Autoscale X</h3>
When clicked, the chart X-axis is automatically scaled so that all power data is visible. When right-clicked, autoscaling of the X-axis will occur whenever new data is added to the chart.
<h3>22: Autoscale Y</h3>
When clicked, the chart Y-axis is automatically scaled so that all power data is visible. When right-clicked, autoscaling of the Y-axis will occur whenever new data is added to the chart.
<h3>23: Set X-axis to Today</h3>
When clicked, the X-axis is set to show today, from midnight to midnight.
When right-clicked, the X-axis is set to show sunrise to sunset. This uses latitude and longitude from Preferences > My position.
<h3>24: Set X-axis to -1 day</h3>
When clicked, the X-axis is set 1 day earlier than the current setting, at the same time.
<h3>25: Set X-axis to +1 day</h3>
When clicked, the X-axis is set 1 day later than the current setting, at the same time.
<h3>26: Start Time</h3>
Displays/sets the current start time of the chart (X-axis minimum). It's possible to scroll through hours/days/months by clicking on the relevent segment and using the mouse scroll wheel.
<h3>27: End Time</h3>
Displays/sets the current end time of the chart (X-axis maximum). It's possible to scroll through hours/days/months by clicking on the relevent segment and using the mouse scroll wheel.
<h3>28: Min</h3>
Displays/sets the minimum Y-axis value.
<h3>29: Max</h3>
Displays/sets the maximum Y-axis value.
<h3>30: Now</h3>
When checked, the latest SDO imagery is displayed. When unchecked, you can enter a date and time for which imagery should be displayed.
<h3>31: Date Time</h3>
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 displayed on the 3D map.
<h3>33: Show Paths on Map</h3>
When clicked, shows the great circle paths between transmitters and receivers on a [Map](../../feature/map/readme.md).
![SID paths](../../../doc/img/SID_plugin_paths.png)
The positions of the transmitters are taken from the Map's VLF database. The position of the receiver is for most devices taken from Preferences > My Position.
For KiwiSDRs, the position is taken from the GPS position indicated by the device.
In order to match a transmitter in the Map's VLF database, the label used in the SID chart must match the transmitter's name. It is possible to add user-defined VLF transmitters via
a `vlftransmitters.csv` file. See the [Map](../../feature/map/readme.md) documentation.
<h2>Tips</h2>
In order to check that a peak in the spectrum is a real VLF signal, you can:
* If using a magnetic loop or other directional antenna, rotate it and make sure the amplitude varies, as mag loops should have a null orthogonal to the plane of the loop.
* Check that the signal has diurnal variation (it should vary with the time of day, due to the changes in the ionosphere).
* Check with online lists of VLF signals (E.g. https://sidstation.loudet.org/stations-list-en.xhtml or https://www.mwlist.org/vlf.php). A number of these are plotted on the [Map](../../feature/map/readme.md) feature.
Occasionally, the X-ray flux data may drop to 0. This is typically when the GOES satellite is in eclipse (The Earth or moon is inbetween the satellite and the Sun).
SIDs are most likely to be detected when it's day time in the path between the signal source and receiver, as at night, the atmosphere is shielded from the X-rays by the Earth.
Also, as the D layer in the ionosphere essentially disappears at night, the received power is not as constant as during the day.
<h2>Codecs</h2>
You may need to install an mp4/h264 codec to view the SDO videos.
On Windows, try [K-Lite Codecs](https://www.codecguide.com/download_k-lite_codec_pack_basic.htm).
On Linux, install gstreamer libav. This can be installed on Ubuntu with: `sudo apt install gstreamer1.0-libav`
<h2>Attribution</h2>
X-Ray and proton data is from [NOAA](https://www.swpc.noaa.gov/products/goes-x-ray-flux).
Solar images are from [SDO | Solar Dynamics Observatory](https://sdo.gsfc.nasa.gov/).
Corona images are from [SOHO](https://soho.nascom.nasa.gov/home.html).
GRB data is from [GRBweb](https://user-web.icecube.wisc.edu/~grbweb_public/index.html).
Solar flare data is from [Solar Orbiter STIX Data Center](https://datacenter.stix.i4ds.net/).

Wyświetl plik

@ -0,0 +1,361 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include "SWGFeatureSettings.h"
#include "SWGDeviceState.h"
#include "dsp/dspengine.h"
#include "device/deviceset.h"
#include "feature/featureset.h"
#include "settings/serializable.h"
#include "maincore.h"
#include "sid.h"
#include "sidworker.h"
MESSAGE_CLASS_DEFINITION(SIDMain::MsgConfigureSID, Message)
MESSAGE_CLASS_DEFINITION(SIDMain::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(SIDMain::MsgReportWorker, Message)
MESSAGE_CLASS_DEFINITION(SIDMain::MsgMeasurement, Message)
const char* const SIDMain::m_featureIdURI = "sdrangel.feature.sid";
const char* const SIDMain::m_featureId = "SID";
SIDMain::SIDMain(WebAPIAdapterInterface *webAPIAdapterInterface) :
Feature(m_featureIdURI, webAPIAdapterInterface),
m_thread(nullptr),
m_worker(nullptr)
{
qDebug("SIDMain::SID: webAPIAdapterInterface: %p", webAPIAdapterInterface);
setObjectName(m_featureId);
m_state = StIdle;
m_errorMessage = "SID error";
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&SIDMain::networkManagerFinished
);
}
SIDMain::~SIDMain()
{
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&SIDMain::networkManagerFinished
);
delete m_networkManager;
}
void SIDMain::start()
{
qDebug("SIDMain::start");
m_thread = new QThread();
m_worker = new SIDWorker(this, m_webAPIAdapterInterface);
m_worker->moveToThread(m_thread);
QObject::connect(m_thread, &QThread::started, m_worker, &SIDWorker::startWork);
QObject::connect(m_thread, &QThread::finished, m_worker, &QObject::deleteLater);
QObject::connect(m_thread, &QThread::finished, m_thread, &QThread::deleteLater);
m_worker->setMessageQueueToFeature(getInputMessageQueue());
m_worker->setMessageQueueToGUI(getMessageQueueToGUI());
m_thread->start();
m_state = StRunning;
MsgConfigureSID *msg = MsgConfigureSID::create(m_settings, QList<QString>(), true);
m_worker->getInputMessageQueue()->push(msg);
}
void SIDMain::stop()
{
qDebug("SIDMain::stop");
m_state = StIdle;
if (m_thread)
{
m_thread->quit();
m_thread->wait();
m_thread = nullptr;
m_worker = nullptr;
}
}
bool SIDMain::handleMessage(const Message& cmd)
{
if (MsgConfigureSID::match(cmd))
{
MsgConfigureSID& cfg = (MsgConfigureSID&) cmd;
qDebug() << "SIDMain::handleMessage: MsgConfigureSID";
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
return true;
}
else if (MsgStartStop::match(cmd))
{
MsgStartStop& cfg = (MsgStartStop&) cmd;
qDebug() << "SIDMain::handleMessage: MsgStartStop: start:" << cfg.getStartStop();
if (cfg.getStartStop()) {
start();
} else {
stop();
}
return true;
}
else if (MsgReportWorker::match(cmd))
{
MsgReportWorker& report = (MsgReportWorker&) cmd;
m_state = StError;
m_errorMessage = report.getMessage();
return true;
}
else
{
return false;
}
}
QByteArray SIDMain::serialize() const
{
return m_settings.serialize();
}
bool SIDMain::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureSID *msg = MsgConfigureSID::create(m_settings, QList<QString>(), true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureSID *msg = MsgConfigureSID::create(m_settings, QList<QString>(), true);
m_inputMessageQueue.push(msg);
return false;
}
}
void SIDMain::applySettings(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force)
{
qDebug() << "SIDMain::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
if (m_worker)
{
MsgConfigureSID *msg = MsgConfigureSID::create(settings, settingsKeys, force);
m_worker->getInputMessageQueue()->push(msg);
}
if (settingsKeys.contains("useReverseAPI"))
{
bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) ||
settingsKeys.contains("reverseAPIAddress") ||
settingsKeys.contains("reverseAPIPort") ||
settingsKeys.contains("reverseAPIFeatureSetIndex") ||
settingsKeys.contains("m_reverseAPIFeatureIndex");
webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force);
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
int SIDMain::webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) errorMessage;
getFeatureStateStr(*response.getState());
MsgStartStop *msg = MsgStartStop::create(run);
getInputMessageQueue()->push(msg);
return 202;
}
int SIDMain::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setSidSettings(new SWGSDRangel::SWGSIDSettings());
response.getSidSettings()->init();
webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int SIDMain::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
SIDSettings settings = m_settings;
webapiUpdateFeatureSettings(settings, featureSettingsKeys, response);
MsgConfigureSID *msg = MsgConfigureSID::create(settings, featureSettingsKeys, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureSID *msgToGUI = MsgConfigureSID::create(settings, featureSettingsKeys, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatFeatureSettings(response, settings);
return 200;
}
void SIDMain::webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const SIDSettings& settings)
{
if (response.getSidSettings()->getTitle()) {
*response.getSidSettings()->getTitle() = settings.m_title;
} else {
response.getSidSettings()->setTitle(new QString(settings.m_title));
}
response.getSidSettings()->setRgbColor(settings.m_rgbColor);
response.getSidSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getSidSettings()->getReverseApiAddress()) {
*response.getSidSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getSidSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getSidSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getSidSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex);
response.getSidSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex);
if (settings.m_rollupState)
{
if (response.getSidSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getSidSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getSidSettings()->setRollupState(swgRollupState);
}
}
}
void SIDMain::webapiUpdateFeatureSettings(
SIDSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response)
{
if (featureSettingsKeys.contains("title")) {
settings.m_title = *response.getSidSettings()->getTitle();
}
if (featureSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getSidSettings()->getRgbColor();
}
if (featureSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getSidSettings()->getUseReverseApi() != 0;
}
if (featureSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getSidSettings()->getReverseApiAddress();
}
if (featureSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getSidSettings()->getReverseApiPort();
}
if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) {
settings.m_reverseAPIFeatureSetIndex = response.getSidSettings()->getReverseApiFeatureSetIndex();
}
if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) {
settings.m_reverseAPIFeatureIndex = response.getSidSettings()->getReverseApiFeatureIndex();
}
if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(featureSettingsKeys, response.getSidSettings()->getRollupState());
}
}
void SIDMain::webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const SIDSettings& settings, bool force)
{
SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings();
// swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet());
// swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex());
swgFeatureSettings->setFeatureType(new QString("SID"));
swgFeatureSettings->setSidSettings(new SWGSDRangel::SWGSIDSettings());
SWGSDRangel::SWGSIDSettings *swgSIDSettings = swgFeatureSettings->getSidSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (featureSettingsKeys.contains("title") || force) {
swgSIDSettings->setTitle(new QString(settings.m_title));
}
if (featureSettingsKeys.contains("rgbColor") || force) {
swgSIDSettings->setRgbColor(settings.m_rgbColor);
}
QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIFeatureSetIndex)
.arg(settings.m_reverseAPIFeatureIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgFeatureSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgFeatureSettings;
}
void SIDMain::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "SIDMain::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("SIDMain::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}

Wyświetl plik

@ -0,0 +1,188 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SID_H_
#define INCLUDE_FEATURE_SID_H_
#include <QThread>
#include <QNetworkRequest>
#include <QDateTime>
#include "feature/feature.h"
#include "util/message.h"
#include "sidsettings.h"
class WebAPIAdapterInterface;
class QNetworkAccessManager;
class QNetworkReply;
class SIDWorker;
namespace SWGSDRangel {
class SWGDeviceState;
}
// There's a structure in winnt.h named SID
class SIDMain : public Feature
{
Q_OBJECT
public:
class MsgConfigureSID : public Message {
MESSAGE_CLASS_DECLARATION
public:
const SIDSettings& getSettings() const { return m_settings; }
const QList<QString>& getSettingsKeys() const { return m_settingsKeys; }
bool getForce() const { return m_force; }
static MsgConfigureSID* create(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force) {
return new MsgConfigureSID(settings, settingsKeys, force);
}
private:
SIDSettings m_settings;
QList<QString> m_settingsKeys;
bool m_force;
MsgConfigureSID(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force) :
Message(),
m_settings(settings),
m_settingsKeys(settingsKeys),
m_force(force)
{ }
};
class MsgStartStop : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getStartStop() const { return m_startStop; }
static MsgStartStop* create(bool startStop) {
return new MsgStartStop(startStop);
}
protected:
bool m_startStop;
MsgStartStop(bool startStop) :
Message(),
m_startStop(startStop)
{ }
};
class MsgReportWorker : public Message {
MESSAGE_CLASS_DECLARATION
public:
QString getMessage() { return m_message; }
static MsgReportWorker* create(QString message) {
return new MsgReportWorker(message);
}
private:
QString m_message;
MsgReportWorker(QString message) :
Message(),
m_message(message)
{}
};
class MsgMeasurement : public Message {
MESSAGE_CLASS_DECLARATION
public:
QDateTime getDateTime() const { return m_dateTime; }
const QStringList& getIds() const { return m_ids; }
const QList<double>& getMeasurements() const { return m_measurements; }
static MsgMeasurement* create(QDateTime dateTime, const QStringList& ids, const QList<double>& measurements) {
return new MsgMeasurement(dateTime, ids, measurements);
}
private:
QDateTime m_dateTime;
QStringList m_ids;
QList<double> m_measurements;
MsgMeasurement(QDateTime dateTime, const QStringList& ids, const QList<double>& measurements) :
Message(),
m_dateTime(dateTime),
m_ids(ids),
m_measurements(measurements)
{}
};
SIDMain(WebAPIAdapterInterface *webAPIAdapterInterface);
virtual ~SIDMain();
virtual void destroy() { delete this; }
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) const { id = objectName(); }
virtual QString getIdentifier() const { return objectName(); }
virtual void getTitle(QString& title) const { title = m_settings.m_title; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
static void webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const SIDSettings& settings);
static void webapiUpdateFeatureSettings(
SIDSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response);
static const char* const m_featureIdURI;
static const char* const m_featureId;
private:
QThread *m_thread;
SIDWorker *m_worker;
SIDSettings m_settings;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void start();
void stop();
void applySettings(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force = false);
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const SIDSettings& settings, bool force);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_FEATURE_SID_H_

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,317 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023-2024 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SIDGUI_H_
#define INCLUDE_FEATURE_SIDGUI_H_
#include <QTimer>
#include <QMenu>
#include <QToolButton>
#include <QDateTime>
#include <QtCharts>
#include <QMediaPlayer>
#include "feature/featuregui.h"
#include "util/messagequeue.h"
#include "util/movingaverage.h"
#include "util/grb.h"
#include "util/goesxray.h"
#include "util/solardynamicsobservatory.h"
#include "util/stix.h"
#include "settings/rollupstate.h"
#include "availablechannelorfeaturehandler.h"
#include "sidsettings.h"
class PluginAPI;
class FeatureUISet;
class SIDMain;
namespace Ui {
class SIDGUI;
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
using namespace QtCharts;
#endif
class SIDGUI : public FeatureGUI {
Q_OBJECT
struct Measurement {
QDateTime m_dateTime;
double m_measurement;
Measurement(QDateTime dateTime, double measurement) :
m_dateTime(dateTime),
m_measurement(measurement)
{
}
};
struct ChannelMeasurement {
QString m_id;
QList<Measurement> m_measurements;
QXYSeries *m_series;
double m_minMeasurement;
double m_maxMeasurement;
MovingAverageUtilVar<double, double> m_movingAverage;
ChannelMeasurement() :
m_series(nullptr),
m_minMeasurement(std::numeric_limits<double>::quiet_NaN()),
m_maxMeasurement(std::numeric_limits<double>::quiet_NaN()),
m_movingAverage(1)
{
}
ChannelMeasurement(const QString& id, int averageSamples) :
m_id(id),
m_series(nullptr),
m_minMeasurement(std::numeric_limits<double>::quiet_NaN()),
m_maxMeasurement(std::numeric_limits<double>::quiet_NaN()),
m_movingAverage(averageSamples)
{
}
void append(QDateTime dateTime, double measurement, bool updateSeries=true)
{
m_measurements.append(Measurement(dateTime, measurement));
if (std::isnan(m_minMeasurement)) {
m_minMeasurement = measurement;
} else {
m_minMeasurement = std::min(m_minMeasurement, measurement);
}
if (std::isnan(m_maxMeasurement)) {
m_maxMeasurement = measurement;
} else {
m_maxMeasurement = std::max(m_maxMeasurement, measurement);
}
if (m_series && updateSeries) {
appendSeries(dateTime, measurement);
}
}
void newSeries(QXYSeries *series, int samples)
{
m_series = series;
m_movingAverage.resize(samples);
}
void appendSeries(QDateTime dateTime, double measurement)
{
m_movingAverage(measurement);
m_series->append(dateTime.toMSecsSinceEpoch(), m_movingAverage.instantAverage());
}
void clear()
{
m_minMeasurement = std::numeric_limits<double>::quiet_NaN();
m_maxMeasurement = std::numeric_limits<double>::quiet_NaN();
m_measurements.clear();
m_series = nullptr;
}
};
public:
static SIDGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual void setWorkspaceIndex(int index);
virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }
virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }
virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }
private:
Ui::SIDGUI* ui;
PluginAPI* m_pluginAPI;
FeatureUISet* m_featureUISet;
SIDSettings m_settings;
QList<QString> m_settingsKeys;
RollupState m_rollupState;
bool m_doApplySettings;
SIDMain* m_sid;
MessageQueue m_inputMessageQueue;
QTimer m_statusTimer;
QTimer m_autosaveTimer;
int m_lastFeatureState;
QFileDialog m_fileDialog;
QList<ChannelMeasurement> m_channelMeasurements;
QDateTimeAxis *m_chartXAxis;
QValueAxis *m_chartY1Axis;
QCategoryAxis *m_chartY2Axis;
QLogValueAxis *m_chartY3Axis;
QLogValueAxis *m_chartProtonAxis;
double m_minMeasurement;
double m_maxMeasurement;
QDateTime m_minDateTime;
QDateTime m_maxDateTime;
QDateTimeAxis *m_xRayChartXAxis;
QCategoryAxis *m_xRayChartYAxis;
GOESXRay *m_goesXRay;
ChannelMeasurement m_xrayShortMeasurements[2]; // Primary and secondary
ChannelMeasurement m_xrayLongMeasurements[2];
ChannelMeasurement m_protonMeasurements[4]; // 4 energy bands
static const QStringList m_protonEnergies;
SolarDynamicsObservatory *m_solarDynamicsObservatory;
QStringList m_sdoImageNames;
QMediaPlayer *m_player;
GRB *m_grb;
QList<GRB::Data> m_grbData;
QScatterSeries *m_grbSeries;
float m_grbMin;
float m_grbMax;
STIX *m_stix;
QList<STIX::FlareData> m_stixData;
QScatterSeries *m_stixSeries;
AvailableChannelOrFeatureHandler m_availableFeatureHandler;
AvailableChannelOrFeatureHandler m_availableChannelHandler;
QStringList m_mapItemNames;
explicit SIDGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
virtual ~SIDGUI();
void blockApplySettings(bool block);
void applySetting(const QString& settingsKey);
void applySettings(const QStringList& settingsKeys, bool force = false);
void applyAllSettings();
void displaySettings();
bool handleMessage(const Message& message);
void makeUIConnections();
void writeCSV(const QString& filename);
void readCSV(const QString& filename, bool autoload);
void setAutosaveTimer();
ChannelMeasurement& addMeasurements(const QString& id);
ChannelMeasurement& getMeasurements(const QString& id);
void addMeasurement(const QString& id, QDateTime dateTime, double measurement);
void plotChart();
void plotXRayChart();
void createGRBSeries(QChart *chart, QDateTimeAxis *xAxis, QLogValueAxis *yAxis);
void createXRaySeries(QChart *chart, QDateTimeAxis *xAxis, QCategoryAxis *yAxis);
void createProtonSeries(QChart *chart, QDateTimeAxis *xAxis, QLogValueAxis *yAxis);
void createSTIXSeries(QChart *chart, QDateTimeAxis *xAxis, QCategoryAxis *yAxis);
void createFlareAxis(QCategoryAxis *yAxis);
void setXAxisRange();
void setY1AxisRange();
void setAutoscaleX();
void setAutoscaleY();
void autoscaleX();
void autoscaleY();
void setButtonBackground(QToolButton *button, bool checked);
void updateMeasurementRange(double measurement);
void updateTimeRange(QDateTime dateTime);
void applySDO();
void applyDateTime();
bool eventFilter(QObject *obj, QEvent *event) override;
void sendToSkyMap(const AvailableChannelOrFeature& skymap, float ra, float dec);
void showGRBContextMenu(QContextMenuEvent *contextEvent, QChartView *chartView, int closestPoint);
void showStixContextMenu(QContextMenuEvent *contextEvent, QChartView *chartView, int closestPoint);
void showContextMenu(QContextMenuEvent *contextEvent);
bool findClosestPoint(QContextMenuEvent *contextEvent, QChart *chart, QScatterSeries *series, int& closestPoint);
void clearMinMax();
bool plotAnyXRay() const;
void clearAllData();
void connectDataUpdates();
void disconnectDataUpdates();
void getData();
void clearFromMap();
static qreal pixelDistance(QChart *chart, QAbstractSeries *series, QPointF a, QPointF b);
private slots:
void onMenuDialogCalled(const QPoint &p);
void onWidgetRolled(QWidget* widget, bool rollDown);
void handleInputMessages();
void on_startStop_toggled(bool checked);
void on_samples_valueChanged(int value);
void on_separateCharts_toggled(bool checked);
void on_displayLegend_toggled(bool checked);
void on_plotXRayLongPrimary_toggled(bool checked);
void on_plotXRayLongSecondary_toggled(bool checked);
void on_plotXRayShortPrimary_toggled(bool checked);
void on_plotXRayShortSecondary_toggled(bool checked);
void on_plotGRB_toggled(bool checked);
void on_plotSTIX_toggled(bool checked);
void on_plotProton_toggled(bool checked);
void on_deleteAll_clicked();
void on_autoscaleX_clicked();
void on_autoscaleY_clicked();
void on_today_clicked();
void on_prevDay_clicked();
void on_nextDay_clicked();
void on_startDateTime_dateTimeChanged(QDateTime value);
void on_endDateTime_dateTimeChanged(QDateTime value);
void on_y1Min_valueChanged(double value);
void on_y1Max_valueChanged(double value);
void on_saveData_clicked();
void on_loadData_clicked();
void on_saveChartImage_clicked();
void autoscaleXRightClicked();
void autoscaleYRightClicked();
void todayRightClicked();
void updateStatus();
void autosave();
void on_settings_clicked();
void xRayDataUpdated(const QList<GOESXRay::XRayData>& data, bool primary);
void protonDataUpdated(const QList<GOESXRay::ProtonData>& data, bool primary);
void grbDataUpdated(const QList<GRB::Data>& data);
void stixDataUpdated(const QList<STIX::FlareData>& data);
void legendMarkerClicked();
void seriesClicked(const QPointF &point);
void chartSplitterMoved(int pos, int index);
void sdoSplitterMoved(int pos, int index);
void on_sdoEnabled_toggled(bool checked);
void on_sdoVideoEnabled_toggled(bool checked);
void on_sdoData_currentIndexChanged(int index);
void on_sdoNow_toggled(bool checked);
void on_sdoDateTime_dateTimeChanged(QDateTime value);
void sdoImageUpdated(const QImage& image);
void sdoVideoError(QMediaPlayer::Error error);
void sdoVideoStatusChanged(QMediaPlayer::MediaStatus status);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
void sdoBufferStatusChanged(int percentFilled);
#else
void sdoBufferProgressChanged(float filled);
#endif
void on_showSats_clicked();
void onSatTrackerAdded(int featureSetIndex, Feature *feature);
void on_map_currentTextChanged(const QString& text);
void on_showPaths_clicked();
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_

Wyświetl plik

@ -0,0 +1,781 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SIDGUI</class>
<widget class="RollupContents" name="SIDGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1044</width>
<height>580</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>100</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>SID</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>964</width>
<height>80</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="chartButtonsLayout">
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="toolTip">
<string>Start/stop measurements</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="loadData">
<property name="toolTip">
<string>Load data from a .csv file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/load.png</normaloff>:/load.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="saveData">
<property name="toolTip">
<string>Save data to a .csv file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/save.png</normaloff>:/save.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="saveChartImage">
<property name="toolTip">
<string>Save chart to an image file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/picture.png</normaloff>:/picture.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="deleteAll">
<property name="toolTip">
<string>Delete all data</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="averageLabel">
<property name="text">
<string>Avg</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="samples">
<property name="toolTip">
<string>Number of samples in average</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotXRayLongPrimary">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Display primary long wavelength X-Ray data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/xlp.svg</normaloff>:/sid/icons/xlp.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotXRayLongSecondary">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Display secondary long wavelength X-Ray data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/xls.svg</normaloff>:/sid/icons/xls.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotXRayShortPrimary">
<property name="toolTip">
<string>Display primary short wavelength X-Ray data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/xsp.svg</normaloff>:/sid/icons/xsp.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotXRayShortSecondary">
<property name="toolTip">
<string>Display secondary short wavelength X-Ray data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/xss.svg</normaloff>:/sid/icons/xss.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotProton">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Display proton flux data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/proton.svg</normaloff>:/sid/icons/proton.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotGRB">
<property name="toolTip">
<string>Display GRBs on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/gamma.svg</normaloff>:/sid/icons/gamma.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotSTIX">
<property name="toolTip">
<string>Display solar flares from STIX on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/solar-orbiter.svg</normaloff>:/sid/icons/solar-orbiter.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="separateCharts">
<property name="toolTip">
<string>Display as a single chart or multiple charts</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/chartcombined.png</normaloff>
<normalon>:/sid/icons/chartseparate.png</normalon>:/sid/icons/chartcombined.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="displayLegend">
<property name="toolTip">
<string>Display legend</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/legend.png</normaloff>:/sid/icons/legend.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="settings">
<property name="toolTip">
<string>Open settings dialog</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/listing.png</normaloff>:/listing.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="sdoLabel">
<property name="text">
<string>SDO/SOHO</string>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="sdoEnabled">
<property name="toolTip">
<string>Display SDO/SOHO imagery</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/sun.png</normaloff>:/sid/icons/sun.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="sdoVideoEnabled">
<property name="toolTip">
<string>Select image or video</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/picture.png</normaloff>
<normalon>:/film.png</normalon>:/picture.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sdoData">
<property name="toolTip">
<string>Image/wavelength selection</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="showSats">
<property name="toolTip">
<string>Show GOES 16, 18 and SDO in Satellite Tracker</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/gps.png</normaloff>:/gps.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="chartAxisLayout">
<item>
<widget class="QToolButton" name="autoscaleX">
<property name="toolTip">
<string>Autoscale X-axis. Right click to continually autoscale</string>
</property>
<property name="text">
<string>X</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="autoscaleY">
<property name="toolTip">
<string>Autoscale Y-axis. Right click to continually autoscale</string>
</property>
<property name="text">
<string>Y</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="today">
<property name="toolTip">
<string>Set X-axis range to today. Right click to set to today's daylight hours.</string>
</property>
<property name="text">
<string>T</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="prevDay">
<property name="toolTip">
<string>Set X-axis range to -1 day</string>
</property>
<property name="text">
<string>-1</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="nextDay">
<property name="toolTip">
<string>Set X-axis range to +1 day</string>
</property>
<property name="text">
<string>+1</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="startDateTimeLabel">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="WrappingDateTimeEdit" name="startDateTime">
<property name="toolTip">
<string>X axis start time</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="endDateTimeLabel">
<property name="text">
<string>End</string>
</property>
</widget>
</item>
<item>
<widget class="WrappingDateTimeEdit" name="endDateTime">
<property name="toolTip">
<string>X axis end time</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="minLabel">
<property name="text">
<string>Min</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="y1Min">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-150.000000000000000</double>
</property>
<property name="value">
<double>-100.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="minUnits">
<property name="text">
<string>dB</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="maxLabel">
<property name="text">
<string>Max</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="y1Max">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-150.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="maxUnits">
<property name="text">
<string>dB</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="sdoNow">
<property name="toolTip">
<string>When checked SDO data is the latest available. When unchecked, date and time may be set manually</string>
</property>
<property name="text">
<string>Now</string>
</property>
</widget>
</item>
<item>
<widget class="WrappingDateTimeEdit" name="sdoDateTime">
<property name="toolTip">
<string>Date and time for SDO data</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mapLabel">
<property name="text">
<string>Map</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="map">
<property name="toolTip">
<string>3D Map feature to send date and time to</string>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QToolButton" name="showPaths">
<property name="toolTip">
<string>Show propagation paths on map</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/world.png</normaloff>:/world.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="charts" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>661</width>
<height>384</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="sdoSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QSplitter" name="chartSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QChartView" name="chart">
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="windowTitle">
<string>Power vs Time</string>
</property>
</widget>
<widget class="QChartView" name="xRayChart"/>
</widget>
<widget class="QWidget" name="sdoContainer" native="true">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QProgressBar" name="sdoProgressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="ScaledImage" name="sdoImage">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QVideoWidget" name="sdoVideo" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QChartView</class>
<extends>QGraphicsView</extends>
<header>QtCharts</header>
</customwidget>
<customwidget>
<class>WrappingDateTimeEdit</class>
<extends>QDateTimeEdit</extends>
<header>gui/wrappingdatetimeedit.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ScaledImage</class>
<extends>QLabel</extends>
<header>gui/scaledimage.h</header>
</customwidget>
<customwidget>
<class>QVideoWidget</class>
<extends>QWidget</extends>
<header>qvideowidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
<include location="icons.qrc"/>
</resources>
<connections/>
</ui>

Wyświetl plik

@ -0,0 +1,79 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "sidgui.h"
#endif
#include "sid.h"
#include "sidplugin.h"
#include "sidwebapiadapter.h"
const PluginDescriptor SIDPlugin::m_pluginDescriptor = {
SIDMain::m_featureId,
QStringLiteral("SID"),
QStringLiteral("7.20.0"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
SIDPlugin::SIDPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(nullptr)
{
}
const PluginDescriptor& SIDPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void SIDPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerFeature(SIDMain::m_featureIdURI, SIDMain::m_featureId, this);
}
#ifdef SERVER_MODE
FeatureGUI* SIDPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
(void) featureUISet;
(void) feature;
return nullptr;
}
#else
FeatureGUI* SIDPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
return SIDGUI::create(m_pluginAPI, featureUISet, feature);
}
#endif
Feature* SIDPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
{
return new SIDMain(webAPIAdapterInterface);
}
FeatureWebAPIAdapter* SIDPlugin::createFeatureWebAPIAdapter() const
{
return new SIDWebAPIAdapter();
}

Wyświetl plik

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SIDPLUGIN_H
#define INCLUDE_FEATURE_SIDPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class FeatureGUI;
class WebAPIAdapterInterface;
class SIDPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.feature.sid")
public:
explicit SIDPlugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const;
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const;
virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_FEATURE_SIDPLUGIN_H

Wyświetl plik

@ -0,0 +1,710 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023-2024 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include <QDataStream>
#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"
// https://medialab.github.io/iwanthue/
// Restricted dark colours and chroma at either end
const QList<QRgb> SIDSettings::m_defaultColors = {
0xdd4187,
0x7ce048,
0xc944db,
0xd5d851,
0x826add,
0x5da242,
0xc97bc1,
0x85e49b,
0xdf5035,
0x57d6d9,
0xd28e2e,
0x7091d3,
0xa3a052,
0xd36d76,
0x4aa47d,
0xc9895a,
};
const QList<QRgb> SIDSettings::m_defaultXRayShortColors = {
0x8a3ffc,
0x8a3ffc
};
const QList<QRgb> SIDSettings::m_defaultXRayLongColors = {
0x4589ff,
0x0f62fe
};
const QList<QRgb> SIDSettings::m_defaultProtonColors = {
0x9ef0f0,
0x3ddbd9,
0x08bdba,
0x009d9a
};
const QRgb SIDSettings::m_defaultGRBColor = 0xffffff;
const QRgb SIDSettings::m_defaultSTIXColor = 0xcccc00;
SIDSettings::SIDSettings() :
m_rollupState(nullptr),
m_workspaceIndex(0)
{
resetToDefaults();
}
void SIDSettings::resetToDefaults()
{
m_channelSettings = {};
m_period = 10.0f;
m_autosave = true;
m_autoload = true;
m_filename = "sid_autosave.csv";
m_autosavePeriod = 10;
m_samples = 1;
m_autoscaleX = true;
m_autoscaleY = true;
m_separateCharts = true;
m_displayLegend = true;
m_legendAlignment = Qt::AlignTop;
m_displayAxisTitles = true;
m_displaySecondaryAxis = true;
m_plotXRayLongPrimary = true;
m_plotXRayLongSecondary = false;
m_plotXRayShortPrimary = true;
m_plotXRayShortSecondary = false;
m_plotGRB = true;
m_plotSTIX = true;
m_plotProton = true;
m_y1Min = -100.0f;
m_y1Max = 0.0f;
m_startDateTime = QDateTime();
m_endDateTime = QDateTime();
m_xrayShortColors = m_defaultXRayShortColors;
m_xrayLongColors = m_defaultXRayLongColors;
m_protonColors = m_defaultProtonColors;
m_grbColor = m_defaultGRBColor;
m_stixColor =m_defaultSTIXColor;
m_sdoEnabled = true;
m_sdoVideoEnabled = false;
m_sdoData = "";
m_sdoNow = true;
m_sdoDateTime = QDateTime();
m_map = "";
m_title = "SID";
m_rgbColor = QColor(102, 0, 102).rgb();
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIFeatureSetIndex = 0;
m_reverseAPIFeatureIndex = 0;
}
QByteArray SIDSettings::serialize() const
{
SimpleSerializer s(1);
s.writeList(1, m_channelSettings);
s.writeFloat(2, m_period);
s.writeBool(10, m_autosave);
s.writeBool(11, m_autoload);
s.writeString(12, m_filename);
s.writeS32(13, m_autosavePeriod);
s.writeS32(20, m_samples);
s.writeBool(21, m_autoscaleX);
s.writeBool(22, m_autoscaleY);
s.writeBool(23, m_separateCharts);
s.writeBool(24, m_displayLegend);
s.writeS32(25, (int) m_legendAlignment);
s.writeBool(26, m_displayAxisTitles);
s.writeBool(27, m_displaySecondaryAxis);
s.writeBool(28, m_plotXRayLongPrimary);
s.writeBool(29, m_plotXRayLongSecondary);
s.writeBool(30, m_plotXRayShortPrimary);
s.writeBool(31, m_plotXRayShortSecondary);
s.writeBool(32, m_plotGRB);
s.writeBool(33, m_plotSTIX);
s.writeBool(34, m_plotProton);
s.writeFloat(36, m_y1Min);
s.writeFloat(37, m_y1Max);
if (m_startDateTime.isValid()) {
s.writeS64(38, m_startDateTime.toMSecsSinceEpoch());
}
if (m_endDateTime.isValid()) {
s.writeS64(39, m_endDateTime.toMSecsSinceEpoch());
}
s.writeList(40, m_xrayShortColors);
s.writeList(41, m_xrayLongColors);
s.writeList(42, m_protonColors);
s.writeU32(43, m_grbColor);
s.writeU32(44, m_stixColor);
s.writeBool(50, m_sdoEnabled);
s.writeBool(51, m_sdoVideoEnabled);
s.writeString(52, m_sdoData);
s.writeBool(53, m_sdoNow);
if (m_sdoDateTime.isValid()) {
s.writeS64(54, m_sdoDateTime.toMSecsSinceEpoch());
}
s.writeString(55, m_map);
s.writeList(60, m_sdoSplitterSizes);
s.writeList(61, m_chartSplitterSizes);
s.writeString(70, m_title);
s.writeU32(71, m_rgbColor);
s.writeBool(72, m_useReverseAPI);
s.writeString(73, m_reverseAPIAddress);
s.writeU32(74, m_reverseAPIPort);
s.writeU32(75, m_reverseAPIFeatureSetIndex);
s.writeU32(76, m_reverseAPIFeatureIndex);
if (m_rollupState) {
s.writeBlob(77, m_rollupState->serialize());
}
s.writeS32(78, m_workspaceIndex);
s.writeBlob(79, m_geometryBytes);
return s.final();
}
bool SIDSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
QByteArray bytetmp;
uint32_t utmp;
qint64 tmp64;
QString strtmp;
QByteArray blob;
d.readList(1, &m_channelSettings);
d.readFloat(2, &m_period, 10.0f);
d.readBool(10, &m_autosave, true);
d.readBool(11, &m_autoload, true);
d.readString(12, &m_filename, "sid_autosave.csv");
d.readS32(13, &m_autosavePeriod, 10);
d.readS32(20, &m_samples, 1);
d.readBool(21, &m_autoscaleX, true);
d.readBool(22, &m_autoscaleY, true);
d.readBool(23, &m_separateCharts, true);
d.readBool(24, &m_displayLegend, true);
d.readS32(25, (int *) &m_legendAlignment, Qt::AlignTop);
d.readBool(26, &m_displayAxisTitles, true);
d.readBool(27, &m_displaySecondaryAxis, true);
d.readBool(28, &m_plotXRayLongPrimary, true);
d.readBool(29, &m_plotXRayLongSecondary, false);
d.readBool(30, &m_plotXRayShortPrimary, true);
d.readBool(31, &m_plotXRayShortSecondary, false);
d.readBool(32, &m_plotGRB, true);
d.readBool(33, &m_plotSTIX, true);
d.readBool(34, &m_plotProton, false);
d.readFloat(36, &m_y1Min, -100.0f);
d.readFloat(37, &m_y1Max, 0.0f);
if (d.readS64(38, &tmp64)) {
m_startDateTime = QDateTime::fromMSecsSinceEpoch(tmp64);
} else {
m_startDateTime = QDateTime();
}
if (d.readS64(39, &tmp64)) {
m_endDateTime = QDateTime::fromMSecsSinceEpoch(tmp64);
} else {
m_endDateTime = QDateTime();
}
d.readList(40, &m_xrayShortColors);
if (m_xrayShortColors.size() != 2) {
m_xrayShortColors = m_defaultXRayShortColors;
}
d.readList(41, &m_xrayLongColors);
if (m_xrayLongColors.size() != 2) {
m_xrayLongColors = m_defaultXRayLongColors;
}
d.readList(42, &m_protonColors);
if (m_protonColors.size() != 4) {
m_protonColors = m_defaultProtonColors;
}
d.readU32(43, &m_grbColor, m_defaultGRBColor);
d.readU32(44, &m_stixColor, m_defaultSTIXColor);
d.readBool(50, &m_sdoEnabled, true);
d.readBool(51, &m_sdoVideoEnabled, false);
d.readString(52, &m_sdoData, "");
d.readBool(53, &m_sdoNow);
if (d.readS64(54, &tmp64)) {
m_sdoDateTime = QDateTime::fromMSecsSinceEpoch(tmp64);
} else {
m_sdoDateTime = QDateTime();
}
d.readString(55, &m_map, "");
d.readList(60, &m_sdoSplitterSizes);
d.readList(61, &m_chartSplitterSizes);
d.readString(70, &m_title, "SID");
d.readU32(71, &m_rgbColor, QColor(102, 0, 102).rgb());
d.readBool(72, &m_useReverseAPI, false);
d.readString(73, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(74, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(75, &utmp, 0);
m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
d.readU32(76, &utmp, 0);
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
if (m_rollupState)
{
d.readBlob(77, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
d.readS32(78, &m_workspaceIndex, 0);
d.readBlob(79, &m_geometryBytes);
return true;
}
else
{
resetToDefaults();
return false;
}
}
void SIDSettings::applySettings(const QStringList& settingsKeys, const SIDSettings& settings)
{
if (settingsKeys.contains("channelSettings")) {
m_channelSettings = settings.m_channelSettings;
}
if (settingsKeys.contains("period")) {
m_period = settings.m_period;
}
if (settingsKeys.contains("autosave")) {
m_autosave = settings.m_autosave;
}
if (settingsKeys.contains("autoload")) {
m_autoload = settings.m_autoload;
}
if (settingsKeys.contains("autosavePeriod")) {
m_autosavePeriod = settings.m_autosavePeriod;
}
if (settingsKeys.contains("filename")) {
m_filename = settings.m_filename;
}
if (settingsKeys.contains("samples")) {
m_samples = settings.m_samples;
}
if (settingsKeys.contains("autoscaleX")) {
m_autoscaleX = settings.m_autoscaleX;
}
if (settingsKeys.contains("autoscaleY")) {
m_autoscaleY = settings.m_autoscaleY;
}
if (settingsKeys.contains("separateCharts")) {
m_separateCharts = settings.m_separateCharts;
}
if (settingsKeys.contains("displayLegend")) {
m_displayLegend = settings.m_displayLegend;
}
if (settingsKeys.contains("legendAlignment")) {
m_legendAlignment = settings.m_legendAlignment;
}
if (settingsKeys.contains("displayAxisTitles")) {
m_displayAxisTitles = settings.m_displayAxisTitles;
}
if (settingsKeys.contains("displayAxisLabels")) {
m_displaySecondaryAxis = settings.m_displaySecondaryAxis;
}
if (settingsKeys.contains("plotXRayLongPrimary")) {
m_plotXRayLongPrimary = settings.m_plotXRayLongPrimary;
}
if (settingsKeys.contains("plotXRayLongSecondary")) {
m_plotXRayLongSecondary = settings.m_plotXRayLongSecondary;
}
if (settingsKeys.contains("plotXRayShortPrimary")) {
m_plotXRayShortPrimary = settings.m_plotXRayShortPrimary;
}
if (settingsKeys.contains("plotXRayShorSecondary")) {
m_plotXRayShortSecondary = settings.m_plotXRayShortSecondary;
}
if (settingsKeys.contains("plotGRB")) {
m_plotGRB = settings.m_plotGRB;
}
if (settingsKeys.contains("plotSTIX")) {
m_plotSTIX = settings.m_plotSTIX;
}
if (settingsKeys.contains("plotProton")) {
m_plotProton = settings.m_plotProton;
}
if (settingsKeys.contains("startDateTime")) {
m_startDateTime = settings.m_startDateTime;
}
if (settingsKeys.contains("endDateTime")) {
m_endDateTime = settings.m_endDateTime;
}
if (settingsKeys.contains("y1Min")) {
m_y1Min = settings.m_y1Min;
}
if (settingsKeys.contains("y1Max")) {
m_y1Max = settings.m_y1Max;
}
if (settingsKeys.contains("xrayShortColors")) {
m_xrayShortColors = settings.m_xrayShortColors;
}
if (settingsKeys.contains("xrayLongColors")) {
m_xrayLongColors = settings.m_xrayLongColors;
}
if (settingsKeys.contains("protonColors")) {
m_protonColors = settings.m_protonColors;
}
if (settingsKeys.contains("grbColor")) {
m_grbColor = settings.m_grbColor;
}
if (settingsKeys.contains("stixColor")) {
m_stixColor = settings.m_stixColor;
}
if (settingsKeys.contains("sdoEnabled")) {
m_sdoEnabled = settings.m_sdoEnabled;
}
if (settingsKeys.contains("sdoVideoEnabled")) {
m_sdoVideoEnabled = settings.m_sdoVideoEnabled;
}
if (settingsKeys.contains("sdoData")) {
m_sdoData = settings.m_sdoData;
}
if (settingsKeys.contains("sdoNow")) {
m_sdoNow = settings.m_sdoNow;
}
if (settingsKeys.contains("sdoDateTime")) {
m_sdoDateTime = settings.m_sdoDateTime;
}
if (settingsKeys.contains("map")) {
m_map = settings.m_map;
}
if (settingsKeys.contains("sdoSplitterSizes")) {
m_sdoSplitterSizes = settings.m_sdoSplitterSizes;
}
if (settingsKeys.contains("chartSplitterSizes")) {
m_chartSplitterSizes = settings.m_chartSplitterSizes;
}
if (settingsKeys.contains("title")) {
m_title = settings.m_title;
}
if (settingsKeys.contains("rgbColor")) {
m_rgbColor = settings.m_rgbColor;
}
if (settingsKeys.contains("useReverseAPI")) {
m_useReverseAPI = settings.m_useReverseAPI;
}
if (settingsKeys.contains("reverseAPIAddress")) {
m_reverseAPIAddress = settings.m_reverseAPIAddress;
}
if (settingsKeys.contains("reverseAPIPort")) {
m_reverseAPIPort = settings.m_reverseAPIPort;
}
if (settingsKeys.contains("reverseAPIFeatureSetIndex")) {
m_reverseAPIFeatureSetIndex = settings.m_reverseAPIFeatureSetIndex;
}
if (settingsKeys.contains("reverseAPIFeatureIndex")) {
m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("workspaceIndex")) {
m_workspaceIndex = settings.m_workspaceIndex;
}
}
QString SIDSettings::getDebugString(const QStringList& settingsKeys, bool force) const
{
std::ostringstream ostr;
if (settingsKeys.contains("channelSettings"))
{
QStringList s;
for (auto cs : m_channelSettings) {
s.append(cs.m_id);
}
ostr << " m_channelSettings: " << s.join(",").toStdString();
}
if (settingsKeys.contains("period") || force) {
ostr << " m_period: " << m_period;
}
if (settingsKeys.contains("autosave") || force) {
ostr << " m_autosave: " << m_autosave;
}
if (settingsKeys.contains("autoload") || force) {
ostr << " m_autoload: " << m_autoload;
}
if (settingsKeys.contains("filename") || force) {
ostr << " m_filename: " << m_filename.toStdString();
}
if (settingsKeys.contains("samples") || force) {
ostr << " m_samples: " << m_samples;
}
if (settingsKeys.contains("autoscaleX") || force) {
ostr << " m_autoscaleX: " << m_autoscaleX;
}
if (settingsKeys.contains("autoscaleY") || force) {
ostr << " m_autoscaleY: " << m_autoscaleY;
}
if (settingsKeys.contains("separateCharts") || force) {
ostr << " m_separateCharts: " << m_separateCharts;
}
if (settingsKeys.contains("displayLegend") || force) {
ostr << " m_displayLegend: " << m_displayLegend;
}
if (settingsKeys.contains("legendAlignment") || force) {
ostr << " m_legendAlignment: " << m_legendAlignment;
}
if (settingsKeys.contains("displayAxisTitles") || force) {
ostr << " m_displayAxisTitles: " << m_displayAxisTitles;
}
if (settingsKeys.contains("displayAxisLabels") || force) {
ostr << " m_displaySecondaryAxis: " << m_displaySecondaryAxis;
}
if (settingsKeys.contains("plotXRayLongPrimary") || force) {
ostr << " m_plotXRayLongPrimary: " << m_plotXRayLongPrimary;
}
if (settingsKeys.contains("plotXRayLongSecondary") || force) {
ostr << " m_plotXRayLongSecondary: " << m_plotXRayLongSecondary;
}
if (settingsKeys.contains("plotXRayShortPrimary") || force) {
ostr << " m_plotXRayShortPrimary: " << m_plotXRayShortPrimary;
}
if (settingsKeys.contains("plotXRayShortSecondary") || force) {
ostr << " m_plotXRayShortSecondary: " << m_plotXRayShortSecondary;
}
if (settingsKeys.contains("plotGRB") || force) {
ostr << " m_plotGRB: " << m_plotGRB;
}
if (settingsKeys.contains("plotSTIX") || force) {
ostr << " m_plotSTIX: " << m_plotSTIX;
}
if (settingsKeys.contains("plotProton") || force) {
ostr << " m_plotProton: " << m_plotProton;
}
if (settingsKeys.contains("startDateTime") || force) {
ostr << " m_startDateTime: " << m_startDateTime.toString().toStdString();
}
if (settingsKeys.contains("endDateTime") || force) {
ostr << " m_endDateTime: " << m_endDateTime.toString().toStdString();
}
if (settingsKeys.contains("y1Min") || force) {
ostr << " m_y1Min: " << m_y1Min;
}
if (settingsKeys.contains("y1Max") || force) {
ostr << " m_y1Max: " << m_y1Max;
}
if (settingsKeys.contains("sdoEnabled") || force) {
ostr << " m_sdoEnabled: " << m_sdoEnabled;
}
if (settingsKeys.contains("sdoVideoEnabled") || force) {
ostr << " m_sdoVideoEnabled: " << m_sdoVideoEnabled;
}
if (settingsKeys.contains("sdoData") || force) {
ostr << " m_sdoData: " << m_sdoData.toStdString();
}
if (settingsKeys.contains("sdoNow") || force) {
ostr << " m_sdoNow: " << m_sdoNow;
}
if (settingsKeys.contains("sdoDateTime") || force) {
ostr << " m_sdoDateTime: " << m_sdoDateTime.toString().toStdString();
}
if (settingsKeys.contains("map") || force) {
ostr << " m_map: " << m_map.toStdString();
}
if (settingsKeys.contains("title") || force) {
ostr << " m_title: " << m_title.toStdString();
}
if (settingsKeys.contains("rgbColor") || force) {
ostr << " m_rgbColor: " << m_rgbColor;
}
if (settingsKeys.contains("useReverseAPI") || force) {
ostr << " m_useReverseAPI: " << m_useReverseAPI;
}
if (settingsKeys.contains("reverseAPIAddress") || force) {
ostr << " m_reverseAPIAddress: " << m_reverseAPIAddress.toStdString();
}
if (settingsKeys.contains("reverseAPIPort") || force) {
ostr << " m_reverseAPIPort: " << m_reverseAPIPort;
}
if (settingsKeys.contains("reverseAPIFeatureSetIndex") || force) {
ostr << " m_reverseAPIFeatureSetIndex: " << m_reverseAPIFeatureSetIndex;
}
if (settingsKeys.contains("reverseAPIFeatureIndex") || force) {
ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("workspaceIndex") || force) {
ostr << " m_workspaceIndex: " << m_workspaceIndex;
}
return QString(ostr.str().c_str());
}
SIDSettings::ChannelSettings *SIDSettings::getChannelSettings(const QString& id)
{
for (int i = 0; i < m_channelSettings.size(); i++)
{
if (m_channelSettings[i].m_id == id) {
return &m_channelSettings[i];
}
}
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);
s.writeString(1, m_id);
s.writeBool(2, m_enabled);
s.writeString(3, m_label);
s.writeU32(4, m_color.rgb());
return s.final();
}
bool SIDSettings::ChannelSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid()) {
return false;
}
if (d.getVersion() == 1)
{
QByteArray blob;
quint32 utmp;
d.readString(1, &m_id);
d.readBool(2, &m_enabled, true);
d.readString(3, &m_label);
d.readU32(4, &utmp);
m_color = utmp;
return true;
}
else
{
return false;
}
}
QDataStream& operator<<(QDataStream& out, const SIDSettings::ChannelSettings& settings)
{
out << settings.serialize();
return out;
}
QDataStream& operator>>(QDataStream& in, SIDSettings::ChannelSettings& settings)
{
QByteArray data;
in >> data;
settings.deserialize(data);
return in;
}

Wyświetl plik

@ -0,0 +1,117 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023-2024 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SIDSETTINGS_H_
#define INCLUDE_FEATURE_SIDSETTINGS_H_
#include <QByteArray>
#include <QString>
#include <QDateTime>
#include <QColor>
#include "util/message.h"
class Serializable;
struct SIDSettings
{
struct ChannelSettings
{
QString m_id;
bool m_enabled;
QColor m_color;
QString m_label;
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
QList<ChannelSettings> m_channelSettings; // Channels to record power from
float m_period; // Mesaurement period, in seconds
bool m_autosave;
bool m_autoload;
QString m_filename; // Filename for autosave
int m_autosavePeriod; // In minutes
int m_samples; // Number of samples in average
bool m_autoscaleX;
bool m_autoscaleY;
bool m_separateCharts;
bool m_displayLegend;
Qt::Alignment m_legendAlignment;
bool m_displayAxisTitles;
bool m_displaySecondaryAxis;
bool m_plotXRayLongPrimary;
bool m_plotXRayLongSecondary;
bool m_plotXRayShortPrimary;
bool m_plotXRayShortSecondary;
bool m_plotGRB;
bool m_plotSTIX;
bool m_plotProton;
QDateTime m_startDateTime;
QDateTime m_endDateTime;
float m_y1Min;
float m_y1Max;
QList<QRgb> m_xrayShortColors;
QList<QRgb> m_xrayLongColors;
QList<QRgb> m_protonColors;
QRgb m_grbColor;
QRgb m_stixColor;
bool m_sdoEnabled;
bool m_sdoVideoEnabled;
QString m_sdoData;
bool m_sdoNow;
QDateTime m_sdoDateTime;
QString m_map; // 3D map Id to send date/time to
QList<int> m_sdoSplitterSizes;
QList<int> m_chartSplitterSizes;
QString m_title;
quint32 m_rgbColor;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIFeatureSetIndex;
uint16_t m_reverseAPIFeatureIndex;
Serializable *m_rollupState;
int m_workspaceIndex;
QByteArray m_geometryBytes;
SIDSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
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;
static const QList<QRgb> m_defaultXRayLongColors;
static const QList<QRgb> m_defaultProtonColors;
static const QRgb m_defaultGRBColor;
static const QRgb m_defaultSTIXColor;
};
#endif // INCLUDE_FEATURE_SIDSETTINGS_H_

Wyświetl plik

@ -0,0 +1,178 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "util/units.h"
#include "gui/colordialog.h"
#include "gui/tablecolorchooser.h"
#include "sidsettingsdialog.h"
SIDSettingsDialog::SIDSettingsDialog(SIDSettings *settings, QWidget* parent) :
QDialog(parent),
ui(new Ui::SIDSettingsDialog),
m_settings(settings),
m_fileDialog(nullptr, "Select file to write autosave CSV data to", "", "*.csv")
{
ui->setupUi(this);
ui->period->setValue(m_settings->m_period);
ui->autosave->setChecked(m_settings->m_autosave);
ui->autoload->setChecked(m_settings->m_autoload);
ui->filename->setText(m_settings->m_filename);
ui->autosavePeriod->setValue(m_settings->m_autosavePeriod);
switch (m_settings->m_legendAlignment) {
case Qt::AlignTop:
ui->legendAlignment->setCurrentIndex(0);
break;
case Qt::AlignRight:
ui->legendAlignment->setCurrentIndex(1);
break;
case Qt::AlignBottom:
ui->legendAlignment->setCurrentIndex(2);
break;
case Qt::AlignLeft:
ui->legendAlignment->setCurrentIndex(3);
break;
}
ui->displayAxisTitles->setChecked(m_settings->m_displayAxisTitles);
ui->displaySecondaryAxis->setChecked(m_settings->m_displaySecondaryAxis);
m_settings->createChannelSettings();
// Add settings to table
for (int i = 0; i < m_settings->m_channelSettings.size(); i++)
{
SIDSettings::ChannelSettings *channelSettings = &m_settings->m_channelSettings[i];
int row = ui->channels->rowCount();
ui->channels->setRowCount(row+1);
ui->channels->setItem(row, CHANNELS_COL_ID, new QTableWidgetItem(channelSettings->m_id));
QTableWidgetItem *enableItem = new QTableWidgetItem();
enableItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
enableItem->setCheckState(channelSettings->m_enabled ? Qt::Checked : Qt::Unchecked);
ui->channels->setItem(row, CHANNELS_COL_ENABLED, enableItem);
ui->channels->setItem(row, CHANNELS_COL_LABEL, new QTableWidgetItem(channelSettings->m_label));
TableColorChooser *colorGUI = new TableColorChooser(ui->channels, row, CHANNELS_COL_COLOR, false, channelSettings->m_color.rgba());
m_channelColorGUIs.append(colorGUI);
}
ui->channels->resizeColumnsToContents();
addColor("Primary Long X-Ray", m_settings->m_xrayLongColors[0]);
addColor("Secondary Long X-Ray", m_settings->m_xrayLongColors[1]);
addColor("Primary Short X-Ray ", m_settings->m_xrayShortColors[0]);
addColor("Secondary Short X-Ray", m_settings->m_xrayShortColors[1]);
addColor("GRB", m_settings->m_grbColor);
addColor("STIX", m_settings->m_stixColor);
addColor("10 MeV Proton", m_settings->m_protonColors[0]);
addColor("100 MeV Proton", m_settings->m_protonColors[2]);
ui->colors->resizeColumnsToContents();
}
void SIDSettingsDialog::addColor(const QString& name, QRgb rgb)
{
int row = ui->colors->rowCount();
ui->colors->setRowCount(row+1);
ui->colors->setItem(row, COLORS_COL_NAME, new QTableWidgetItem(name));
TableColorChooser *colorGUI = new TableColorChooser(ui->colors, row, COLORS_COL_COLOR, false, rgb);
m_colorGUIs.append(colorGUI);
}
SIDSettingsDialog::~SIDSettingsDialog()
{
delete ui;
qDeleteAll(m_channelColorGUIs);
qDeleteAll(m_colorGUIs);
}
void SIDSettingsDialog::accept()
{
m_settings->m_period = ui->period->value();
m_settings->m_autosave = ui->autosave->isChecked();
m_settings->m_autoload = ui->autoload->isChecked();
m_settings->m_filename = ui->filename->text();
m_settings->m_autosavePeriod = ui->autosavePeriod->value();
switch (ui->legendAlignment->currentIndex() ) {
case 0:
m_settings->m_legendAlignment = Qt::AlignTop;
break;
case 1:
m_settings->m_legendAlignment = Qt::AlignRight;
break;
case 2:
m_settings->m_legendAlignment = Qt::AlignBottom;
break;
case 3:
m_settings->m_legendAlignment = Qt::AlignLeft;
break;
}
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];
channelSettings->m_id = ui->channels->item(i, CHANNELS_COL_ID)->text();
channelSettings->m_enabled = ui->channels->item(i, CHANNELS_COL_ENABLED)->checkState() == Qt::Checked;
channelSettings->m_label = ui->channels->item(i, CHANNELS_COL_LABEL)->text();
channelSettings->m_color = m_channelColorGUIs[i]->m_color;
}
QDialog::accept();
}
void SIDSettingsDialog::on_browse_clicked()
{
m_fileDialog.setAcceptMode(QFileDialog::AcceptSave);
if (m_fileDialog.exec())
{
QStringList fileNames = m_fileDialog.selectedFiles();
if (fileNames.size() > 0) {
ui->filename->setText(fileNames[0]);
}
}
}
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);
}
}

Wyświetl plik

@ -0,0 +1,68 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SIDSETTINGSDIALOG_H
#define INCLUDE_SIDSETTINGSDIALOG_H
#include <QFileDialog>
#include "ui_sidsettingsdialog.h"
#include "sidsettings.h"
class TableColorChooser;
class SIDSettingsDialog : public QDialog {
Q_OBJECT
public:
explicit SIDSettingsDialog(SIDSettings *settings, QWidget* parent = 0);
~SIDSettingsDialog();
private:
void addColor(const QString& name, QRgb rgb);
private slots:
void accept();
void on_browse_clicked();
void on_remove_clicked();
private:
Ui::SIDSettingsDialog* ui;
SIDSettings *m_settings;
QList<TableColorChooser *> m_channelColorGUIs;
QList<TableColorChooser *> m_colorGUIs;
QFileDialog m_fileDialog;
QStringList m_removeIds;
enum ChannelsRows {
CHANNELS_COL_ID,
CHANNELS_COL_ENABLED,
CHANNELS_COL_LABEL,
CHANNELS_COL_COLOR
};
enum ColorsRows {
COLORS_COL_NAME,
COLORS_COL_COLOR
};
signals:
void removeChannels(const QStringList& ids);
};
#endif // INCLUDE_SIDSETTINGSDIALOG_H

Wyświetl plik

@ -0,0 +1,348 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SIDSettingsDialog</class>
<widget class="QDialog" name="SIDSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>441</width>
<height>800</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>SID Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Data</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="channelsLabel">
<property name="text">
<string>Channels to record power from:</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableWidget" name="channels">
<property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>ID</string>
</property>
</column>
<column>
<property name="text">
<string>Enabled</string>
</property>
</column>
<column>
<property name="text">
<string>Label</string>
</property>
</column>
<column>
<property name="text">
<string>Colour</string>
</property>
</column>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="periodLabel">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Measurement period (s)</string>
</property>
</widget>
</item>
<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>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.001000000000000</double>
</property>
<property name="maximum">
<double>100000.000000000000000</double>
</property>
<property name="value">
<double>10.000000000000000</double>
</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>
<item>
<widget class="QGroupBox" name="autosaveGroup">
<property name="title">
<string>Autosave</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="0">
<widget class="QLabel" name="autosavePeriodLabel">
<property name="text">
<string>Autosave Period (min)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="autosave">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="autoloadLabel">
<property name="text">
<string>Autoload</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="filenameLabel">
<property name="text">
<string>Autosave filename</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="filename"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="autosaveLabel">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Autosave</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="autosavePeriod">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>10.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="autoload">
<property name="toolTip">
<string>Automatically load last autosave data</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="browse">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="chartsGroup">
<property name="title">
<string>Charts</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="legendAlignmentLabel">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Legend Position</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="legendAlignment">
<property name="toolTip">
<string>Position of legend</string>
</property>
<item>
<property name="text">
<string>Top</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom</string>
</property>
</item>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="displayAxisTitlesLabel">
<property name="text">
<string>Display axis titles</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="displayAxisTitles">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="displaySecondaryAxisLabel">
<property name="text">
<string>Display secondary axis</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="displaySecondaryAxis">
<property name="toolTip">
<string>Whether to display secondary axis scale (E.g. for GRB / Proton flux on right of chart)</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QTableWidget" name="colors">
<column>
<property name="text">
<string>Series</string>
</property>
</column>
<column>
<property name="text">
<string>Colour</string>
</property>
</column>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Colours:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SIDSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>31</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SIDSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>31</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

Wyświetl plik

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "SWGFeatureSettings.h"
#include "sid.h"
#include "sidwebapiadapter.h"
SIDWebAPIAdapter::SIDWebAPIAdapter()
{}
SIDWebAPIAdapter::~SIDWebAPIAdapter()
{}
int SIDWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setSidSettings(new SWGSDRangel::SWGSIDSettings());
response.getSidSettings()->init();
SIDMain::webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int SIDWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
SIDMain::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response);
return 200;
}

Wyświetl plik

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SID_WEBAPIADAPTER_H
#define INCLUDE_SID_WEBAPIADAPTER_H
#include "feature/featurewebapiadapter.h"
#include "sidsettings.h"
/**
* Standalone API adapter only for the settings
*/
class SIDWebAPIAdapter : public FeatureWebAPIAdapter {
public:
SIDWebAPIAdapter();
virtual ~SIDWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
private:
SIDSettings m_settings;
};
#endif // INCLUDE_SID_WEBAPIADAPTER_H

Wyświetl plik

@ -0,0 +1,161 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include "webapi/webapiadapterinterface.h"
#include "webapi/webapiutils.h"
#include "channel/channelwebapiutils.h"
#include "device/deviceset.h"
#include "device/deviceapi.h"
#include "maincore.h"
#include "sid.h"
#include "sidworker.h"
SIDWorker::SIDWorker(SIDMain *sid, WebAPIAdapterInterface *webAPIAdapterInterface) :
m_sid(sid),
m_webAPIAdapterInterface(webAPIAdapterInterface),
m_msgQueueToFeature(nullptr),
m_msgQueueToGUI(nullptr),
m_pollTimer(this)
{
}
SIDWorker::~SIDWorker()
{
stopWork();
m_inputMessageQueue.clear();
}
void SIDWorker::startWork()
{
qDebug("SIDWorker::startWork");
QMutexLocker mutexLocker(&m_mutex);
connect(&m_pollTimer, &QTimer::timeout, this, &SIDWorker::update);
m_pollTimer.start(1000);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
// Handle any messages already on the queue
handleInputMessages();
}
void SIDWorker::stopWork()
{
qDebug("SIDWorker::stopWork");
QMutexLocker mutexLocker(&m_mutex);
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_pollTimer.stop();
disconnect(&m_pollTimer, &QTimer::timeout, this, &SIDWorker::update);
}
void SIDWorker::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool SIDWorker::handleMessage(const Message& cmd)
{
if (SIDMain::MsgConfigureSID::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
SIDMain::MsgConfigureSID& cfg = (SIDMain::MsgConfigureSID&) cmd;
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
return true;
}
else
{
return false;
}
}
void SIDWorker::applySettings(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force)
{
qDebug() << "SIDWorker::applySettings:" << settings.getDebugString(settingsKeys, force) << force;
if (settingsKeys.contains("period") || force)
{
m_pollTimer.stop();
m_pollTimer.start(settings.m_period * 1000);
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
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)
{
if (channelSettings.m_enabled)
{
unsigned int deviceSetIndex, channelIndex;
if (MainCore::getDeviceAndChannelIndexFromId(channelSettings.m_id, deviceSetIndex, channelIndex))
{
// Check device is running
std::vector<DeviceSet*> deviceSets = MainCore::instance()->getDeviceSets();
if (deviceSetIndex < deviceSets.size())
{
DeviceSet *deviceSet = deviceSets[deviceSetIndex];
if (deviceSet && (deviceSet->m_deviceAPI->state() == DeviceAPI::StRunning))
{
double power;
if (ChannelWebAPIUtils::getChannelReportValue(deviceSetIndex, channelIndex, "channelPowerDB", power))
{
if (getMessageQueueToGUI())
{
ids.append(channelSettings.m_id);
measurements.append(power);
}
}
else
{
qDebug() << "SIDWorker::update: Failed to get power for channel " << channelSettings.m_id;
}
}
}
}
else
{
qDebug() << "SIDWorker::update: Malformed channel id: " << channelSettings.m_id;
}
}
}
if (getMessageQueueToGUI() && (ids.size() > 0))
{
SIDMain::MsgMeasurement *msgToGUI = SIDMain::MsgMeasurement::create(dateTime, ids, measurements);
getMessageQueueToGUI()->push(msgToGUI);
}
}

Wyświetl plik

@ -0,0 +1,67 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SIDWORKER_H_
#define INCLUDE_FEATURE_SIDWORKER_H_
#include <QObject>
#include <QTimer>
#include "util/message.h"
#include "util/messagequeue.h"
#include "sid.h"
#include "sidsettings.h"
class WebAPIAdapterInterface;
class SIDMain;
class SIDWorker : public QObject
{
Q_OBJECT
public:
SIDWorker(SIDMain *m_sid, WebAPIAdapterInterface *webAPIAdapterInterface);
~SIDWorker();
void startWork();
void stopWork();
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setMessageQueueToFeature(MessageQueue *messageQueue) { m_msgQueueToFeature = messageQueue; }
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_msgQueueToGUI = messageQueue; }
private:
SIDMain *m_sid;
WebAPIAdapterInterface *m_webAPIAdapterInterface;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object
MessageQueue *m_msgQueueToGUI;
SIDSettings m_settings;
QRecursiveMutex m_mutex;
QTimer m_pollTimer;
bool handleMessage(const Message& cmd);
void applySettings(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force = false);
MessageQueue *getMessageQueueToGUI() { return m_msgQueueToGUI; }
private slots:
void handleInputMessages();
void update();
};
#endif // INCLUDE_FEATURE_SIDWORKER_H_

Wyświetl plik

@ -13,6 +13,10 @@ With the ESASky map, a host of astronomical data is available from JWST, Hubble,
![Sky Map feature ESASky](../../../doc/img/SkyMap_ESASky.png)
Additionally, a map of the Moon can be viewed in 2D or 3D, that supports a variety of data layers from various Lunar missions, such as LRO (Lunar Reconnaissance Orbiter).
![Moon map](../../../doc/img/SkyMap_Moon.png)
<h2>Interface</h2>
![Sky Map feature plugin GUI](../../../doc/img/SkyMap_settings.png)
@ -38,6 +42,7 @@ Allows you to select the sky map:
* WWT - World Wide Telescope
* ESASky
* Aladin
* Moon - Map of the Moon
Each map provides different features, image and data sets.
@ -128,6 +133,14 @@ ESASky and Aladin are able to overlay catalog data:
<h2>Attribution</h2>
WWT is from [NumFOCUS](https://worldwidetelescope.org/about/).
ESASky is from [ESAC | ESA](https://www.cosmos.esa.int/web/esdc/esasky-credits).
Aladin Sky Atlas is from [Centre de Données astronomiques de Strasbourg | CDS](https://aladin.cds.unistra.fr/).
Moon map is from [Applied Coherent Technology | ACT](https://www.actgate.com/).
Constellation icons created by Freepik - https://www.flaticon.com
<h2>API</h2>

Wyświetl plik

@ -200,7 +200,8 @@ SkyMapGUI::SkyMapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *
m_featureUISet(featureUISet),
m_doApplySettings(true),
m_source(nullptr),
m_availableChannelOrFeatureHandler(SkyMapSettings::m_pipeURIs, {"target", "skymap.target"})
m_availableChannelOrFeatureHandler(SkyMapSettings::m_pipeURIs, {"target", "skymap.target"}),
m_ready(false)
{
m_feature = feature;
setAttribute(Qt::WA_DeleteOnClose, true);
@ -294,12 +295,17 @@ void SkyMapGUI::on_map_currentIndexChanged(int index)
m_settings.m_map = ui->map->currentText();
applySetting("map");
m_ready = false;
if (m_settings.m_map == "WWT") {
ui->web->load(QUrl(QString("http://127.0.0.1:%1/skymap/html/wwt.html").arg(m_webPort)));
} else if (m_settings.m_map == "ESASky") {
ui->web->load(QUrl(QString("http://127.0.0.1:%1/skymap/html/esasky.html").arg(m_webPort)));
} else if (m_settings.m_map == "Aladin") {
ui->web->load(QUrl(QString("http://127.0.0.1:%1/skymap/html/aladin.html").arg(m_webPort)));
} else if (m_settings.m_map == "Moon") {
ui->web->load(QUrl(QString("http://quickmap.lroc.asu.edu/"))); // Jumping straight to 3D view doesn't seem to work
setStatusText("");
m_ready = true;
}
updateToolbar();
updateBackgrounds();
@ -497,13 +503,21 @@ void SkyMapGUI::applyAllSettings()
void SkyMapGUI::find(const QString& text)
{
float ra, dec;
if (!m_ready)
{
// Save for when ready
m_find = text;
}
else
{
float ra, dec;
// WWT's find doesn't support coordinates, so we check here
if (Units::stringToRADec(text, ra, dec)) {
m_webInterface->setView(ra, dec);
} else {
m_webInterface->track(text);
// WWT's find doesn't support coordinates, so we check here
if (Units::stringToRADec(text, ra, dec)) {
m_webInterface->setView(ra, dec);
} else {
m_webInterface->track(text);
}
}
}
@ -765,7 +779,15 @@ void SkyMapGUI::receivedEvent(const QJsonObject &obj)
}
else if (event == "ready")
{
m_ready = true;
initSkyMap();
// Run find that was requested while map was initialising
if (!m_find.isEmpty())
{
find(m_find);
m_find = "";
}
}
}
else
@ -965,6 +987,7 @@ void SkyMapGUI::updateToolbar()
bool namesVisible = false;
bool projectionVisible = true;
bool backgroundVisible = true;
bool basicVisible = true;
if (m_settings.m_map == "WWT")
{
@ -977,6 +1000,13 @@ void SkyMapGUI::updateToolbar()
backgroundVisible = false;
reticleVisible = false;
}
else if (m_settings.m_map == "Moon")
{
projectionVisible = false;
backgroundVisible = false;
reticleVisible = false;
basicVisible = false;
}
ui->background->setVisible(backgroundVisible);
ui->projection->setVisible(projectionVisible);
@ -985,6 +1015,14 @@ void SkyMapGUI::updateToolbar()
ui->displayConstellations->setVisible(constellationsVisible);
ui->displayReticle->setVisible(reticleVisible);
ui->find->setVisible(basicVisible);
ui->findLabel->setVisible(basicVisible);
ui->displayGrid->setVisible(basicVisible);
ui->displayReticle->setVisible(basicVisible);
ui->displayAntennaFoV->setVisible(basicVisible);
ui->track->setVisible(basicVisible);
ui->source->setVisible(basicVisible);
updateProjection();
}

Wyświetl plik

@ -88,6 +88,8 @@ private:
quint16 m_webPort;
WTML m_wtml;
WebInterface *m_webInterface;
bool m_ready; //!< Web app is ready
QString m_find;
double m_ra; //!< Target from source plugin
double m_dec;

Wyświetl plik

@ -116,6 +116,11 @@
<string>Aladin</string>
</property>
</item>
<item>
<property name="text">
<string>Moon</string>
</property>
</item>
</widget>
</item>
<item>
@ -271,7 +276,7 @@
</property>
<item>
<property name="text">
<string>F0:1 StarTracker</string>
<string/>
</property>
</item>
</widget>

Wyświetl plik

@ -28,13 +28,6 @@
#include "skymapsettings.h"
const QStringList SkyMapSettings::m_pipeTypes = {
QStringLiteral("StarTracker"),
QStringLiteral("SatelliteTracker"),
QStringLiteral("GS232Controller"),
QStringLiteral("Map")
};
const QStringList SkyMapSettings::m_pipeURIs = {
QStringLiteral("sdrangel.feature.startracker"),
QStringLiteral("sdrangel.feature.satellitetracker"),

Wyświetl plik

@ -66,7 +66,6 @@ struct SkyMapSettings
void applySettings(const QStringList& settingsKeys, const SkyMapSettings& settings);
QString getDebugString(const QStringList& settingsKeys, bool force=false) const;
static const QStringList m_pipeTypes;
static const QStringList m_pipeURIs;
};

Wyświetl plik

@ -49,7 +49,10 @@ KiwiSDRInput::KiwiSDRInput(DeviceAPI *deviceAPI) :
m_kiwiSDRWorkerThread(nullptr),
m_deviceDescription("KiwiSDR"),
m_running(false),
m_masterTimer(deviceAPI->getMasterTimer())
m_masterTimer(deviceAPI->getMasterTimer()),
m_latitude(std::numeric_limits<float>::quiet_NaN()),
m_longitude(std::numeric_limits<float>::quiet_NaN()),
m_altitude(std::numeric_limits<float>::quiet_NaN())
{
m_sampleFifo.setLabel(m_deviceDescription);
m_deviceAPI->setNbSourceStreams(1);
@ -237,6 +240,16 @@ bool KiwiSDRInput::handleMessage(const Message& message)
return true;
}
else if (KiwiSDRWorker::MsgReportPosition::match(message))
{
KiwiSDRWorker::MsgReportPosition& report = (KiwiSDRWorker::MsgReportPosition&) message;
m_latitude = report.getLatitude();
m_longitude = report.getLongitude();
m_altitude = report.getAltitude();
return true;
}
else if (MsgStartStop::match(message))
{
MsgStartStop& cmd = (MsgStartStop&) message;
@ -455,6 +468,9 @@ void KiwiSDRInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& re
void KiwiSDRInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response)
{
response.getKiwiSdrReport()->setStatus(getStatus());
response.getKiwiSdrReport()->setLatitude(m_latitude);
response.getKiwiSdrReport()->setLongitude(m_longitude);
response.getKiwiSdrReport()->setAltitude(m_altitude);
}
void KiwiSDRInput::webapiReverseSendSettings(const QList<QString>& deviceSettingsKeys, const KiwiSDRSettings& settings, bool force)

Wyświetl plik

@ -167,6 +167,9 @@ private:
const QTimer& m_masterTimer;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
float m_latitude;
float m_longitude;
float m_altitude;
int getStatus() const;
bool applySettings(const KiwiSDRSettings& settings, const QList<QString>& settingsKeys, bool force);

Wyświetl plik

@ -21,6 +21,7 @@
#include "kiwisdrworker.h"
MESSAGE_CLASS_DEFINITION(KiwiSDRWorker::MsgReportSampleRate, Message)
MESSAGE_CLASS_DEFINITION(KiwiSDRWorker::MsgReportPosition, Message)
KiwiSDRWorker::KiwiSDRWorker(SampleSinkFifo* sampleFifo) :
QObject(),
@ -124,6 +125,41 @@ void KiwiSDRWorker::onBinaryMessageReceived(const QByteArray &message)
emit updateStatus(2);
}
}
else if ((al.size() >= 2) && al[1].startsWith("load_cfg="))
{
QByteArray urlEncoded = al[1].mid(9).toLatin1();
QString json = QUrl::fromPercentEncoding(urlEncoded);
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
if (doc.isObject())
{
QJsonObject obj = doc.object();
if (obj.contains("rx_gps"))
{
QString gps = obj.value("rx_gps").toString();
QRegularExpression re("\\((-?[0-9]+(\\.[0-9]+)?), *(-?[0-9]+(\\.[0-9]+)?)\\)");
QRegularExpressionMatch match = re.match(gps);
if (match.hasMatch())
{
float latitude = match.captured(1).toFloat();
float longitude = match.captured(3).toFloat();
float altitude = 0.0f;
if (obj.contains("rx_asl")) {
altitude = (float) obj.value("rx_asl").toInt();
}
if (m_inputMessageQueue) {
m_inputMessageQueue->push(MsgReportPosition::create(latitude, longitude, altitude));
}
}
}
}
else
{
qDebug() << "KiwiSDRWorker::onBinaryMessageReceived - Document is not an object";
}
}
}
else if (message[0] == 'S' && message[1] == 'N' && message[2] == 'D')
{

Wyświetl plik

@ -53,6 +53,31 @@ public:
{ }
};
class MsgReportPosition : public Message {
MESSAGE_CLASS_DECLARATION
public:
float getLatitude() const { return m_latitude; }
float getLongitude() const { return m_longitude; }
float getAltitude() const { return m_altitude; }
static MsgReportPosition* create(float latitude, float longitude, float altitude) {
return new MsgReportPosition(latitude, longitude, altitude);
}
private:
float m_latitude;
float m_longitude;
float m_altitude;
MsgReportPosition(float latitude, float longitude, float altitude) :
Message(),
m_latitude(latitude),
m_longitude(longitude),
m_altitude(altitude)
{ }
};
KiwiSDRWorker(SampleSinkFifo* sampleFifo);
int getStatus() const { return m_status; }
void setInputMessageQueue(MessageQueue *messageQueue) { m_inputMessageQueue = messageQueue; }

Wyświetl plik

@ -345,7 +345,7 @@ QString SDRPlayV3Settings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("reverseAPIPort") || force) {
ostr << " m_reverseAPIPort: " << m_reverseAPIPort;
}
if (settingsKeys.contains("everseAPIDeviceIndex") || force) {
if (settingsKeys.contains("reverseAPIDeviceIndex") || force) {
ostr << " m_reverseAPIDeviceIndex: " << m_reverseAPIDeviceIndex;
}

Wyświetl plik

@ -238,7 +238,9 @@ set(sdrbase_SOURCES
util/flightinformation.cpp
util/ft8message.cpp
util/giro.cpp
util/goesxray.cpp
util/golay2312.cpp
util/grb.cpp
util/httpdownloadmanager.cpp
util/interpolation.cpp
util/kiwisdrlist.cpp
@ -266,13 +268,16 @@ set(sdrbase_SOURCES
util/samplesourceserializer.cpp
util/simpleserializer.cpp
util/serialutil.cpp
util/solardynamicsobservatory.cpp
#util/spinlock.cpp
util/spyserverlist.cpp
util/stix.cpp
util/rtty.cpp
util/uid.cpp
util/units.cpp
util/timeutil.cpp
util/visa.cpp
util/vlftransmitters.cpp
util/waypoints.cpp
util/weather.cpp
util/iot/device.cpp
@ -487,7 +492,9 @@ set(sdrbase_HEADERS
util/flightinformation.h
util/ft8message.h
util/giro.h
util/goesxray.h
util/golay2312.h
util/grb.h
util/httpdownloadmanager.h
util/incrementalarray.h
util/incrementalvector.h
@ -520,12 +527,15 @@ set(sdrbase_HEADERS
util/samplesourceserializer.h
util/simpleserializer.h
util/serialutil.h
util/solardynamicsobservatory.h
#util/spinlock.h
util/spyserverlist.h
util/stix.h
util/uid.h
util/units.h
util/timeutil.h
util/visa.h
util/vlftransmitters.h
util/waypoints.h
util/weather.h
util/iot/device.h

Wyświetl plik

@ -81,11 +81,28 @@ void AvailableChannelOrFeatureHandler::scanAvailableChannelsAndFeatures()
}
}
// Create lists of which channels and features have been added or removed
QStringList added;
QStringList removed;
for (const auto& channelOrFeature : availableChannelOrFeatureList)
{
if (m_availableChannelOrFeatureList.indexOfObject(channelOrFeature.m_object) < 0) {
added.append(channelOrFeature.getId());
}
}
for (const auto& channelOrFeature : m_availableChannelOrFeatureList)
{
if (availableChannelOrFeatureList.indexOfObject(channelOrFeature.m_object) < 0) {
removed.append(channelOrFeature.getId());
}
}
m_availableChannelOrFeatureList = availableChannelOrFeatureList;
// Signal if list has changed
if (changes) {
emit channelsOrFeaturesChanged(renameFrom, renameTo);
emit channelsOrFeaturesChanged(renameFrom, renameTo, removed, added);
}
}

Wyświetl plik

@ -78,7 +78,7 @@ private slots:
void handleFeatureRemoved(int featureSetIndex, Feature *feature);
signals:
void channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo); //!< Emitted when list of channels or features has changed
void channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo, const QStringList& removed, const QStringList& added); //!< Emitted when list of channels or features has changed
void messageEnqueued(MessageQueue *messageQueue); //!< Emitted when message enqueued to a pipe
};

Wyświetl plik

@ -1156,6 +1156,119 @@ bool ChannelWebAPIUtils::getDeviceReportList(unsigned int deviceIndex, const QSt
return false;
}
bool ChannelWebAPIUtils::getDevicePosition(unsigned int deviceIndex, QGeoCoordinate& position)
{
SWGSDRangel::SWGDeviceReport deviceReport;
if (getDeviceReport(deviceIndex, deviceReport))
{
QJsonObject *jsonObj = deviceReport.asJsonObject();
double latitude, longitude, altitude;
if (WebAPIUtils::getSubObjectDouble(*jsonObj, "latitude", latitude)
&& WebAPIUtils::getSubObjectDouble(*jsonObj, "longitude", longitude)
&& WebAPIUtils::getSubObjectDouble(*jsonObj, "altitude", altitude))
{
position.setLatitude(latitude);
position.setLongitude(longitude);
position.setAltitude(altitude);
// Done
return true;
}
else
{
//qWarning("ChannelWebAPIUtils::getDevicePosition: no latitude/longitude/altitude in device report");
return false;
}
}
return false;
}
bool ChannelWebAPIUtils::runFeature(unsigned int featureSetIndex, unsigned int featureIndex)
{
SWGSDRangel::SWGDeviceState runResponse;
QString errorResponse;
int httpRC;
FeatureSet *featureSet;
Feature *feature;
std::vector<FeatureSet*> featureSets = MainCore::instance()->getFeatureeSets();
if (featureSetIndex < featureSets.size())
{
runResponse.setState(new QString());
featureSet = featureSets[featureSetIndex];
if (featureIndex < (unsigned int)featureSet->getNumberOfFeatures())
{
feature = featureSet->getFeatureAt(featureIndex);
httpRC = feature->webapiRun(1, runResponse, errorResponse);
}
else
{
qDebug() << "ChannelWebAPIUtils::runFeature - no feature " << featureSetIndex << ':' << featureIndex;
return false;
}
}
else
{
qDebug() << "ChannelWebAPIUtils::runFeature - no feature set " << featureSetIndex;
return false;
}
if (httpRC/100 != 2)
{
qWarning("ChannelWebAPIUtils::runFeature: run error %d: %s",
httpRC, qPrintable(errorResponse));
return false;
}
return true;
}
bool ChannelWebAPIUtils::stopFeature(unsigned int featureSetIndex, unsigned int featureIndex)
{
SWGSDRangel::SWGDeviceState runResponse;
QString errorResponse;
int httpRC;
FeatureSet *featureSet;
Feature *feature;
std::vector<FeatureSet*> featureSets = MainCore::instance()->getFeatureeSets();
if (featureSetIndex < featureSets.size())
{
runResponse.setState(new QString());
featureSet = featureSets[featureSetIndex];
if (featureIndex < (unsigned int)featureSet->getNumberOfFeatures())
{
feature = featureSet->getFeatureAt(featureIndex);
httpRC = feature->webapiRun(0, runResponse, errorResponse);
}
else
{
qDebug() << "ChannelWebAPIUtils::stopFeature - no feature " << featureSetIndex << ':' << featureIndex;
return false;
}
}
else
{
qDebug() << "ChannelWebAPIUtils::stopFeature - no feature set " << featureSetIndex;
return false;
}
if (httpRC/100 != 2)
{
qWarning("ChannelWebAPIUtils::stopFeature: run error %d: %s",
httpRC, qPrintable(errorResponse));
return false;
}
return true;
}
bool ChannelWebAPIUtils::patchDeviceSetting(unsigned int deviceIndex, const QString &setting, int value)
{
SWGSDRangel::SWGDeviceSettings deviceSettingsResponse;
@ -1299,6 +1412,80 @@ bool ChannelWebAPIUtils::patchFeatureSetting(unsigned int featureSetIndex, unsig
}
}
bool ChannelWebAPIUtils::patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, const QJsonArray& value)
{
SWGSDRangel::SWGFeatureSettings featureSettingsResponse;
QString errorResponse;
int httpRC;
Feature *feature;
if (getFeatureSettings(featureSetIndex, featureIndex, featureSettingsResponse, feature))
{
// Patch settings
QJsonObject *jsonObj = featureSettingsResponse.asJsonObject();
// Set value
bool found = false;
for (QJsonObject::iterator it = jsonObj->begin(); it != jsonObj->end(); it++)
{
QJsonValue jsonValue = it.value();
if (jsonValue.isObject())
{
QJsonObject subObject = jsonValue.toObject();
if (subObject.contains(setting))
{
subObject[setting] = value;
it.value() = subObject;
found = true;
break;
}
}
}
if (!found)
{
for (QJsonObject::iterator it = jsonObj->begin(); it != jsonObj->end(); it++)
{
QJsonValueRef jsonValue = it.value();
if (jsonValue.isObject())
{
QJsonObject subObject = jsonValue.toObject();
subObject.insert(setting, value);
jsonValue = subObject;
}
}
}
QStringList featureSettingsKeys;
featureSettingsKeys.append(setting);
featureSettingsResponse.init();
featureSettingsResponse.fromJsonObject(*jsonObj);
SWGSDRangel::SWGErrorResponse errorResponse2;
httpRC = feature->webapiSettingsPutPatch(false, featureSettingsKeys, featureSettingsResponse, *errorResponse2.getMessage());
if (httpRC/100 == 2)
{
qDebug("ChannelWebAPIUtils::patchFeatureSetting: set feature setting %s OK", qPrintable(setting));
return true;
}
else
{
qWarning("ChannelWebAPIUtils::patchFeatureSetting: set feature setting %s error %d: %s",
qPrintable(setting), httpRC, qPrintable(*errorResponse2.getMessage()));
return false;
}
}
else
{
return false;
}
}
bool ChannelWebAPIUtils::patchChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, double value)
{
SWGSDRangel::SWGChannelSettings channelSettingsResponse;

Wyświetl plik

@ -22,6 +22,8 @@
#define SDRBASE_CHANNEL_CHANNELWEBAPIUTILS_H_
#include <QString>
#include <QJsonArray>
#include <QGeoCoordinate>
#include "SWGDeviceSettings.h"
#include "SWGDeviceReport.h"
@ -70,9 +72,13 @@ public:
static bool getDeviceSetting(unsigned int deviceIndex, const QString &setting, int &value);
static bool getDeviceReportValue(unsigned int deviceIndex, const QString &key, QString &value);
static bool getDeviceReportList(unsigned int deviceIndex, const QString &key, const QString &subKey, QList<int> &values);
static bool getDevicePosition(unsigned int deviceIndex, QGeoCoordinate& position);
static bool patchDeviceSetting(unsigned int deviceIndex, const QString &setting, int value);
static bool runFeature(unsigned int featureSetIndex, unsigned int featureIndex);
static bool stopFeature(unsigned int featureSetIndex, unsigned int featureIndex);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, const QString &value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, const QJsonArray& value);
static bool patchChannelSetting(unsigned int deviceSetIndex, unsigned int channeIndex, const QString &setting, double value);
static bool patchChannelSetting(unsigned int deviceSetIndex, unsigned int channeIndex, const QString &setting, const QJsonArray& value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, int &value);

Wyświetl plik

@ -86,6 +86,36 @@ bool FeatureWebAPIUtils::mapSetDateTime(const QDateTime& dateTime, int featureSe
}
}
// Find the specified target on the sky map
bool FeatureWebAPIUtils::skyMapFind(const QString& target, int featureSetIndex, int featureIndex)
{
Feature *feature = FeatureWebAPIUtils::getFeature(featureSetIndex, featureIndex, "sdrangel.feature.skymap");
if (feature != nullptr)
{
QString errorMessage;
QStringList featureActionKeys = {"find"};
SWGSDRangel::SWGFeatureActions query;
SWGSDRangel::SWGSkyMapActions *skyMapActions = new SWGSDRangel::SWGSkyMapActions();
skyMapActions->setFind(new QString(target));
query.setSkyMapActions(skyMapActions);
int httpRC = feature->webapiActionsPost(featureActionKeys, query, errorMessage);
if (httpRC/100 != 2)
{
qWarning() << "FeatureWebAPIUtils::skyMapFind: error " << httpRC << ":" << errorMessage;
return false;
}
return true;
}
else
{
qWarning("FeatureWebAPIUtils::skyMapFind: no Sky Map feature");
return false;
}
}
// Get first feature with the given URI
Feature* FeatureWebAPIUtils::getFeature(int& featureSetIndex, int& featureIndex, const QString& uri)
{
@ -180,3 +210,56 @@ bool FeatureWebAPIUtils::satelliteLOS(const QString name)
// Not currently required by any features
return true;
}
// Open a Sky Map feature and find the specified target
bool FeatureWebAPIUtils::openSkyMapAndFind(const QString& target)
{
return SkyMapOpener::open(target);
}
bool SkyMapOpener::open(const QString& target)
{
// Create a SkyMap feature
MainCore *mainCore = MainCore::instance();
PluginAPI::FeatureRegistrations *featureRegistrations = mainCore->getPluginManager()->getFeatureRegistrations();
int nbRegistrations = featureRegistrations->size();
int index = 0;
for (; index < nbRegistrations; index++)
{
if (featureRegistrations->at(index).m_featureId == "SkyMap") {
break;
}
}
if (index < nbRegistrations)
{
new SkyMapOpener(target);
MainCore::MsgAddFeature *msg = MainCore::MsgAddFeature::create(0, index);
mainCore->getMainMessageQueue()->push(msg);
return true;
}
else
{
qWarning() << "Sky Map feature not available";
return false;
}
}
SkyMapOpener::SkyMapOpener(const QString& target) :
m_target(target)
{
connect(MainCore::instance(), &MainCore::featureAdded, this, &SkyMapOpener::onSkyMapAdded);
}
void SkyMapOpener::onSkyMapAdded(int featureSetIndex, Feature *feature)
{
if (feature->getURI() == "sdrangel.feature.skymap")
{
disconnect(MainCore::instance(), &MainCore::featureAdded, this, &SkyMapOpener::onSkyMapAdded);
FeatureWebAPIUtils::skyMapFind(m_target, featureSetIndex, feature->getIndexInFeatureSet());
deleteLater();
}
}

Wyświetl plik

@ -27,11 +27,29 @@
class Feature;
class SkyMapOpener : public QObject {
Q_OBJECT
QString m_target;
private:
SkyMapOpener(const QString& target);
public:
static bool open(const QString& target);
private slots:
void onSkyMapAdded(int featureSetIndex, Feature *feature);
};
class SDRBASE_API FeatureWebAPIUtils
{
public:
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);
static bool openSkyMapAndFind(const QString& target);
static Feature *getFeature(int& featureSetIndex, int& featureIndex, const QString& uri);
static bool satelliteAOS(const QString name, const QDateTime aos, const QDateTime los);
static bool satelliteLOS(const QString name);

Wyświetl plik

@ -612,12 +612,16 @@ bool MainCore::getDeviceAndChannelIndexFromId(const QString& channelId, unsigned
bool MainCore::getFeatureIndexFromId(const QString& featureId, unsigned int &featureSetIndex, unsigned int &featureIndex)
{
const QRegularExpression re("[F]([0-9]+):([0-9]+)");
const QRegularExpression re("F([0-9]+)?:([0-9]+)");
QRegularExpressionMatch match = re.match(featureId);
if (match.hasMatch())
{
featureSetIndex = match.capturedTexts()[1].toInt();
if (match.capturedTexts()[1].isEmpty()) {
featureSetIndex = 0;
} else {
featureSetIndex = match.capturedTexts()[1].toInt();
}
featureIndex = match.capturedTexts()[2].toInt();
return true;
}

Wyświetl plik

@ -6684,6 +6684,9 @@ margin-bottom: 20px;
"SatelliteTrackerSettings" : {
"$ref" : "#/definitions/SatelliteTrackerSettings"
},
"SIDSettings" : {
"$ref" : "#/definitions/SIDSettings"
},
"SimplePTTSettings" : {
"$ref" : "#/definitions/SimplePTTSettings"
},
@ -8555,6 +8558,18 @@ margin-bottom: 20px;
"status" : {
"type" : "integer",
"description" : "0 for Idle, 1 for Connecting, 2 for Connected, 3 for Error, 4 for Disconnected"
},
"latitude" : {
"type" : "number",
"format" : "float"
},
"longitude" : {
"type" : "number",
"format" : "float"
},
"altitude" : {
"type" : "number",
"format" : "float"
}
},
"description" : "KiwiSDR"
@ -10050,6 +10065,10 @@ margin-bottom: 20px;
"format" : "float",
"description" : "Extruded height (from surface) for polygons"
},
"availableFrom" : {
"type" : "string",
"description" : "Date and time at which the item should first appear on 3D map"
},
"availableUntil" : {
"type" : "string",
"description" : "Date and time until after which this item should no longer appear on 3D map"
@ -10210,6 +10229,10 @@ margin-bottom: 20px;
"format" : "float",
"description" : "Extruded height (from surface) for polygons"
},
"availableFrom" : {
"type" : "string",
"description" : "Date and time at which the item should first appear on 3D map"
},
"availableUntil" : {
"type" : "string",
"description" : "Date and time until after which this item should no longer appear on 3D map"
@ -13761,6 +13784,52 @@ margin-bottom: 20px;
}
},
"description" : "SDRplayV3"
};
defs.SIDSettings = {
"properties" : {
"period" : {
"type" : "number",
"format" : "float"
},
"autosave" : {
"type" : "integer"
},
"autoload" : {
"type" : "integer"
},
"filename" : {
"type" : "integer"
},
"autosavePeriod" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"rgbColor" : {
"type" : "integer"
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIFeatureSetIndex" : {
"type" : "integer"
},
"reverseAPIFeatureIndex" : {
"type" : "integer"
},
"rollupState" : {
"$ref" : "#/definitions/RollupState"
}
},
"description" : "SID settings"
};
defs.SSBDemodReport = {
"properties" : {
@ -58865,7 +58934,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2024-03-13T16:45:40.518+01:00
Generated 2024-04-04T16:23:36.765+02:00
</div>
</div>
</div>

Wyświetl plik

@ -41,6 +41,8 @@ FeatureSettings:
$ref: "/doc/swagger/include/RigCtlServer.yaml#/RigCtlServerSettings"
SatelliteTrackerSettings:
$ref: "/doc/swagger/include/SatelliteTracker.yaml#/SatelliteTrackerSettings"
SIDSettings:
$ref: "/doc/swagger/include/SID.yaml#/SIDSettings"
SimplePTTSettings:
$ref: "/doc/swagger/include/SimplePTT.yaml#/SimplePTTSettings"
SkyMapSettings:

Some files were not shown because too many files have changed in this diff Show More