mirror of https://github.com/f4exb/sdrangel.git
Merge pull request #1986 from srcejon/freq_scanner
Sky Map feature plugin
This commit is contained in:
commit
fcd94aaaf8
|
@ -141,6 +141,7 @@ option(ENABLE_FEATURE_RIGCTLSERVER "Enable feature rigctlserver plugin" ON)
|
|||
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)
|
||||
|
||||
# on windows always build external libraries
|
||||
if(WIN32)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 357 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
|
@ -67,6 +67,7 @@ FreqScanner::FreqScanner(DeviceAPI *deviceAPI) :
|
|||
m_basebandSink(nullptr),
|
||||
m_running(false),
|
||||
m_basebandSampleRate(0),
|
||||
m_availableChannelHandler({}),
|
||||
m_scanDeviceSetIndex(0),
|
||||
m_scanChannelIndex(0),
|
||||
m_state(IDLE),
|
||||
|
@ -95,19 +96,8 @@ FreqScanner::FreqScanner(DeviceAPI *deviceAPI) :
|
|||
|
||||
start();
|
||||
|
||||
scanAvailableChannels();
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
this,
|
||||
&FreqScanner::handleChannelAdded
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelRemoved,
|
||||
this,
|
||||
&FreqScanner::handleChannelRemoved
|
||||
);
|
||||
QObject::connect(&m_availableChannelHandler, &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged, this, &FreqScanner::channelsChanged);
|
||||
m_availableChannelHandler.scanAvailableChannelsAndFeatures();
|
||||
|
||||
m_timeoutTimer.callOnTimeout(this, &FreqScanner::timeout);
|
||||
}
|
||||
|
@ -708,7 +698,7 @@ void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStri
|
|||
m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex);
|
||||
m_deviceAPI->addChannelSink(this, settings.m_streamIndex);
|
||||
m_deviceAPI->addChannelSinkAPI(this);
|
||||
scanAvailableChannels(); // re-scan
|
||||
//FIXME:scanAvailableChannels(); // re-scan
|
||||
emit streamIndexChanged(settings.m_streamIndex);
|
||||
}
|
||||
}
|
||||
|
@ -1116,186 +1106,18 @@ void FreqScanner::handleIndexInDeviceSetChanged(int index)
|
|||
m_basebandSink->setFifoLabel(fifoLabel);
|
||||
}
|
||||
|
||||
void FreqScanner::scanAvailableChannels()
|
||||
void FreqScanner::channelsChanged(const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
m_availableChannels.clear();
|
||||
|
||||
DSPDeviceSourceEngine* deviceSourceEngine = getDeviceAPI()->getDeviceSourceEngine();
|
||||
|
||||
if (deviceSourceEngine)
|
||||
{
|
||||
for (int chi = 0; chi < getDeviceAPI()->getNbSinkChannels(); chi++) // Rx channels
|
||||
{
|
||||
ChannelAPI* channel = getDeviceAPI()->getChanelSinkAPIAt(chi);
|
||||
|
||||
if (channel->getIndexInDeviceSet() == getIndexInDeviceSet()) { // Exclude oneself
|
||||
continue;
|
||||
}
|
||||
|
||||
FreqScannerSettings::AvailableChannel availableChannel =
|
||||
FreqScannerSettings::AvailableChannel{
|
||||
channel->getDeviceSetIndex(),
|
||||
channel->getIndexInDeviceSet(),
|
||||
-1
|
||||
};
|
||||
m_availableChannels[channel] = availableChannel;
|
||||
QObject::connect(
|
||||
channel,
|
||||
&ChannelAPI::streamIndexChanged,
|
||||
[=](int streamIndex){
|
||||
this->handleChannelStreamIndexChanged(streamIndex, channel);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DSPDeviceMIMOEngine *deviceMIMOEngine = getDeviceAPI()->getDeviceMIMOEngine();
|
||||
|
||||
if (deviceMIMOEngine)
|
||||
{
|
||||
for (int chi = 0; chi < getDeviceAPI()->getNbSinkChannels(); chi++) // Rx channels
|
||||
{
|
||||
ChannelAPI* channel = getDeviceAPI()->getChanelSinkAPIAt(chi);
|
||||
|
||||
if (channel->getIndexInDeviceSet() == getIndexInDeviceSet()) { // Exclude oneself
|
||||
continue;
|
||||
}
|
||||
|
||||
// Single Rx on the same I/Q stream
|
||||
if ((channel->getNbSinkStreams() == 1)
|
||||
&& (channel->getNbSourceStreams() == 0)
|
||||
&& (channel->getStreamIndex() == m_settings.m_streamIndex))
|
||||
{
|
||||
FreqScannerSettings::AvailableChannel availableChannel =
|
||||
FreqScannerSettings::AvailableChannel{
|
||||
channel->getDeviceSetIndex(),
|
||||
channel->getIndexInDeviceSet(),
|
||||
channel->getStreamIndex()
|
||||
};
|
||||
m_availableChannels[channel] = availableChannel;
|
||||
QObject::connect(
|
||||
channel,
|
||||
&ChannelAPI::streamIndexChanged,
|
||||
[=](int streamIndex){
|
||||
this->handleChannelStreamIndexChanged(streamIndex, channel);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyUpdateChannels();
|
||||
m_availableChannels = m_availableChannelHandler.getAvailableChannelOrFeatureList();
|
||||
notifyUpdateChannels(renameFrom, renameTo);
|
||||
}
|
||||
|
||||
void FreqScanner::handleChannelAdded(int deviceSetIndex, ChannelAPI* channel)
|
||||
{
|
||||
if (deviceSetIndex != getDeviceSetIndex()) { // Can control channels only in the same device set
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug("FreqScanner::handleChannelAdded: deviceSetIndex: %d:%d channel: %s (%p)",
|
||||
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
|
||||
DSPDeviceSourceEngine* deviceSourceEngine = getDeviceAPI()->getDeviceSourceEngine();
|
||||
|
||||
if (deviceSourceEngine)
|
||||
{
|
||||
FreqScannerSettings::AvailableChannel availableChannel =
|
||||
FreqScannerSettings::AvailableChannel{ deviceSetIndex, channel->getIndexInDeviceSet(), -1};
|
||||
m_availableChannels[channel] = availableChannel;
|
||||
QObject::connect(
|
||||
channel,
|
||||
&ChannelAPI::streamIndexChanged,
|
||||
[=](int streamIndex){
|
||||
this->handleChannelStreamIndexChanged(streamIndex, channel);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
DSPDeviceMIMOEngine *deviceMIMOEngine = getDeviceAPI()->getDeviceMIMOEngine();
|
||||
|
||||
if (deviceMIMOEngine
|
||||
&& (channel->getNbSinkStreams() == 1)
|
||||
&& (channel->getNbSourceStreams() == 0)
|
||||
&& (channel->getStreamIndex() == m_settings.m_streamIndex))
|
||||
{
|
||||
FreqScannerSettings::AvailableChannel availableChannel =
|
||||
FreqScannerSettings::AvailableChannel{
|
||||
deviceSetIndex,
|
||||
channel->getIndexInDeviceSet(),
|
||||
channel->getStreamIndex()
|
||||
};
|
||||
m_availableChannels[channel] = availableChannel;
|
||||
QObject::connect(
|
||||
channel,
|
||||
&ChannelAPI::streamIndexChanged,
|
||||
[=](int streamIndex){
|
||||
this->handleChannelStreamIndexChanged(streamIndex, channel);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
notifyUpdateChannels();
|
||||
}
|
||||
|
||||
void FreqScanner::handleChannelRemoved(int deviceSetIndex, ChannelAPI* channel)
|
||||
{
|
||||
if (deviceSetIndex != getDeviceSetIndex()) { // Can control channels only in the same device set
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug("FreqScanner::handleChannelRemoved: deviceSetIndex: %d:%d channel: %s (%p)",
|
||||
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
|
||||
DSPDeviceSourceEngine* deviceSourceEngine = getDeviceAPI()->getDeviceSourceEngine();
|
||||
DSPDeviceMIMOEngine *deviceMIMOEngine = getDeviceAPI()->getDeviceMIMOEngine();
|
||||
|
||||
if (deviceSourceEngine || deviceMIMOEngine) {
|
||||
m_availableChannels.remove(channel);
|
||||
}
|
||||
|
||||
notifyUpdateChannels();
|
||||
}
|
||||
|
||||
void FreqScanner::handleChannelStreamIndexChanged(int streamIndex, ChannelAPI* channel)
|
||||
{
|
||||
qDebug("FreqScanner::handleChannelStreamIndexChanged: channel: %s (%p) stream: %d",
|
||||
qPrintable(channel->getURI()), channel, streamIndex);
|
||||
if (streamIndex != m_settings.m_streamIndex) // channel has moved to another I/Q stream
|
||||
{
|
||||
m_availableChannels.remove(channel);
|
||||
notifyUpdateChannels();
|
||||
}
|
||||
else if (!m_availableChannels.contains(channel)) // if channel has been tracked before put back it in the list
|
||||
{
|
||||
FreqScannerSettings::AvailableChannel availableChannel =
|
||||
FreqScannerSettings::AvailableChannel{
|
||||
getDeviceSetIndex(),
|
||||
channel->getIndexInDeviceSet(),
|
||||
channel->getStreamIndex()
|
||||
};
|
||||
m_availableChannels[channel] = availableChannel;
|
||||
notifyUpdateChannels();
|
||||
}
|
||||
}
|
||||
|
||||
void FreqScanner::notifyUpdateChannels()
|
||||
void FreqScanner::notifyUpdateChannels(const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportChannels* msgToGUI = MsgReportChannels::create();
|
||||
QList<FreqScannerSettings::AvailableChannel>& msgChannels = msgToGUI->getChannels();
|
||||
QHash<ChannelAPI*, FreqScannerSettings::AvailableChannel>::iterator it = m_availableChannels.begin();
|
||||
|
||||
for (; it != m_availableChannels.end(); ++it)
|
||||
{
|
||||
FreqScannerSettings::AvailableChannel msgChannel =
|
||||
FreqScannerSettings::AvailableChannel{
|
||||
it->m_deviceSetIndex,
|
||||
it->m_channelIndex,
|
||||
it->m_streamIndex
|
||||
};
|
||||
msgChannels.push_back(msgChannel);
|
||||
}
|
||||
|
||||
MsgReportChannels* msgToGUI = MsgReportChannels::create(renameFrom, renameTo);
|
||||
msgToGUI->getChannels() = m_availableChannels;
|
||||
getMessageQueueToGUI()->push(msgToGUI);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "dsp/basebandsamplesink.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "util/message.h"
|
||||
#include "availablechannelorfeaturehandler.h"
|
||||
|
||||
#include "freqscannerbaseband.h"
|
||||
#include "freqscannersettings.h"
|
||||
|
@ -71,17 +72,23 @@ public:
|
|||
|
||||
public:
|
||||
|
||||
QList<FreqScannerSettings::AvailableChannel>& getChannels() { return m_channels; }
|
||||
AvailableChannelOrFeatureList& getChannels() { return m_channels; }
|
||||
const QStringList& getRenameFrom() const { return m_renameFrom; }
|
||||
const QStringList& getRenameTo() const { return m_renameTo; }
|
||||
|
||||
static MsgReportChannels* create() {
|
||||
return new MsgReportChannels();
|
||||
static MsgReportChannels* create(const QStringList& renameFrom, const QStringList& renameTo) {
|
||||
return new MsgReportChannels(renameFrom, renameTo);
|
||||
}
|
||||
|
||||
private:
|
||||
QList<FreqScannerSettings::AvailableChannel> m_channels;
|
||||
AvailableChannelOrFeatureList m_channels;
|
||||
QStringList m_renameFrom;
|
||||
QStringList m_renameTo;
|
||||
|
||||
MsgReportChannels() :
|
||||
Message()
|
||||
MsgReportChannels(const QStringList& renameFrom, const QStringList& renameTo) :
|
||||
Message(),
|
||||
m_renameFrom(renameFrom),
|
||||
m_renameTo(renameTo)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -369,7 +376,8 @@ private:
|
|||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
QHash<ChannelAPI*, FreqScannerSettings::AvailableChannel> m_availableChannels;
|
||||
AvailableChannelOrFeatureList m_availableChannels;
|
||||
AvailableChannelOrFeatureHandler m_availableChannelHandler;
|
||||
|
||||
unsigned int m_scanDeviceSetIndex;
|
||||
unsigned int m_scanChannelIndex;
|
||||
|
@ -402,8 +410,8 @@ private:
|
|||
);
|
||||
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
|
||||
|
||||
void scanAvailableChannels();
|
||||
void notifyUpdateChannels();
|
||||
//void scanAvailableChannels();
|
||||
void notifyUpdateChannels(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void startScan();
|
||||
void stopScan();
|
||||
void initScan();
|
||||
|
@ -416,9 +424,7 @@ private:
|
|||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
void handleIndexInDeviceSetChanged(int index);
|
||||
void handleChannelAdded(int deviceSetIndex, ChannelAPI* channel);
|
||||
void handleChannelRemoved(int deviceSetIndex, ChannelAPI* channel);
|
||||
void handleChannelStreamIndexChanged(int streamIndex, ChannelAPI* channel);
|
||||
void channelsChanged(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void timeout();
|
||||
|
||||
};
|
||||
|
|
|
@ -118,8 +118,7 @@ bool FreqScannerGUI::handleMessage(const Message& message)
|
|||
else if (FreqScanner::MsgReportChannels::match(message))
|
||||
{
|
||||
FreqScanner::MsgReportChannels& report = (FreqScanner::MsgReportChannels&)message;
|
||||
m_availableChannels = report.getChannels();
|
||||
updateChannelsList(m_availableChannels);
|
||||
updateChannelsList(report.getChannels(), report.getRenameFrom(), report.getRenameTo());
|
||||
return true;
|
||||
}
|
||||
else if (FreqScanner::MsgStatus::match(message))
|
||||
|
@ -212,7 +211,7 @@ bool FreqScannerGUI::handleMessage(const Message& message)
|
|||
return false;
|
||||
}
|
||||
|
||||
void FreqScannerGUI::updateChannelsCombo(QComboBox *combo, const QList<FreqScannerSettings::AvailableChannel>& channels, const QString& channel, bool empty)
|
||||
void FreqScannerGUI::updateChannelsCombo(QComboBox *combo, const AvailableChannelOrFeatureList& channels, const QString& channel, bool empty)
|
||||
{
|
||||
combo->blockSignals(true);
|
||||
combo->clear();
|
||||
|
@ -223,17 +222,8 @@ void FreqScannerGUI::updateChannelsCombo(QComboBox *combo, const QList<FreqScann
|
|||
for (const auto& channel : channels)
|
||||
{
|
||||
// Add channels in this device set, other than ourself (Don't use ChannelGUI::getDeviceSetIndex()/getIndex() as not valid when this is first called)
|
||||
if ((channel.m_deviceSetIndex == m_freqScanner->getDeviceSetIndex()) && (channel.m_channelIndex != m_freqScanner->getIndexInDeviceSet()))
|
||||
{
|
||||
QString name;
|
||||
|
||||
if (channel.m_streamIndex < 0) { // Rx
|
||||
name = QString("R%1:%2").arg(channel.m_deviceSetIndex).arg(channel.m_channelIndex);
|
||||
} else { // MIMO
|
||||
name = QString("M%1:%2.%3").arg(channel.m_deviceSetIndex).arg(channel.m_channelIndex).arg(channel.m_streamIndex);
|
||||
}
|
||||
|
||||
combo->addItem(name);
|
||||
if ((channel.m_superIndex == m_freqScanner->getDeviceSetIndex()) && (channel.m_index != m_freqScanner->getIndexInDeviceSet())) {
|
||||
combo->addItem(channel.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,8 +240,29 @@ void FreqScannerGUI::updateChannelsCombo(QComboBox *combo, const QList<FreqScann
|
|||
combo->blockSignals(false);
|
||||
}
|
||||
|
||||
void FreqScannerGUI::updateChannelsList(const QList<FreqScannerSettings::AvailableChannel>& channels)
|
||||
void FreqScannerGUI::updateChannelsList(const AvailableChannelOrFeatureList& channels, const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
m_availableChannels = channels;
|
||||
|
||||
// Update channel setting if it has been renamed
|
||||
if (renameFrom.contains(m_settings.m_channel))
|
||||
{
|
||||
m_settings.m_channel = renameTo[renameFrom.indexOf(m_settings.m_channel)];
|
||||
applySetting("channel");
|
||||
}
|
||||
bool rename = false;
|
||||
for (auto& setting : m_settings.m_frequencySettings)
|
||||
{
|
||||
if (renameFrom.contains(setting.m_channel))
|
||||
{
|
||||
setting.m_channel = renameTo[renameFrom.indexOf(setting.m_channel)];
|
||||
rename = true;
|
||||
}
|
||||
}
|
||||
if (rename) {
|
||||
applySetting("frequencySettings");
|
||||
}
|
||||
|
||||
updateChannelsCombo(ui->channels, channels, m_settings.m_channel, false);
|
||||
|
||||
for (int row = 0; row < ui->table->rowCount(); row++)
|
||||
|
@ -515,10 +526,12 @@ void FreqScannerGUI::applySetting(const QString& settingsKey)
|
|||
|
||||
void FreqScannerGUI::applySettings(const QStringList& settingsKeys, bool force)
|
||||
{
|
||||
m_settingsKeys.append(settingsKeys);
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
FreqScanner::MsgConfigureFreqScanner* message = FreqScanner::MsgConfigureFreqScanner::create(m_settings, settingsKeys, force);
|
||||
FreqScanner::MsgConfigureFreqScanner* message = FreqScanner::MsgConfigureFreqScanner::create(m_settings, m_settingsKeys, force);
|
||||
m_freqScanner->getInputMessageQueue()->push(message);
|
||||
m_settingsKeys.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ private:
|
|||
ChannelMarker m_channelMarker;
|
||||
RollupState m_rollupState;
|
||||
FreqScannerSettings m_settings;
|
||||
QList<QString> m_settingsKeys;
|
||||
qint64 m_deviceCenterFrequency;
|
||||
bool m_doApplySettings;
|
||||
|
||||
|
@ -82,7 +83,7 @@ private:
|
|||
|
||||
QMenu *m_menu;
|
||||
|
||||
QList<FreqScannerSettings::AvailableChannel> m_availableChannels;
|
||||
AvailableChannelOrFeatureList m_availableChannels;
|
||||
|
||||
explicit FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||
virtual ~FreqScannerGUI();
|
||||
|
@ -98,8 +99,8 @@ private:
|
|||
void addRow(const FreqScannerSettings::FrequencySettings& frequencySettings);
|
||||
void updateAnnotation(int row);
|
||||
void updateAnnotations();
|
||||
void updateChannelsCombo(QComboBox *combo, const QList<FreqScannerSettings::AvailableChannel>& channels, const QString& channel, bool empty);
|
||||
void updateChannelsList(const QList<FreqScannerSettings::AvailableChannel>& channels);
|
||||
void updateChannelsCombo(QComboBox *combo, const AvailableChannelOrFeatureList& channels, const QString& channel, bool empty);
|
||||
void updateChannelsList(const AvailableChannelOrFeatureList& channels, const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void setAllEnabled(bool enable);
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
|
|
|
@ -31,17 +31,6 @@ class ChannelAPI;
|
|||
|
||||
struct FreqScannerSettings
|
||||
{
|
||||
struct AvailableChannel
|
||||
{
|
||||
int m_deviceSetIndex;
|
||||
int m_channelIndex;
|
||||
int m_streamIndex;
|
||||
|
||||
AvailableChannel() = default;
|
||||
AvailableChannel(const AvailableChannel&) = default;
|
||||
AvailableChannel& operator=(const AvailableChannel&) = default;
|
||||
};
|
||||
|
||||
struct FrequencySettings {
|
||||
qint64 m_frequency;
|
||||
bool m_enabled;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// Copyright (C) 2021-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 //
|
||||
|
@ -67,10 +67,12 @@ const char * const RadioAstronomy::m_channelIdURI = "sdrangel.channel.radioastro
|
|||
const char * const RadioAstronomy::m_channelId = "RadioAstronomy";
|
||||
|
||||
RadioAstronomy::RadioAstronomy(DeviceAPI *deviceAPI) :
|
||||
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_basebandSampleRate(0),
|
||||
m_sweeping(false)
|
||||
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_basebandSampleRate(0),
|
||||
m_availableFeatureHandler({"sdrangel.feature.startracker"}, QStringList{"startracker.target"}),
|
||||
m_availableRotatorHandler({"sdrangel.feature.gs232controller"}),
|
||||
m_sweeping(false)
|
||||
{
|
||||
qDebug("RadioAstronomy::RadioAstronomy");
|
||||
setObjectName(m_channelId);
|
||||
|
@ -105,17 +107,26 @@ RadioAstronomy::RadioAstronomy(DeviceAPI *deviceAPI) :
|
|||
&RadioAstronomy::handleIndexInDeviceSetChanged
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureAdded,
|
||||
&m_availableFeatureHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&RadioAstronomy::handleFeatureAdded
|
||||
&RadioAstronomy::handleFeatureMessageQueue
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureRemoved,
|
||||
&m_availableFeatureHandler,
|
||||
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
||||
this,
|
||||
&RadioAstronomy::handleFeatureRemoved
|
||||
&RadioAstronomy::featuresChanged
|
||||
);
|
||||
m_availableFeatureHandler.scanAvailableChannelsAndFeatures();
|
||||
|
||||
QObject::connect(
|
||||
&m_availableRotatorHandler,
|
||||
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
||||
this,
|
||||
&RadioAstronomy::rotatorsChanged
|
||||
);
|
||||
m_availableRotatorHandler.scanAvailableChannelsAndFeatures();
|
||||
|
||||
m_sweepTimer.setSingleShot(true);
|
||||
}
|
||||
|
@ -123,17 +134,20 @@ RadioAstronomy::RadioAstronomy(DeviceAPI *deviceAPI) :
|
|||
RadioAstronomy::~RadioAstronomy()
|
||||
{
|
||||
qDebug("RadioAstronomy::~RadioAstronomy");
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureRemoved,
|
||||
QObject::disconnect(&m_availableFeatureHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&RadioAstronomy::handleFeatureRemoved
|
||||
&RadioAstronomy::handleFeatureMessageQueue
|
||||
);
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureAdded,
|
||||
QObject::disconnect(&m_availableFeatureHandler,
|
||||
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
||||
this,
|
||||
&RadioAstronomy::handleFeatureAdded
|
||||
&RadioAstronomy::featuresChanged
|
||||
);
|
||||
QObject::disconnect(&m_availableRotatorHandler,
|
||||
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
||||
this,
|
||||
&RadioAstronomy::rotatorsChanged
|
||||
);
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
|
@ -194,8 +208,6 @@ void RadioAstronomy::start()
|
|||
m_basebandSink->getInputMessageQueue()->push(new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency));
|
||||
m_basebandSink->getInputMessageQueue()->push(RadioAstronomyBaseband::MsgConfigureRadioAstronomyBaseband::create(m_settings, true));
|
||||
m_worker->getInputMessageQueue()->push(RadioAstronomyWorker::MsgConfigureRadioAstronomyWorker::create(m_settings, true));
|
||||
|
||||
scanAvailableFeatures();
|
||||
}
|
||||
|
||||
void RadioAstronomy::stop()
|
||||
|
@ -330,7 +342,8 @@ bool RadioAstronomy::handleMessage(const Message& cmd)
|
|||
}
|
||||
else if (MsgScanAvailableFeatures::match(cmd))
|
||||
{
|
||||
scanAvailableFeatures();
|
||||
notifyUpdateFeatures({}, {});
|
||||
notifyUpdateRotators({}, {});
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -753,22 +766,14 @@ void RadioAstronomy::applySettings(const RadioAstronomySettings& settings, bool
|
|||
{
|
||||
if (!settings.m_starTracker.isEmpty())
|
||||
{
|
||||
Feature *feature = nullptr;
|
||||
|
||||
for (const auto& fval : m_availableFeatures)
|
||||
int index = m_availableFeatures.indexOfLongId(settings.m_starTracker);
|
||||
if (index >= 0)
|
||||
{
|
||||
QString starTrackerText = tr("F%1:%2 %3").arg(fval.m_featureSetIndex).arg(fval.m_featureIndex).arg(fval.m_type);
|
||||
|
||||
if (settings.m_starTracker == starTrackerText)
|
||||
{
|
||||
feature = m_availableFeatures.key(fval);
|
||||
break;
|
||||
}
|
||||
m_selectedPipe = m_availableFeatures[index].m_object;
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
m_selectedPipe = feature;
|
||||
} else {
|
||||
else
|
||||
{
|
||||
m_selectedPipe = nullptr;
|
||||
qDebug() << "RadioAstronomy::applySettings: No plugin corresponding to target " << settings.m_starTracker;
|
||||
}
|
||||
}
|
||||
|
@ -1246,145 +1251,39 @@ void RadioAstronomy::handleIndexInDeviceSetChanged(int index)
|
|||
m_basebandSink->setFifoLabel(fifoLabel);
|
||||
}
|
||||
|
||||
void RadioAstronomy::scanAvailableFeatures()
|
||||
void RadioAstronomy::featuresChanged(const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
qDebug("RadioAstronomy::scanAvailableFeatures");
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
MessagePipes& messagePipes = mainCore->getMessagePipes();
|
||||
std::vector<FeatureSet*>& featureSets = mainCore->getFeatureeSets();
|
||||
m_availableFeatures.clear();
|
||||
m_rotators.clear();
|
||||
|
||||
for (const auto& featureSet : featureSets)
|
||||
{
|
||||
for (int fei = 0; fei < featureSet->getNumberOfFeatures(); fei++)
|
||||
{
|
||||
Feature *feature = featureSet->getFeatureAt(fei);
|
||||
|
||||
if (RadioAstronomySettings::m_pipeURIs.contains(feature->getURI()))
|
||||
{
|
||||
if (!m_availableFeatures.contains(feature))
|
||||
{
|
||||
qDebug("RadioAstronomy::scanAvailableFeatures: register %d:%d %s (%p)",
|
||||
featureSet->getIndex(), fei, qPrintable(feature->getURI()), feature);
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(feature, this, "startracker.target");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handleFeatureMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
connect(pipe, SIGNAL(toBeDeleted(int, QObject*)), this, SLOT(handleMessagePipeToBeDeleted(int, QObject*)));
|
||||
RadioAstronomySettings::AvailableFeature availableFeature =
|
||||
RadioAstronomySettings::AvailableFeature{featureSet->getIndex(), fei, feature->getIdentifier()};
|
||||
m_availableFeatures[feature] = availableFeature;
|
||||
}
|
||||
}
|
||||
else if (feature->getURI() == "sdrangel.feature.gs232controller")
|
||||
{
|
||||
RadioAstronomySettings::AvailableFeature rotator =
|
||||
RadioAstronomySettings::AvailableFeature{featureSet->getIndex(), fei, feature->getIdentifier()};
|
||||
m_rotators[feature] = rotator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyUpdateFeatures();
|
||||
notifyUpdateRotators();
|
||||
m_availableFeatures = m_availableFeatureHandler.getAvailableChannelOrFeatureList();
|
||||
notifyUpdateFeatures(renameFrom, renameTo);
|
||||
}
|
||||
|
||||
void RadioAstronomy::notifyUpdateFeatures()
|
||||
|
||||
void RadioAstronomy::notifyUpdateFeatures(const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportAvailableFeatures *msg = MsgReportAvailableFeatures::create();
|
||||
msg->getFeatures() = m_availableFeatures.values();
|
||||
MsgReportAvailableFeatures *msg = MsgReportAvailableFeatures::create(renameFrom, renameTo);
|
||||
msg->getFeatures() = m_availableFeatures;
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void RadioAstronomy::notifyUpdateRotators()
|
||||
void RadioAstronomy::rotatorsChanged(const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
m_rotators = m_availableRotatorHandler.getAvailableChannelOrFeatureList();
|
||||
notifyUpdateRotators(renameFrom, renameTo);
|
||||
}
|
||||
|
||||
void RadioAstronomy::notifyUpdateRotators(const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportAvailableRotators *msg = MsgReportAvailableRotators::create();
|
||||
msg->getFeatures() = m_rotators.values();
|
||||
MsgReportAvailableRotators *msg = MsgReportAvailableRotators::create(renameFrom, renameTo);
|
||||
msg->getFeatures() = m_rotators;
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void RadioAstronomy::handleFeatureAdded(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
qDebug("RadioAstronomy::handleFeatureAdded: featureSetIndex: %d:%d feature: %s (%p)",
|
||||
featureSetIndex, feature->getIndexInFeatureSet(), qPrintable(feature->getURI()), feature);
|
||||
FeatureSet *featureSet = MainCore::instance()->getFeatureeSets()[featureSetIndex];
|
||||
|
||||
if (RadioAstronomySettings::m_pipeURIs.contains(feature->getURI()))
|
||||
{
|
||||
int fei = feature->getIndexInFeatureSet();
|
||||
|
||||
if (!m_availableFeatures.contains(feature))
|
||||
{
|
||||
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(feature, this, "startracker.target");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handleFeatureMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
QObject::connect(
|
||||
pipe,
|
||||
&ObjectPipe::toBeDeleted,
|
||||
this,
|
||||
&RadioAstronomy::handleMessagePipeToBeDeleted
|
||||
);
|
||||
}
|
||||
|
||||
RadioAstronomySettings::AvailableFeature availableFeature =
|
||||
RadioAstronomySettings::AvailableFeature{featureSet->getIndex(), fei, feature->getIdentifier()};
|
||||
m_availableFeatures[feature] = availableFeature;
|
||||
|
||||
notifyUpdateFeatures();
|
||||
}
|
||||
else if (feature->getURI() == "sdrangel.feature.gs232controller")
|
||||
{
|
||||
if (!m_rotators.contains(feature))
|
||||
{
|
||||
RadioAstronomySettings::AvailableFeature rotator =
|
||||
RadioAstronomySettings::AvailableFeature{featureSet->getIndex(), feature->getIndexInFeatureSet(), feature->getIdentifier()};
|
||||
m_rotators[feature] = rotator;
|
||||
}
|
||||
|
||||
notifyUpdateRotators();
|
||||
}
|
||||
}
|
||||
|
||||
void RadioAstronomy::handleFeatureRemoved(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
qDebug("RadioAstronomy::handleFeatureRemoved: featureSetIndex: %d (%p)", featureSetIndex, feature);
|
||||
|
||||
if (m_rotators.contains(feature))
|
||||
{
|
||||
m_rotators.remove(feature);
|
||||
notifyUpdateRotators();
|
||||
}
|
||||
}
|
||||
|
||||
void RadioAstronomy::handleMessagePipeToBeDeleted(int reason, QObject* object)
|
||||
{
|
||||
if ((reason == 0) && m_availableFeatures.contains((Feature*) object)) // producer (feature)
|
||||
{
|
||||
qDebug("RadioAstronomy::handleMessagePipeToBeDeleted: removing feature at (%p)", object);
|
||||
m_availableFeatures.remove((Feature*) object);
|
||||
notifyUpdateFeatures();
|
||||
}
|
||||
}
|
||||
|
||||
void RadioAstronomy::handleFeatureMessageQueue(MessageQueue* messageQueue)
|
||||
{
|
||||
Message* message;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// Copyright (C) 2021-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 //
|
||||
|
@ -31,6 +31,7 @@
|
|||
#include "dsp/basebandsamplesink.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "util/message.h"
|
||||
#include "availablechannelorfeaturehandler.h"
|
||||
|
||||
#include "radioastronomybaseband.h"
|
||||
#include "radioastronomysettings.h"
|
||||
|
@ -329,17 +330,23 @@ public:
|
|||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QList<RadioAstronomySettings::AvailableFeature>& getFeatures() { return m_availableFeatures; }
|
||||
AvailableChannelOrFeatureList& getFeatures() { return m_availableFeatures; }
|
||||
const QStringList& getRenameFrom() const { return m_renameFrom; }
|
||||
const QStringList& getRenameTo() const { return m_renameTo; }
|
||||
|
||||
static MsgReportAvailableFeatures* create() {
|
||||
return new MsgReportAvailableFeatures();
|
||||
static MsgReportAvailableFeatures* create(const QStringList& renameFrom, const QStringList& renameTo) {
|
||||
return new MsgReportAvailableFeatures(renameFrom, renameTo);
|
||||
}
|
||||
|
||||
private:
|
||||
QList<RadioAstronomySettings::AvailableFeature> m_availableFeatures;
|
||||
AvailableChannelOrFeatureList m_availableFeatures;
|
||||
QStringList m_renameFrom;
|
||||
QStringList m_renameTo;
|
||||
|
||||
MsgReportAvailableFeatures() :
|
||||
Message()
|
||||
MsgReportAvailableFeatures(const QStringList& renameFrom, const QStringList& renameTo) :
|
||||
Message(),
|
||||
m_renameFrom(renameFrom),
|
||||
m_renameTo(renameTo)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -347,17 +354,24 @@ public:
|
|||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QList<RadioAstronomySettings::AvailableFeature>& getFeatures() { return m_availableFeatures; }
|
||||
AvailableChannelOrFeatureList& getFeatures() { return m_availableFeatures; }
|
||||
const QStringList& getRenameFrom() const { return m_renameFrom; }
|
||||
const QStringList& getRenameTo() const { return m_renameTo; }
|
||||
|
||||
static MsgReportAvailableRotators* create() {
|
||||
return new MsgReportAvailableRotators();
|
||||
|
||||
static MsgReportAvailableRotators* create(const QStringList& renameFrom, const QStringList& renameTo) {
|
||||
return new MsgReportAvailableRotators(renameFrom, renameTo);
|
||||
}
|
||||
|
||||
private:
|
||||
QList<RadioAstronomySettings::AvailableFeature> m_availableFeatures;
|
||||
AvailableChannelOrFeatureList m_availableFeatures;
|
||||
QStringList m_renameFrom;
|
||||
QStringList m_renameTo;
|
||||
|
||||
MsgReportAvailableRotators() :
|
||||
Message()
|
||||
MsgReportAvailableRotators(const QStringList& renameFrom, const QStringList& renameTo) :
|
||||
Message(),
|
||||
m_renameFrom(renameFrom),
|
||||
m_renameTo(renameTo)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -448,8 +462,10 @@ private:
|
|||
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
|
||||
qint64 m_centerFrequency;
|
||||
|
||||
QHash<Feature*, RadioAstronomySettings::AvailableFeature> m_availableFeatures;
|
||||
QHash<Feature*, RadioAstronomySettings::AvailableFeature> m_rotators;
|
||||
AvailableChannelOrFeatureHandler m_availableFeatureHandler;
|
||||
AvailableChannelOrFeatureList m_availableFeatures;
|
||||
AvailableChannelOrFeatureHandler m_availableRotatorHandler;
|
||||
AvailableChannelOrFeatureList m_rotators;
|
||||
QObject *m_selectedPipe;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
|
@ -482,9 +498,8 @@ private:
|
|||
void sweepStart();
|
||||
void startCal(bool hot);
|
||||
void calComplete(MsgCalComplete* report);
|
||||
void scanAvailableFeatures();
|
||||
void notifyUpdateFeatures();
|
||||
void notifyUpdateRotators();
|
||||
void notifyUpdateFeatures(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void notifyUpdateRotators(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
|
@ -496,9 +511,8 @@ private slots:
|
|||
void sweepNext();
|
||||
void sweepComplete();
|
||||
void handleIndexInDeviceSetChanged(int index);
|
||||
void handleFeatureAdded(int featureSetIndex, Feature *feature);
|
||||
void handleFeatureRemoved(int featureSetIndex, Feature *feature);
|
||||
void handleMessagePipeToBeDeleted(int reason, QObject* object);
|
||||
void featuresChanged(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void rotatorsChanged(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void handleFeatureMessageQueue(MessageQueue* messageQueue);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// Copyright (C) 2021-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 //
|
||||
|
@ -916,31 +916,33 @@ bool RadioAstronomyGUI::deserialize(const QByteArray& data)
|
|||
}
|
||||
}
|
||||
|
||||
void RadioAstronomyGUI::updateAvailableFeatures()
|
||||
void RadioAstronomyGUI::updateAvailableFeatures(const AvailableChannelOrFeatureList& availableFeatures, const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
QString currentText = ui->starTracker->currentText();
|
||||
// Update starTracker settting if it has been renamed
|
||||
if (renameFrom.contains(m_settings.m_starTracker))
|
||||
{
|
||||
m_settings.m_starTracker = renameTo[renameFrom.indexOf(m_settings.m_starTracker)];
|
||||
applySettings();
|
||||
}
|
||||
|
||||
ui->starTracker->blockSignals(true);
|
||||
ui->starTracker->clear();
|
||||
|
||||
for (const auto& feature : m_availableFeatures) {
|
||||
ui->starTracker->addItem(tr("F%1:%2 %3").arg(feature.m_featureSetIndex).arg(feature.m_featureIndex).arg(feature.m_type));
|
||||
for (const auto& feature : availableFeatures) {
|
||||
ui->starTracker->addItem(feature.getLongId());
|
||||
}
|
||||
|
||||
if (currentText.isEmpty())
|
||||
{
|
||||
if (m_availableFeatures.size() > 0) {
|
||||
ui->starTracker->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->starTracker->setCurrentIndex(ui->starTracker->findText(currentText));
|
||||
int idx = ui->starTracker->findText(m_settings.m_starTracker);
|
||||
if (idx >= 0) {
|
||||
ui->starTracker->setCurrentIndex(idx);
|
||||
} else {
|
||||
ui->starTracker->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
ui->starTracker->blockSignals(false);
|
||||
QString newText = ui->starTracker->currentText();
|
||||
|
||||
if (currentText != newText)
|
||||
QString newText = ui->starTracker->currentText();
|
||||
if (m_settings.m_starTracker != newText)
|
||||
{
|
||||
m_settings.m_starTracker = newText;
|
||||
applySettings();
|
||||
|
@ -970,8 +972,7 @@ bool RadioAstronomyGUI::handleMessage(const Message& message)
|
|||
{
|
||||
qDebug("RadioAstronomyGUI::handleMessage: MsgReportAvailableFeatures");
|
||||
RadioAstronomy::MsgReportAvailableFeatures& report = (RadioAstronomy::MsgReportAvailableFeatures&) message;
|
||||
m_availableFeatures = report.getFeatures();
|
||||
updateAvailableFeatures();
|
||||
updateAvailableFeatures(report.getFeatures(), report.getRenameFrom(), report.getRenameTo());
|
||||
return true;
|
||||
}
|
||||
else if (MainCore::MsgStarTrackerTarget::match(message))
|
||||
|
@ -1063,7 +1064,7 @@ bool RadioAstronomyGUI::handleMessage(const Message& message)
|
|||
else if (RadioAstronomy::MsgReportAvailableRotators::match(message))
|
||||
{
|
||||
RadioAstronomy::MsgReportAvailableRotators& report = (RadioAstronomy::MsgReportAvailableRotators&) message;
|
||||
updateRotatorList(report.getFeatures());
|
||||
updateRotatorList(report.getFeatures(), report.getRenameFrom(), report.getRenameTo());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1901,11 +1902,11 @@ void RadioAstronomyGUI::on_loadSpectrumData_clicked()
|
|||
|
||||
void RadioAstronomyGUI::on_powerTable_cellDoubleClicked(int row, int column)
|
||||
{
|
||||
if ((column >= POWER_COL_RA) && (column >= POWER_COL_EL))
|
||||
if ((column >= POWER_COL_RA) && (column <= POWER_COL_EL))
|
||||
{
|
||||
// Display target in Star Tracker
|
||||
QList<ObjectPipe*> starTrackerPipes;
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(this, "startracker.display", starTrackerPipes);
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(m_radioAstronomy, "startracker.display", starTrackerPipes);
|
||||
|
||||
for (const auto& pipe : starTrackerPipes)
|
||||
{
|
||||
|
@ -2604,17 +2605,22 @@ void RadioAstronomyGUI::tick()
|
|||
m_tickCount++;
|
||||
}
|
||||
|
||||
void RadioAstronomyGUI::updateRotatorList(const QList<RadioAstronomySettings::AvailableFeature>& rotators)
|
||||
void RadioAstronomyGUI::updateRotatorList(const AvailableChannelOrFeatureList& rotators, const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
// Update rotator settting if it has been renamed
|
||||
if (renameFrom.contains(m_settings.m_rotator))
|
||||
{
|
||||
m_settings.m_rotator = renameTo[renameFrom.indexOf(m_settings.m_rotator)];
|
||||
applySettings();
|
||||
}
|
||||
|
||||
// Update list of rotators
|
||||
ui->rotator->blockSignals(true);
|
||||
ui->rotator->clear();
|
||||
ui->rotator->addItem("None");
|
||||
|
||||
for (const auto& rotator : rotators)
|
||||
{
|
||||
QString name = QString("F%1:%2 %3").arg(rotator.m_featureSetIndex).arg(rotator.m_featureIndex).arg(rotator.m_type);
|
||||
ui->rotator->addItem(name);
|
||||
for (const auto& rotator : rotators) {
|
||||
ui->rotator->addItem(rotator.getLongId());
|
||||
}
|
||||
|
||||
// Rotator feature can be created after this plugin, so select it
|
||||
|
@ -4335,7 +4341,7 @@ void RadioAstronomyGUI::updateLoSMarker(const QString& name, float l, float b, f
|
|||
{
|
||||
// Send to Star Tracker
|
||||
QList<ObjectPipe*> starTrackerPipes;
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(this, "startracker.display", starTrackerPipes);
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(m_radioAstronomy, "startracker.display", starTrackerPipes);
|
||||
|
||||
for (const auto& pipe : starTrackerPipes)
|
||||
{
|
||||
|
@ -4794,7 +4800,7 @@ void RadioAstronomyGUI::on_spectrumIndex_valueChanged(int value)
|
|||
|
||||
// Display target in Star Tracker
|
||||
QList<ObjectPipe*> starTrackerPipes;
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(this, "startracker.display", starTrackerPipes);
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(m_radioAstronomy, "startracker.display", starTrackerPipes);
|
||||
|
||||
for (const auto& pipe : starTrackerPipes)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// Copyright (C) 2021-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 //
|
||||
|
@ -235,7 +235,6 @@ private:
|
|||
RollupState m_rollupState;
|
||||
RadioAstronomySettings m_settings;
|
||||
bool m_doApplySettings;
|
||||
QList<RadioAstronomySettings::AvailableFeature> m_availableFeatures;
|
||||
|
||||
int m_basebandSampleRate;
|
||||
quint64 m_centerFrequency;
|
||||
|
@ -351,8 +350,8 @@ private:
|
|||
void displaySettings();
|
||||
void displaySpectrumLineFrequency();
|
||||
void displayRunModeSettings();
|
||||
void updateAvailableFeatures();
|
||||
void updateRotatorList(const QList<RadioAstronomySettings::AvailableFeature>& rotators);
|
||||
void updateAvailableFeatures(const AvailableChannelOrFeatureList& availableFeatures, const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void updateRotatorList(const AvailableChannelOrFeatureList& rotators, const QStringList& renameFrom, const QStringList& renameTo);
|
||||
bool handleMessage(const Message& message);
|
||||
void makeUIConnections();
|
||||
void updateAbsoluteCenterFrequency();
|
||||
|
|
|
@ -23,14 +23,6 @@
|
|||
#include "settings/serializable.h"
|
||||
#include "radioastronomysettings.h"
|
||||
|
||||
const QStringList RadioAstronomySettings::m_pipeTypes = {
|
||||
QStringLiteral("StarTracker")
|
||||
};
|
||||
|
||||
const QStringList RadioAstronomySettings::m_pipeURIs = {
|
||||
QStringLiteral("sdrangel.feature.startracker")
|
||||
};
|
||||
|
||||
RadioAstronomySettings::RadioAstronomySettings() :
|
||||
m_channelMarker(nullptr),
|
||||
m_rollupState(nullptr)
|
||||
|
|
|
@ -35,20 +35,6 @@ class Serializable;
|
|||
|
||||
struct RadioAstronomySettings
|
||||
{
|
||||
struct AvailableFeature
|
||||
{
|
||||
int m_featureSetIndex;
|
||||
int m_featureIndex;
|
||||
QString m_type;
|
||||
|
||||
AvailableFeature() = default;
|
||||
AvailableFeature(const AvailableFeature&) = default;
|
||||
AvailableFeature& operator=(const AvailableFeature&) = default;
|
||||
bool operator==(const AvailableFeature& a) const {
|
||||
return (m_featureSetIndex == a.m_featureSetIndex) && (m_featureIndex == a.m_featureIndex) && (m_type == a.m_type);
|
||||
}
|
||||
};
|
||||
|
||||
int m_inputFrequencyOffset;
|
||||
int m_sampleRate;
|
||||
int m_rfBandwidth;
|
||||
|
@ -244,9 +230,6 @@ struct RadioAstronomySettings
|
|||
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
static const QStringList m_pipeTypes;
|
||||
static const QStringList m_pipeURIs;
|
||||
};
|
||||
|
||||
#endif /* INCLUDE_RADIOASTRONOMYSETTINGS_H */
|
||||
|
|
|
@ -12,6 +12,11 @@ else()
|
|||
message(STATUS "Not building map (ENABLE_FEATURE_MAP=${ENABLE_FEATURE_MAP} Qt${QT_DEFAULT_MAJOR_VERSION}Quick_FOUND=${Qt${QT_DEFAULT_MAJOR_VERSION}Quick_FOUND} Qt${QT_DEFAULT_MAJOR_VERSION}QuickWidgets_FOUND=${Qt${QT_DEFAULT_MAJOR_VERSION}QuickWidgets_FOUND} Qt${QT_DEFAULT_MAJOR_VERSION}Positioning_FOUND=${Qt${QT_DEFAULT_MAJOR_VERSION}Positioning_FOUND} Qt${QT_DEFAULT_MAJOR_VERSION}Location_FOUND=${Qt${QT_DEFAULT_MAJOR_VERSION}Location_FOUND})")
|
||||
endif()
|
||||
|
||||
# WebEngine on Qt5, WebEngineCore on Qt6
|
||||
if(ENABLE_FEATURE_SKYMAP AND Qt${QT_DEFAULT_MAJOR_VERSION}WebEngine_FOUND OR Qt${QT_DEFAULT_MAJOR_VERSION}WebEngineCore_FOUND)
|
||||
add_subdirectory(skymap)
|
||||
endif()
|
||||
|
||||
if (ENABLE_FEATURE_VORLOCALIZER AND Qt${QT_DEFAULT_MAJOR_VERSION}Quick_FOUND AND Qt${QT_DEFAULT_MAJOR_VERSION}QuickWidgets_FOUND AND Qt${QT_DEFAULT_MAJOR_VERSION}Positioning_FOUND)
|
||||
add_subdirectory(vorlocalizer)
|
||||
else()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -41,7 +41,8 @@ const char* const AIS::m_featureIdURI = "sdrangel.feature.ais";
|
|||
const char* const AIS::m_featureId = "AIS";
|
||||
|
||||
AIS::AIS(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface)
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface),
|
||||
m_availableChannelHandler({"sdrangel.channel.aisdemod"}, QStringList{"ais"})
|
||||
{
|
||||
qDebug("AIS::AIS: webAPIAdapterInterface: %p", webAPIAdapterInterface);
|
||||
setObjectName(m_featureId);
|
||||
|
@ -54,23 +55,20 @@ AIS::AIS(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
|||
this,
|
||||
&AIS::networkManagerFinished
|
||||
);
|
||||
scanAvailableChannels();
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
&m_availableChannelHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&AIS::handleChannelAdded
|
||||
);
|
||||
&AIS::handleChannelMessageQueue);
|
||||
}
|
||||
|
||||
AIS::~AIS()
|
||||
{
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
&m_availableChannelHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&AIS::handleChannelAdded
|
||||
);
|
||||
&AIS::handleChannelMessageQueue);
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
|
@ -362,90 +360,6 @@ void AIS::networkManagerFinished(QNetworkReply *reply)
|
|||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void AIS::scanAvailableChannels()
|
||||
{
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
MessagePipes& messagePipes = mainCore->getMessagePipes();
|
||||
std::vector<DeviceSet*>& deviceSets = mainCore->getDeviceSets();
|
||||
m_availableChannels.clear();
|
||||
|
||||
for (const auto& deviceSet : deviceSets)
|
||||
{
|
||||
DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine;
|
||||
|
||||
if (deviceSourceEngine)
|
||||
{
|
||||
for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++)
|
||||
{
|
||||
ChannelAPI *channel = deviceSet->getChannelAt(chi);
|
||||
|
||||
if ((channel->getURI() == "sdrangel.channel.aisdemod") && !m_availableChannels.contains(channel))
|
||||
{
|
||||
qDebug("AIS::scanAvailableChannels: register %d:%d (%p)", deviceSet->getIndex(), chi, channel);
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(channel, this, "ais");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handleChannelMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
QObject::connect(
|
||||
pipe,
|
||||
&ObjectPipe::toBeDeleted,
|
||||
this,
|
||||
&AIS::handleMessagePipeToBeDeleted
|
||||
);
|
||||
m_availableChannels.insert(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AIS::handleChannelAdded(int deviceSetIndex, ChannelAPI *channel)
|
||||
{
|
||||
qDebug("AIS::handleChannelAdded: deviceSetIndex: %d:%d channel: %s (%p)",
|
||||
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
|
||||
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
|
||||
DeviceSet *deviceSet = deviceSets[deviceSetIndex];
|
||||
DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine;
|
||||
|
||||
if (deviceSourceEngine && (channel->getURI() == "sdrangel.channel.aisdemod"))
|
||||
{
|
||||
if (!m_availableChannels.contains(channel))
|
||||
{
|
||||
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(channel, this, "ais");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handleChannelMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
QObject::connect(
|
||||
pipe,
|
||||
&ObjectPipe::toBeDeleted,
|
||||
this,
|
||||
&AIS::handleMessagePipeToBeDeleted
|
||||
);
|
||||
m_availableChannels.insert(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AIS::handleMessagePipeToBeDeleted(int reason, QObject* object)
|
||||
{
|
||||
if ((reason == 0) && m_availableChannels.contains((ChannelAPI*) object)) // producer (channel)
|
||||
{
|
||||
qDebug("AIS::handleMessagePipeToBeDeleted: removing channel at (%p)", object);
|
||||
m_availableChannels.remove((ChannelAPI*) object);
|
||||
}
|
||||
}
|
||||
|
||||
void AIS::handleChannelMessageQueue(MessageQueue* messageQueue)
|
||||
{
|
||||
Message* message;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
|
||||
// Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
#include "availablechannelorfeaturehandler.h"
|
||||
|
||||
#include "aissettings.h"
|
||||
|
||||
|
@ -101,7 +102,8 @@ public:
|
|||
|
||||
private:
|
||||
AISSettings m_settings;
|
||||
QSet<ChannelAPI*> m_availableChannels;
|
||||
AvailableChannelOrFeatureHandler m_availableChannelHandler;
|
||||
AvailableChannelOrFeatureList m_availableChannels;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
@ -110,12 +112,9 @@ private:
|
|||
void stop();
|
||||
void applySettings(const AISSettings& settings, const QList<QString>& settingsKeys, bool force = false);
|
||||
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const AISSettings& settings, bool force);
|
||||
void scanAvailableChannels();
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
void handleChannelAdded(int deviceSetIndex, ChannelAPI *channel);
|
||||
void handleMessagePipeToBeDeleted(int reason, QObject* object);
|
||||
void handleChannelMessageQueue(MessageQueue* messageQueue);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
|
@ -47,7 +47,7 @@ QStringList AISGUI::m_shipModels = {
|
|||
"tug_20m.glbe", "tug_30m_1.glbe", "tug_30m_2.glbe", "tug_30m_3.glbe",
|
||||
"cargo_75m.glbe", "tanker_50m.glbe", "dredger_53m.glbe",
|
||||
"trawler_22m.glbe",
|
||||
"speedboat_8m.glbe", "yacht_10m.glbe", "yacht_20m.glbe", "yacht_42m.glbe"
|
||||
"speedboat_8m.glbe", /*"yacht_10m.glbe",*/ "yacht_20m.glbe", "yacht_42m.glbe"
|
||||
};
|
||||
|
||||
QStringList AISGUI::m_sailboatModels = {
|
||||
|
@ -236,6 +236,7 @@ AISGUI::AISGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur
|
|||
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
m_resizer.enableChildMouseTracking();
|
||||
}
|
||||
|
||||
AISGUI::~AISGUI()
|
||||
|
@ -845,14 +846,14 @@ void AISGUI::getImageAndModel(const QString &type, const QString &shipType, int
|
|||
{
|
||||
vessel->m_model = "cargo_75m.glbe";
|
||||
}
|
||||
else if (length < 200)
|
||||
else /*if (length < 200)*/
|
||||
{
|
||||
vessel->m_model = "cargo_190m.glbe";
|
||||
}
|
||||
else
|
||||
/*else
|
||||
{
|
||||
vessel->m_model = "cargo_230m.glbe";
|
||||
}
|
||||
}*/
|
||||
}
|
||||
else if (shipType == "Tanker")
|
||||
{
|
||||
|
@ -905,10 +906,10 @@ void AISGUI::getImageAndModel(const QString &type, const QString &shipType, int
|
|||
{
|
||||
vessel->m_model = "speedboat_8m.glbe";
|
||||
}
|
||||
else if (length < 18)
|
||||
/*else if (length < 18)
|
||||
{
|
||||
vessel->m_model = "yacht_10m.glbe";
|
||||
}
|
||||
}*/
|
||||
else if (length < 32)
|
||||
{
|
||||
vessel->m_model = "yacht_20m.glbe";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
|
@ -46,7 +46,8 @@ const char* const APRS::m_featureId = "APRS";
|
|||
APRS::APRS(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface),
|
||||
m_thread(nullptr),
|
||||
m_worker(nullptr)
|
||||
m_worker(nullptr),
|
||||
m_availableChannelHandler(APRSSettings::m_pipeURIs, QStringList{"packets"})
|
||||
{
|
||||
qDebug("APRS::APRS: webAPIAdapterInterface: %p", webAPIAdapterInterface);
|
||||
setObjectName(m_featureId);
|
||||
|
@ -59,23 +60,31 @@ APRS::APRS(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
|||
this,
|
||||
&APRS::networkManagerFinished
|
||||
);
|
||||
scanAvailableChannels();
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
&m_availableChannelHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&APRS::handleChannelAdded
|
||||
);
|
||||
&APRS::handleChannelMessageQueue);
|
||||
QObject::connect(
|
||||
&m_availableChannelHandler,
|
||||
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
||||
this,
|
||||
&APRS::channelsChanged);
|
||||
m_availableChannelHandler.scanAvailableChannelsAndFeatures();
|
||||
}
|
||||
|
||||
APRS::~APRS()
|
||||
{
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
&m_availableChannelHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&APRS::handleChannelAdded
|
||||
);
|
||||
&APRS::handleChannelMessageQueue);
|
||||
QObject::disconnect(
|
||||
&m_availableChannelHandler,
|
||||
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
||||
this,
|
||||
&APRS::channelsChanged);
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
|
@ -858,6 +867,7 @@ void APRS::networkManagerFinished(QNetworkReply *reply)
|
|||
reply->deleteLater();
|
||||
}
|
||||
|
||||
/*
|
||||
void APRS::scanAvailableChannels()
|
||||
{
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
|
@ -905,18 +915,25 @@ void APRS::scanAvailableChannels()
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void APRS::channelsChanged()
|
||||
{
|
||||
m_availableChannels = m_availableChannelHandler.getAvailableChannelOrFeatureList();
|
||||
notifyUpdateChannels();
|
||||
}
|
||||
|
||||
void APRS::notifyUpdateChannels()
|
||||
{
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportAvailableChannels *msg = MsgReportAvailableChannels::create();
|
||||
msg->getChannels() = m_availableChannels.values();
|
||||
msg->getChannels() = m_availableChannels;
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void APRS::handleChannelAdded(int deviceSetIndex, ChannelAPI *channel)
|
||||
/*void APRS::handleChannelAdded(int deviceSetIndex, ChannelAPI *channel)
|
||||
{
|
||||
qDebug("APRS::handleChannelAdded: deviceSetIndex: %d:%d channel: %s (%p)",
|
||||
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
|
||||
|
@ -964,7 +981,7 @@ void APRS::handleMessagePipeToBeDeleted(int reason, QObject* object)
|
|||
m_availableChannels.remove((ChannelAPI*) object);
|
||||
notifyUpdateChannels();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void APRS::handleChannelMessageQueue(MessageQueue* messageQueue)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
|
||||
// Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
#include "availablechannelorfeaturehandler.h"
|
||||
|
||||
#include "aprssettings.h"
|
||||
|
||||
|
@ -104,14 +105,14 @@ public:
|
|||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QList<APRSSettings::AvailableChannel>& getChannels() { return m_availableChannels; }
|
||||
AvailableChannelOrFeatureList& getChannels() { return m_availableChannels; }
|
||||
|
||||
static MsgReportAvailableChannels* create() {
|
||||
return new MsgReportAvailableChannels();
|
||||
}
|
||||
|
||||
private:
|
||||
QList<APRSSettings::AvailableChannel> m_availableChannels;
|
||||
AvailableChannelOrFeatureList m_availableChannels;
|
||||
|
||||
MsgReportAvailableChannels() :
|
||||
Message()
|
||||
|
@ -160,7 +161,8 @@ private:
|
|||
QThread *m_thread;
|
||||
APRSWorker *m_worker;
|
||||
APRSSettings m_settings;
|
||||
QHash<ChannelAPI*, APRSSettings::AvailableChannel> m_availableChannels;
|
||||
AvailableChannelOrFeatureHandler m_availableChannelHandler;
|
||||
AvailableChannelOrFeatureList m_availableChannels;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
@ -169,14 +171,12 @@ private:
|
|||
void stop();
|
||||
void applySettings(const APRSSettings& settings, const QList<QString>& settingsKeys, bool force = false);
|
||||
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const APRSSettings& settings, bool force);
|
||||
void scanAvailableChannels();
|
||||
void notifyUpdateChannels();
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
void handleChannelAdded(int deviceSetIndex, ChannelAPI *channel);
|
||||
void handleMessagePipeToBeDeleted(int reason, QObject* object);
|
||||
void handleChannelMessageQueue(MessageQueue* messageQueue);
|
||||
void channelsChanged();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_APRS_H_
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
|
@ -708,7 +708,7 @@ void APRSGUI::updateChannelList()
|
|||
ui->sourcePipes->clear();
|
||||
|
||||
for (const auto& channel : m_availableChannels) {
|
||||
ui->sourcePipes->addItem(tr("R%1:%2 %3").arg(channel.m_deviceSetIndex).arg(channel.m_channelIndex).arg(channel.m_type));
|
||||
ui->sourcePipes->addItem(channel.getLongId());
|
||||
}
|
||||
|
||||
ui->sourcePipes->blockSignals(false);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
|
@ -30,6 +30,7 @@
|
|||
#include "util/messagequeue.h"
|
||||
#include "util/aprs.h"
|
||||
#include "settings/rollupstate.h"
|
||||
#include "availablechannelorfeature.h"
|
||||
|
||||
#include "aprssettings.h"
|
||||
|
||||
|
@ -123,7 +124,7 @@ private:
|
|||
QList<QString> m_settingsKeys;
|
||||
RollupState m_rollupState;
|
||||
bool m_doApplySettings;
|
||||
QList<APRSSettings::AvailableChannel> m_availableChannels;
|
||||
AvailableChannelOrFeatureList m_availableChannels;
|
||||
|
||||
APRS* m_aprs;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020-2021 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -38,17 +38,6 @@ class Serializable;
|
|||
|
||||
struct APRSSettings
|
||||
{
|
||||
struct AvailableChannel
|
||||
{
|
||||
int m_deviceSetIndex;
|
||||
int m_channelIndex;
|
||||
QString m_type;
|
||||
|
||||
AvailableChannel() = default;
|
||||
AvailableChannel(const AvailableChannel&) = default;
|
||||
AvailableChannel& operator=(const AvailableChannel&) = default;
|
||||
};
|
||||
|
||||
QString m_igateServer;
|
||||
int m_igatePort;
|
||||
QString m_igateCallsign;
|
||||
|
|
|
@ -48,4 +48,4 @@ APRS icons are from: https://github.com/hessu/aprs-symbols
|
|||
|
||||
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to enable the APRS-IS IGate:
|
||||
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "APRS", "APRSSettings": { "igateCallsign": "MYCALLSIGN", "igatePasscode": "12345", "igateFilter": "r/50.2/10.2/25", "igateEnabled": 1 }}'
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "APRS", "APRSSettings": { "igateCallsign": "MYCALLSIGN", "igatePasscode": "12345", "igateFilter": "r/50.2/10.2/25", "igateEnabled": 1 }}'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
|
||||
// //
|
||||
|
@ -56,6 +56,8 @@ GS232Controller::GS232Controller(WebAPIAdapterInterface *webAPIAdapterInterface)
|
|||
Feature(m_featureIdURI, webAPIAdapterInterface),
|
||||
m_thread(nullptr),
|
||||
m_worker(nullptr),
|
||||
m_availableChannelOrFeatureHandler(GS232ControllerSettings::m_pipeURIs),
|
||||
m_selectedPipe(nullptr),
|
||||
m_currentAzimuth(0.0f),
|
||||
m_currentElevation(0.0f)
|
||||
{
|
||||
|
@ -63,7 +65,6 @@ GS232Controller::GS232Controller(WebAPIAdapterInterface *webAPIAdapterInterface)
|
|||
setObjectName(m_featureId);
|
||||
m_state = StIdle;
|
||||
m_errorMessage = "GS232Controller error";
|
||||
m_selectedPipe = nullptr;
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
QObject::connect(
|
||||
m_networkManager,
|
||||
|
@ -71,30 +72,21 @@ GS232Controller::GS232Controller(WebAPIAdapterInterface *webAPIAdapterInterface)
|
|||
this,
|
||||
&GS232Controller::networkManagerFinished
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureAdded,
|
||||
&m_availableChannelOrFeatureHandler,
|
||||
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
||||
this,
|
||||
&GS232Controller::handleFeatureAdded
|
||||
&GS232Controller::channelsOrFeaturesChanged
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
&m_availableChannelOrFeatureHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&GS232Controller::handleChannelAdded
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureRemoved,
|
||||
this,
|
||||
&GS232Controller::handleFeatureRemoved
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelRemoved,
|
||||
this,
|
||||
&GS232Controller::handleChannelRemoved
|
||||
&GS232Controller::handlePipeMessageQueue
|
||||
);
|
||||
m_availableChannelOrFeatureHandler.scanAvailableChannelsAndFeatures();
|
||||
|
||||
connect(&m_timer, &QTimer::timeout, this, &GS232Controller::scanSerialPorts);
|
||||
m_timer.start(5000);
|
||||
}
|
||||
|
@ -104,28 +96,16 @@ GS232Controller::~GS232Controller()
|
|||
m_timer.stop();
|
||||
disconnect(&m_timer, &QTimer::timeout, this, &GS232Controller::scanSerialPorts);
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelRemoved,
|
||||
&m_availableChannelOrFeatureHandler,
|
||||
&AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged,
|
||||
this,
|
||||
&GS232Controller::handleChannelRemoved
|
||||
&GS232Controller::channelsOrFeaturesChanged
|
||||
);
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureRemoved,
|
||||
&m_availableChannelOrFeatureHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&GS232Controller::handleFeatureRemoved
|
||||
);
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
this,
|
||||
&GS232Controller::handleChannelAdded
|
||||
);
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureAdded,
|
||||
this,
|
||||
&GS232Controller::handleFeatureAdded
|
||||
&GS232Controller::handlePipeMessageQueue
|
||||
);
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
|
@ -142,7 +122,7 @@ void GS232Controller::start()
|
|||
qDebug("GS232Controller::start");
|
||||
|
||||
m_thread = new QThread();
|
||||
m_worker = new GS232ControllerWorker();
|
||||
m_worker = new GS232ControllerWorker(this);
|
||||
m_worker->moveToThread(m_thread);
|
||||
QObject::connect(m_thread, &QThread::started, m_worker, &GS232ControllerWorker::startWork);
|
||||
QObject::connect(m_thread, &QThread::finished, m_worker, &QObject::deleteLater);
|
||||
|
@ -154,8 +134,6 @@ void GS232Controller::start()
|
|||
GS232ControllerWorker::MsgConfigureGS232ControllerWorker *msg =
|
||||
GS232ControllerWorker::MsgConfigureGS232ControllerWorker::create(m_settings, QList<QString>(), true);
|
||||
m_worker->getInputMessageQueue()->push(msg);
|
||||
|
||||
scanAvailableChannelsAndFeatures();
|
||||
}
|
||||
|
||||
void GS232Controller::stop()
|
||||
|
@ -210,7 +188,7 @@ bool GS232Controller::handleMessage(const Message& cmd)
|
|||
}
|
||||
else if (MsgScanAvailableChannelOrFeatures::match(cmd))
|
||||
{
|
||||
scanAvailableChannelsAndFeatures();
|
||||
notifyUpdate({}, {});
|
||||
return true;
|
||||
}
|
||||
else if (GS232ControllerReport::MsgReportAzAl::match(cmd))
|
||||
|
@ -248,8 +226,6 @@ bool GS232Controller::handleMessage(const Message& cmd)
|
|||
applySettings(m_settings, QList<QString>{"azimuth", "elevation"});
|
||||
}
|
||||
}
|
||||
else
|
||||
qDebug() << "GS232Controller::handleMessage: No match " << msg.getPipeSource() << " " << m_selectedPipe;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -306,57 +282,12 @@ void GS232Controller::applySettings(const GS232ControllerSettings& settings, con
|
|||
{
|
||||
qDebug() << "GS232Controller::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
|
||||
|
||||
// if ((m_settings.m_source != settings.m_source)
|
||||
// || (!settings.m_source.isEmpty() && (m_selectedPipe == nullptr)) // Change in available pipes
|
||||
// || force)
|
||||
|
||||
if (settingsKeys.contains("source")
|
||||
|| (!settings.m_source.isEmpty() && (m_selectedPipe == nullptr)) // Change in available pipes
|
||||
|| force)
|
||||
{
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
MessagePipes& messagePipes = mainCore->getMessagePipes();
|
||||
|
||||
if (m_selectedPipe)
|
||||
{
|
||||
qDebug("GS232Controller::applySettings: unregister %s (%p)", qPrintable(m_selectedPipe->objectName()), m_selectedPipe);
|
||||
messagePipes.unregisterProducerToConsumer(m_selectedPipe, this, "target");
|
||||
}
|
||||
|
||||
if (!settings.m_source.isEmpty())
|
||||
{
|
||||
QObject *object = nullptr;
|
||||
|
||||
for (const auto& oval : m_availableChannelOrFeatures)
|
||||
{
|
||||
QString itemText = tr("%1%2:%3 %4")
|
||||
.arg(oval.m_kind)
|
||||
.arg(oval.m_superIndex)
|
||||
.arg(oval.m_index)
|
||||
.arg(oval.m_type);
|
||||
|
||||
if (settings.m_source == itemText)
|
||||
{
|
||||
object = m_availableChannelOrFeatures.key(oval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (object)
|
||||
{
|
||||
registerPipe(object);
|
||||
m_selectedPipe = object;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_selectedPipe = nullptr;
|
||||
qDebug() << "GS232Controller::applySettings: No plugin corresponding to source " << settings.m_source;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_selectedPipe = nullptr;
|
||||
}
|
||||
m_availableChannelOrFeatureHandler.deregisterPipes(m_selectedPipe, {"target"});
|
||||
m_selectedPipe = m_availableChannelOrFeatureHandler.registerPipes(settings.m_source, {"target"});
|
||||
}
|
||||
|
||||
GS232ControllerWorker::MsgConfigureGS232ControllerWorker *msg = GS232ControllerWorker::MsgConfigureGS232ControllerWorker::create(
|
||||
|
@ -724,12 +655,7 @@ void GS232Controller::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& r
|
|||
|
||||
for (const auto& item : m_availableChannelOrFeatures)
|
||||
{
|
||||
QString itemText = tr("%1%2:%3 %4")
|
||||
.arg(item.m_kind)
|
||||
.arg(item.m_superIndex)
|
||||
.arg(item.m_index)
|
||||
.arg(item.m_type);
|
||||
|
||||
QString itemText = item.getLongId();
|
||||
response.getGs232ControllerReport()->getSources()->append(new QString(itemText));
|
||||
}
|
||||
|
||||
|
@ -769,163 +695,22 @@ void GS232Controller::networkManagerFinished(QNetworkReply *reply)
|
|||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void GS232Controller::scanAvailableChannelsAndFeatures()
|
||||
void GS232Controller::channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
qDebug("GS232Controller::scanAvailableChannelsAndFeatures");
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
std::vector<FeatureSet*>& featureSets = mainCore->getFeatureeSets();
|
||||
m_availableChannelOrFeatures.clear();
|
||||
|
||||
for (const auto& featureSet : featureSets)
|
||||
{
|
||||
for (int fei = 0; fei < featureSet->getNumberOfFeatures(); fei++)
|
||||
{
|
||||
Feature *feature = featureSet->getFeatureAt(fei);
|
||||
|
||||
if (GS232ControllerSettings::m_pipeURIs.contains(feature->getURI()) && !m_availableChannelOrFeatures.contains(feature))
|
||||
{
|
||||
qDebug("GS232Controller::scanAvailableChannelsAndFeatures: store feature %d:%d %s (%p)",
|
||||
featureSet->getIndex(), fei, qPrintable(feature->getURI()), feature);
|
||||
GS232ControllerSettings::AvailableChannelOrFeature availableItem =
|
||||
GS232ControllerSettings::AvailableChannelOrFeature{"F", featureSet->getIndex(), fei, feature->getIdentifier()};
|
||||
m_availableChannelOrFeatures[feature] = availableItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DeviceSet*>& deviceSets = mainCore->getDeviceSets();
|
||||
|
||||
for (const auto& deviceSet : deviceSets)
|
||||
{
|
||||
DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine;
|
||||
|
||||
if (deviceSourceEngine)
|
||||
{
|
||||
for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++)
|
||||
{
|
||||
ChannelAPI *channel = deviceSet->getChannelAt(chi);
|
||||
|
||||
if (GS232ControllerSettings::m_pipeURIs.contains(channel->getURI()) && !m_availableChannelOrFeatures.contains(channel))
|
||||
{
|
||||
qDebug("GS232Controller::scanAvailableChannelsAndFeatures: store channel %d:%d %s (%p)",
|
||||
deviceSet->getIndex(), chi, qPrintable(channel->getURI()), channel);
|
||||
GS232ControllerSettings::AvailableChannelOrFeature availableItem =
|
||||
GS232ControllerSettings::AvailableChannelOrFeature{"R", deviceSet->getIndex(), chi, channel->getIdentifier()};
|
||||
m_availableChannelOrFeatures[channel] = availableItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyUpdate();
|
||||
m_availableChannelOrFeatures = m_availableChannelOrFeatureHandler.getAvailableChannelOrFeatureList();
|
||||
notifyUpdate(renameFrom, renameTo);
|
||||
}
|
||||
|
||||
void GS232Controller::notifyUpdate()
|
||||
void GS232Controller::notifyUpdate(const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportAvailableChannelOrFeatures *msg = MsgReportAvailableChannelOrFeatures::create();
|
||||
msg->getItems() = m_availableChannelOrFeatures.values();
|
||||
MsgReportAvailableChannelOrFeatures *msg = MsgReportAvailableChannelOrFeatures::create(renameFrom, renameTo);
|
||||
msg->getItems() = m_availableChannelOrFeatures;
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void GS232Controller::handleFeatureAdded(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
qDebug("GS232Controller::handleFeatureAdded: featureSetIndex: %d:%d feature: %s (%p)",
|
||||
featureSetIndex, feature->getIndexInFeatureSet(), qPrintable(feature->getURI()), feature);
|
||||
FeatureSet *featureSet = MainCore::instance()->getFeatureeSets()[featureSetIndex];
|
||||
|
||||
if (GS232ControllerSettings::m_pipeURIs.contains(feature->getURI()))
|
||||
{
|
||||
GS232ControllerSettings::AvailableChannelOrFeature availableItem =
|
||||
GS232ControllerSettings::AvailableChannelOrFeature{
|
||||
"F",
|
||||
featureSet->getIndex(),
|
||||
feature->getIndexInFeatureSet(),
|
||||
feature->getIdentifier()
|
||||
};
|
||||
m_availableChannelOrFeatures[feature] = availableItem;
|
||||
|
||||
notifyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void GS232Controller::handleFeatureRemoved(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
qDebug("GS232Controller::handleFeatureRemoved: featureSetIndex: %d (%p)", featureSetIndex, feature);
|
||||
|
||||
if (m_availableChannelOrFeatures.contains(feature))
|
||||
{
|
||||
m_availableChannelOrFeatures.remove(feature);
|
||||
notifyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void GS232Controller::handleChannelAdded(int deviceSetIndex, ChannelAPI *channel)
|
||||
{
|
||||
qDebug("GS232Controller::handleChannelAdded: deviceSetIndex: %d:%d channel: %s (%p)",
|
||||
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
|
||||
DeviceSet *deviceSet = MainCore::instance()->getDeviceSets()[deviceSetIndex];
|
||||
DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine;
|
||||
|
||||
if (deviceSourceEngine && GS232ControllerSettings::m_pipeURIs.contains(channel->getURI()))
|
||||
{
|
||||
GS232ControllerSettings::AvailableChannelOrFeature availableItem =
|
||||
GS232ControllerSettings::AvailableChannelOrFeature{
|
||||
"R",
|
||||
deviceSet->getIndex(),
|
||||
channel->getIndexInDeviceSet(),
|
||||
channel->getIdentifier()
|
||||
};
|
||||
m_availableChannelOrFeatures[channel] = availableItem;
|
||||
|
||||
notifyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void GS232Controller::handleChannelRemoved(int deviceSetIndex, ChannelAPI *channel)
|
||||
{
|
||||
qDebug("GS232Controller::handleChannelRemoved: deviceSetIndex: %d (%p)", deviceSetIndex, channel);
|
||||
|
||||
if (m_availableChannelOrFeatures.contains(channel))
|
||||
{
|
||||
m_availableChannelOrFeatures.remove(channel);
|
||||
notifyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void GS232Controller::registerPipe(QObject *object)
|
||||
{
|
||||
qDebug("GS232Controller::registerPipe: register %s (%p)", qPrintable(object->objectName()), object);
|
||||
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(object, this, "target");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handlePipeMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
QObject::connect(
|
||||
pipe,
|
||||
&ObjectPipe::toBeDeleted,
|
||||
this,
|
||||
&GS232Controller::handleMessagePipeToBeDeleted
|
||||
);
|
||||
}
|
||||
|
||||
void GS232Controller::handleMessagePipeToBeDeleted(int reason, QObject* object)
|
||||
{
|
||||
if ((reason == 0) && m_availableChannelOrFeatures.contains(object)) // producer
|
||||
{
|
||||
qDebug("GS232Controller::handleMessagePipeToBeDeleted: removing channel or feature at (%p)", object);
|
||||
m_availableChannelOrFeatures.remove(object);
|
||||
notifyUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void GS232Controller::handlePipeMessageQueue(MessageQueue* messageQueue)
|
||||
{
|
||||
Message* message;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
|
||||
// //
|
||||
|
@ -27,6 +27,8 @@
|
|||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
#include "availablechannelorfeaturehandler.h"
|
||||
#include "maincore.h"
|
||||
|
||||
#include "gs232controllersettings.h"
|
||||
|
||||
|
@ -110,17 +112,23 @@ public:
|
|||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QList<GS232ControllerSettings::AvailableChannelOrFeature>& getItems() { return m_availableChannelOrFeatures; }
|
||||
AvailableChannelOrFeatureList& getItems() { return m_availableChannelOrFeatures; }
|
||||
const QStringList& getRenameFrom() const { return m_renameFrom; }
|
||||
const QStringList& getRenameTo() const { return m_renameTo; }
|
||||
|
||||
static MsgReportAvailableChannelOrFeatures* create() {
|
||||
return new MsgReportAvailableChannelOrFeatures();
|
||||
static MsgReportAvailableChannelOrFeatures* create(const QStringList& renameFrom, const QStringList& renameTo) {
|
||||
return new MsgReportAvailableChannelOrFeatures(renameFrom, renameTo);
|
||||
}
|
||||
|
||||
private:
|
||||
QList<GS232ControllerSettings::AvailableChannelOrFeature> m_availableChannelOrFeatures;
|
||||
AvailableChannelOrFeatureList m_availableChannelOrFeatures;
|
||||
QStringList m_renameFrom;
|
||||
QStringList m_renameTo;
|
||||
|
||||
MsgReportAvailableChannelOrFeatures() :
|
||||
Message()
|
||||
MsgReportAvailableChannelOrFeatures(const QStringList& renameFrom, const QStringList& renameTo) :
|
||||
Message(),
|
||||
m_renameFrom(renameFrom),
|
||||
m_renameTo(renameTo)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -212,7 +220,8 @@ private:
|
|||
QThread *m_thread;
|
||||
GS232ControllerWorker *m_worker;
|
||||
GS232ControllerSettings m_settings;
|
||||
QHash<QObject*, GS232ControllerSettings::AvailableChannelOrFeature> m_availableChannelOrFeatures;
|
||||
AvailableChannelOrFeatureList m_availableChannelOrFeatures;
|
||||
AvailableChannelOrFeatureHandler m_availableChannelOrFeatureHandler;
|
||||
QObject *m_selectedPipe;
|
||||
|
||||
QTimer m_timer;
|
||||
|
@ -229,17 +238,11 @@ private:
|
|||
void applySettings(const GS232ControllerSettings& settings, const QList<QString>& settingsKeys, bool force = false);
|
||||
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const GS232ControllerSettings& settings, bool force);
|
||||
void webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response);
|
||||
void scanAvailableChannelsAndFeatures();
|
||||
void notifyUpdate();
|
||||
void registerPipe(QObject *object);
|
||||
void notifyUpdate(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
void handleFeatureAdded(int featureSetIndex, Feature *feature);
|
||||
void handleChannelAdded(int deviceSetIndex, ChannelAPI *channel);
|
||||
void handleFeatureRemoved(int featureSetIndex, Feature *feature);
|
||||
void handleChannelRemoved(int deviceSetIndex, ChannelAPI *feature);
|
||||
void handleMessagePipeToBeDeleted(int reason, QObject* object);
|
||||
void channelsOrFeaturesChanged(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void handlePipeMessageQueue(MessageQueue* messageQueue);
|
||||
void scanSerialPorts();
|
||||
};
|
||||
|
|
|
@ -51,7 +51,7 @@ void GS232ControllerGUI::resetToDefaults()
|
|||
{
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
applyAllSettings();
|
||||
}
|
||||
|
||||
QByteArray GS232ControllerGUI::serialize() const
|
||||
|
@ -65,7 +65,7 @@ bool GS232ControllerGUI::deserialize(const QByteArray& data)
|
|||
{
|
||||
m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
applyAllSettings();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -121,9 +121,7 @@ void GS232ControllerGUI::displayToAzEl(float coord1, float coord2)
|
|||
m_settings.m_azimuth = coord1;
|
||||
m_settings.m_elevation = coord2;
|
||||
}
|
||||
m_settingsKeys.append("azimuth");
|
||||
m_settingsKeys.append("elevation");
|
||||
applySettings();
|
||||
applySettings({"azimuth", "elevation"});
|
||||
}
|
||||
|
||||
bool GS232ControllerGUI::handleMessage(const Message& message)
|
||||
|
@ -149,7 +147,7 @@ bool GS232ControllerGUI::handleMessage(const Message& message)
|
|||
{
|
||||
GS232Controller::MsgReportAvailableChannelOrFeatures& report =
|
||||
(GS232Controller::MsgReportAvailableChannelOrFeatures&) message;
|
||||
updatePipeList(report.getItems());
|
||||
updatePipeList(report.getItems(), report.getRenameFrom(), report.getRenameTo());
|
||||
return true;
|
||||
}
|
||||
else if (GS232ControllerReport::MsgReportAzAl::match(message))
|
||||
|
@ -206,7 +204,7 @@ void GS232ControllerGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
|||
(void) rollDown;
|
||||
|
||||
getRollupContents()->saveState(m_rollupState);
|
||||
applySettings();
|
||||
applySetting("rollupState");
|
||||
}
|
||||
|
||||
GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
|
||||
|
@ -260,7 +258,7 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu
|
|||
connect(&m_inputTimer, &QTimer::timeout, this, &GS232ControllerGUI::checkInputController);
|
||||
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
applyAllSettings();
|
||||
makeUIConnections();
|
||||
|
||||
// Get pre-existing pipes
|
||||
|
@ -408,8 +406,7 @@ void GS232ControllerGUI::on_inputController_currentIndexChanged(int index)
|
|||
if (index >= 0)
|
||||
{
|
||||
m_settings.m_inputController = ui->inputController->currentText();
|
||||
m_settingsKeys.append("inputController");
|
||||
applySettings();
|
||||
applySetting("inputController");
|
||||
updateInputController();
|
||||
}
|
||||
}
|
||||
|
@ -425,28 +422,24 @@ void GS232ControllerGUI::on_highSensitivity_clicked(bool checked)
|
|||
{
|
||||
m_settings.m_highSensitivity = checked;
|
||||
ui->highSensitivity->setText(checked ? "H" : "L");
|
||||
m_settingsKeys.append("highSensitivity");
|
||||
applySettings();
|
||||
applySetting("highSensitivity");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_enableTargetControl_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_targetControlEnabled = checked;
|
||||
m_settingsKeys.append("targetControlEnabled");
|
||||
applySettings();
|
||||
applySetting("targetControlEnabled");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_enableOffsetControl_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_offsetControlEnabled = checked;
|
||||
m_settingsKeys.append("offsetControlEnabled");
|
||||
applySettings();
|
||||
applySetting("offsetControlEnabled");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::inputConfigurationComplete()
|
||||
{
|
||||
m_settingsKeys.append("inputControllerSettings");
|
||||
applySettings();
|
||||
applySetting("inputControllerSettings");
|
||||
}
|
||||
|
||||
GS232ControllerGUI::~GS232ControllerGUI()
|
||||
|
@ -546,57 +539,50 @@ void GS232ControllerGUI::updateSerialPortList(const QStringList& serialPorts)
|
|||
ui->serialPort->blockSignals(false);
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::updatePipeList(const QList<GS232ControllerSettings::AvailableChannelOrFeature>& sources)
|
||||
void GS232ControllerGUI::updatePipeList(const AvailableChannelOrFeatureList& sources, const QStringList& renameFrom, const QStringList& renameTo)
|
||||
{
|
||||
QString currentText = ui->sources->currentText();
|
||||
QString newText;
|
||||
// Update source settting if it has been renamed
|
||||
if (renameFrom.contains(m_settings.m_source))
|
||||
{
|
||||
m_settings.m_source = renameTo[renameFrom.indexOf(m_settings.m_source)];
|
||||
applySetting("source");
|
||||
}
|
||||
|
||||
int prevIdx = ui->sources->currentIndex();
|
||||
ui->sources->blockSignals(true);
|
||||
ui->sources->clear();
|
||||
|
||||
for (const auto& source : sources)
|
||||
{
|
||||
QString name = tr("%1%2:%3 %4")
|
||||
.arg(source.m_kind)
|
||||
.arg(source.m_superIndex)
|
||||
.arg(source.m_index)
|
||||
.arg(source.m_type);
|
||||
ui->sources->addItem(name);
|
||||
for (const auto& source : sources) {
|
||||
ui->sources->addItem(source.getLongId());
|
||||
}
|
||||
|
||||
int index = ui->sources->findText(m_settings.m_source);
|
||||
ui->sources->setCurrentIndex(index);
|
||||
|
||||
if (index < 0) // current source is not found
|
||||
// Select current setting, if it exists
|
||||
// If not, and no prior setting, make sure nothing selected, as channel/feature may be created later on
|
||||
// If not found and something was previously selected, clear the setting, as probably deleted
|
||||
int idx = ui->sources->findText(m_settings.m_source);
|
||||
if (idx >= 0)
|
||||
{
|
||||
ui->sources->setCurrentIndex(idx);
|
||||
}
|
||||
else if (prevIdx == -1)
|
||||
{
|
||||
ui->sources->setCurrentIndex(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.m_source = "";
|
||||
applySetting("source");
|
||||
ui->targetName->setText("");
|
||||
m_settingsKeys.append("source");
|
||||
applySettings();
|
||||
}
|
||||
|
||||
// if (currentText.isEmpty())
|
||||
// {
|
||||
// // Source feature may be loaded after this, so may not have existed when
|
||||
// // displaySettings was called
|
||||
// if (sources.size() > 0) {
|
||||
// ui->sources->setCurrentIndex(ui->sources->findText(m_settings.m_source));
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ui->sources->setCurrentIndex(ui->sources->findText(currentText));
|
||||
// }
|
||||
|
||||
ui->sources->blockSignals(false);
|
||||
|
||||
// QString newText = ui->sources->currentText();
|
||||
|
||||
// if (currentText != newText)
|
||||
// {
|
||||
// m_settings.m_source = newText;
|
||||
// ui->targetName->setText("");
|
||||
// applySettings();
|
||||
// }
|
||||
// If no current settting, select first available
|
||||
if (m_settings.m_source.isEmpty() && (ui->sources->count() > 0))
|
||||
{
|
||||
ui->sources->setCurrentIndex(0);
|
||||
on_sources_currentTextChanged(ui->sources->currentText());
|
||||
}
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::onMenuDialogCalled(const QPoint &p)
|
||||
|
@ -626,15 +612,17 @@ void GS232ControllerGUI::onMenuDialogCalled(const QPoint &p)
|
|||
setTitle(m_settings.m_title);
|
||||
setTitleColor(m_settings.m_rgbColor);
|
||||
|
||||
m_settingsKeys.append("title");
|
||||
m_settingsKeys.append("rgbColor");
|
||||
m_settingsKeys.append("useReverseAPI");
|
||||
m_settingsKeys.append("reverseAPIAddress");
|
||||
m_settingsKeys.append("reverseAPIPort");
|
||||
m_settingsKeys.append("reverseAPIFeatureSetIndex");
|
||||
m_settingsKeys.append("reverseAPIFeatureIndex");
|
||||
QList<QString> settingsKeys({
|
||||
"rgbColor",
|
||||
"title",
|
||||
"useReverseAPI",
|
||||
"reverseAPIAddress",
|
||||
"reverseAPIPort",
|
||||
"reverseAPIDeviceIndex",
|
||||
"reverseAPIChannelIndex"
|
||||
});
|
||||
|
||||
applySettings();
|
||||
applySettings(settingsKeys);
|
||||
}
|
||||
|
||||
resetContextMenuType();
|
||||
|
@ -704,15 +692,13 @@ void GS232ControllerGUI::on_protocol_currentIndexChanged(int index)
|
|||
{
|
||||
m_settings.m_protocol = (GS232ControllerSettings::Protocol)index;
|
||||
setProtocol(m_settings.m_protocol);
|
||||
m_settingsKeys.append("protocol");
|
||||
applySettings();
|
||||
applySetting("protocol");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_connection_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_connection = (GS232ControllerSettings::Connection)index;
|
||||
m_settingsKeys.append("connection");
|
||||
applySettings();
|
||||
applySetting("connection");
|
||||
updateConnectionWidgets();
|
||||
}
|
||||
|
||||
|
@ -720,30 +706,26 @@ void GS232ControllerGUI::on_serialPort_currentIndexChanged(int index)
|
|||
{
|
||||
(void) index;
|
||||
m_settings.m_serialPort = ui->serialPort->currentText();
|
||||
m_settingsKeys.append("serialPort");
|
||||
applySettings();
|
||||
applySetting("serialPort");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_baudRate_currentIndexChanged(int index)
|
||||
{
|
||||
(void) index;
|
||||
m_settings.m_baudRate = ui->baudRate->currentText().toInt();
|
||||
m_settingsKeys.append("baudRate");
|
||||
applySettings();
|
||||
applySetting("baudRate");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_host_editingFinished()
|
||||
{
|
||||
m_settings.m_host = ui->host->text();
|
||||
m_settingsKeys.append("host");
|
||||
applySettings();
|
||||
applySetting("host");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_port_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_port = value;
|
||||
m_settingsKeys.append("port");
|
||||
applySettings();
|
||||
applySetting("port");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_coord1_valueChanged(double value)
|
||||
|
@ -770,8 +752,7 @@ void GS232ControllerGUI::on_azimuthOffset_valueChanged(double value)
|
|||
m_inputAzOffset = value;
|
||||
}
|
||||
m_settings.m_azimuthOffset = (float) value;
|
||||
m_settingsKeys.append("azimuthOffset");
|
||||
applySettings();
|
||||
applySetting("azimuthOffset");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_elevationOffset_valueChanged(double value)
|
||||
|
@ -780,58 +761,50 @@ void GS232ControllerGUI::on_elevationOffset_valueChanged(double value)
|
|||
m_inputElOffset = value;
|
||||
}
|
||||
m_settings.m_elevationOffset = (float) value;
|
||||
m_settingsKeys.append("elevationOffset");
|
||||
applySettings();
|
||||
applySetting("elevationOffset");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_azimuthMin_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_azimuthMin = value;
|
||||
m_settingsKeys.append("azimuthMin");
|
||||
applySettings();
|
||||
applySetting("azimuthMin");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_azimuthMax_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_azimuthMax = value;
|
||||
m_settingsKeys.append("azimuthMax");
|
||||
applySettings();
|
||||
applySetting("azimuthMax");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_elevationMin_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_elevationMin = value;
|
||||
m_settingsKeys.append("elevationMin");
|
||||
applySettings();
|
||||
applySetting("elevationMin");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_elevationMax_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_elevationMax = value;
|
||||
m_settingsKeys.append("elevationMax");
|
||||
applySettings();
|
||||
applySetting("elevationMax");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_tolerance_valueChanged(double value)
|
||||
{
|
||||
m_settings.m_tolerance = value;
|
||||
m_settingsKeys.append("tolerance");
|
||||
applySettings();
|
||||
applySetting("tolerance");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_precision_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_precision = value;
|
||||
setPrecision();
|
||||
m_settingsKeys.append("precision");
|
||||
applySettings();
|
||||
applySetting("precision");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_coordinates_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_coordinates = (GS232ControllerSettings::Coordinates)index;
|
||||
m_settingsKeys.append("coordinates");
|
||||
applySettings();
|
||||
applySetting("coordinates");
|
||||
|
||||
float coord1, coord2;
|
||||
azElToDisplay(m_settings.m_azimuth, m_settings.m_elevation, coord1, coord2);
|
||||
|
@ -886,8 +859,7 @@ void GS232ControllerGUI::on_track_stateChanged(int state)
|
|||
ui->targetName->setText("");
|
||||
}
|
||||
|
||||
m_settingsKeys.append("track");
|
||||
applySettings();
|
||||
applySetting("track");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_sources_currentTextChanged(const QString& text)
|
||||
|
@ -895,36 +867,31 @@ void GS232ControllerGUI::on_sources_currentTextChanged(const QString& text)
|
|||
qDebug("GS232ControllerGUI::on_sources_currentTextChanged: %s", qPrintable(text));
|
||||
m_settings.m_source = text;
|
||||
ui->targetName->setText("");
|
||||
m_settingsKeys.append("source");
|
||||
applySettings();
|
||||
applySetting("source");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_dfmTrack_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_dfmTrackOn = checked;
|
||||
m_settingsKeys.append("dfmTrackOn");
|
||||
applySettings();
|
||||
applySetting("dfmTrackOn");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_dfmLubePumps_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_dfmLubePumpsOn = checked;
|
||||
m_settingsKeys.append("dfmLubePumpsOn");
|
||||
applySettings();
|
||||
applySetting("dfmLubePumpsOn");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_dfmBrakes_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_dfmBrakesOn = checked;
|
||||
m_settingsKeys.append("dfmBrakesOn");
|
||||
applySettings();
|
||||
applySetting("dfmBrakesOn");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_dfmDrives_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_dfmDrivesOn = checked;
|
||||
m_settingsKeys.append("dfmDrivesOn");
|
||||
applySettings();
|
||||
applySetting("dfmDrivesOn");
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::on_dfmShowStatus_clicked()
|
||||
|
@ -989,17 +956,28 @@ void GS232ControllerGUI::updateStatus()
|
|||
}
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::applySettings(bool force)
|
||||
void GS232ControllerGUI::applySetting(const QString& settingsKey)
|
||||
{
|
||||
applySettings({settingsKey});
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::applySettings(const QStringList& settingsKeys, bool force)
|
||||
{
|
||||
m_settingsKeys.append(settingsKeys);
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
GS232Controller::MsgConfigureGS232Controller* message = GS232Controller::MsgConfigureGS232Controller::create(m_settings, m_settingsKeys, force);
|
||||
m_gs232Controller->getInputMessageQueue()->push(message);
|
||||
m_settingsKeys.clear();
|
||||
}
|
||||
|
||||
m_settingsKeys.clear();
|
||||
}
|
||||
|
||||
void GS232ControllerGUI::applyAllSettings()
|
||||
{
|
||||
applySettings(QStringList(), true);
|
||||
}
|
||||
|
||||
|
||||
void GS232ControllerGUI::makeUIConnections()
|
||||
{
|
||||
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &GS232ControllerGUI::on_startStop_toggled);
|
||||
|
|
|
@ -85,12 +85,14 @@ private:
|
|||
virtual ~GS232ControllerGUI();
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void applySetting(const QString& settingsKey);
|
||||
void applySettings(const QStringList& settingsKeys, bool force = false);
|
||||
void applyAllSettings();
|
||||
void displaySettings();
|
||||
void setProtocol(GS232ControllerSettings::Protocol protocol);
|
||||
void setPrecision();
|
||||
void updateConnectionWidgets();
|
||||
void updatePipeList(const QList<GS232ControllerSettings::AvailableChannelOrFeature>& sources);
|
||||
void updatePipeList(const AvailableChannelOrFeatureList& sources, const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void updateSerialPortList();
|
||||
void updateSerialPortList(const QStringList& serialPorts);
|
||||
bool handleMessage(const Message& message);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2017, 2019-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020-2021, 2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020-2021, 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -26,16 +26,10 @@
|
|||
#include "gs232controllersettings.h"
|
||||
#include "inputcontrollersettings.h"
|
||||
|
||||
const QStringList GS232ControllerSettings::m_pipeTypes = {
|
||||
QStringLiteral("ADSBDemod"),
|
||||
QStringLiteral("Map"),
|
||||
QStringLiteral("StarTracker"),
|
||||
QStringLiteral("SatelliteTracker")
|
||||
};
|
||||
|
||||
const QStringList GS232ControllerSettings::m_pipeURIs = {
|
||||
QStringLiteral("sdrangel.channel.adsbdemod"),
|
||||
QStringLiteral("sdrangel.feature.map"),
|
||||
QStringLiteral("sdrangel.feature.skymap"),
|
||||
QStringLiteral("sdrangel.feature.startracker"),
|
||||
QStringLiteral("sdrangel.feature.satellitetracker")
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020-2021, 2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020-2021, 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -31,21 +31,6 @@ class Serializable;
|
|||
|
||||
struct GS232ControllerSettings
|
||||
{
|
||||
struct AvailableChannelOrFeature
|
||||
{
|
||||
QString m_kind; //!< "R" for channel, "F" for feature
|
||||
int m_superIndex;
|
||||
int m_index;
|
||||
QString m_type;
|
||||
|
||||
AvailableChannelOrFeature() = default;
|
||||
AvailableChannelOrFeature(const AvailableChannelOrFeature&) = default;
|
||||
AvailableChannelOrFeature& operator=(const AvailableChannelOrFeature&) = default;
|
||||
bool operator==(const AvailableChannelOrFeature& a) const {
|
||||
return (m_kind == a.m_kind) && (m_superIndex == a.m_superIndex) && (m_index == a.m_index) && (m_type == a.m_type);
|
||||
}
|
||||
};
|
||||
|
||||
float m_azimuth;
|
||||
float m_elevation;
|
||||
QString m_serialPort;
|
||||
|
@ -96,7 +81,6 @@ struct GS232ControllerSettings
|
|||
void applySettings(const QStringList& settingsKeys, const GS232ControllerSettings& settings);
|
||||
QString getDebugString(const QStringList& settingsKeys, bool force=false) const;
|
||||
|
||||
static const QStringList m_pipeTypes;
|
||||
static const QStringList m_pipeURIs;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,11 @@
|
|||
#include <QSerialPort>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "maincore.h"
|
||||
#include "util/astronomy.h"
|
||||
|
||||
#include "SWGTargetAzimuthElevation.h"
|
||||
|
||||
#include "gs232controller.h"
|
||||
#include "gs232controllerworker.h"
|
||||
#include "gs232controllerreport.h"
|
||||
|
@ -32,7 +37,8 @@
|
|||
MESSAGE_CLASS_DEFINITION(GS232ControllerWorker::MsgConfigureGS232ControllerWorker, Message)
|
||||
MESSAGE_CLASS_DEFINITION(GS232ControllerReport::MsgReportAzAl, Message)
|
||||
|
||||
GS232ControllerWorker::GS232ControllerWorker() :
|
||||
GS232ControllerWorker::GS232ControllerWorker(GS232Controller *controller) :
|
||||
m_controller(controller),
|
||||
m_msgQueueToFeature(nullptr),
|
||||
m_device(nullptr),
|
||||
m_serialPort(this),
|
||||
|
@ -193,6 +199,8 @@ void GS232ControllerWorker::applySettings(const GS232ControllerSettings& setting
|
|||
{
|
||||
setAzimuth(azimuth);
|
||||
}
|
||||
|
||||
sendToSkyMap(azimuth, elevation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,3 +288,18 @@ void GS232ControllerWorker::update()
|
|||
}
|
||||
}
|
||||
|
||||
void GS232ControllerWorker::sendToSkyMap(float azimuth, float elevation)
|
||||
{
|
||||
QList<ObjectPipe*> targetPipes;
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(m_controller, "target", targetPipes);
|
||||
|
||||
for (const auto& pipe : targetPipes)
|
||||
{
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
SWGSDRangel::SWGTargetAzimuthElevation *swgTarget = new SWGSDRangel::SWGTargetAzimuthElevation();
|
||||
swgTarget->setName(new QString("Rotator"));
|
||||
swgTarget->setAzimuth(azimuth);
|
||||
swgTarget->setElevation(elevation);
|
||||
messageQueue->push(MainCore::MsgTargetAzimuthElevation::create(m_controller, swgTarget));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "gs232controllersettings.h"
|
||||
#include "controllerprotocol.h"
|
||||
|
||||
class GS232Controller;
|
||||
|
||||
class GS232ControllerWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -62,7 +64,7 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
GS232ControllerWorker();
|
||||
GS232ControllerWorker(GS232Controller *controller);
|
||||
~GS232ControllerWorker();
|
||||
void startWork();
|
||||
void stopWork();
|
||||
|
@ -72,6 +74,7 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
GS232Controller *m_controller;
|
||||
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
||||
MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object
|
||||
GS232ControllerSettings m_settings;
|
||||
|
@ -92,6 +95,7 @@ private:
|
|||
QIODevice *openSocket(const GS232ControllerSettings& settings);
|
||||
void setAzimuth(float azimuth);
|
||||
void setAzimuthElevation(float azimuth, float elevation);
|
||||
void sendToSkyMap(float azimuth, float elevation);
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
|
|
|
@ -185,8 +185,8 @@ The controller uses the 'P' and 'p' commands to set and get azimuth and elevatio
|
|||
|
||||
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to set the azimuth and elevation from the command line:
|
||||
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "GS232Controller", "GS232ControllerSettings": { "azimuth": 180, "elevation": 45 }}'
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "GS232Controller", "GS232ControllerSettings": { "azimuth": 180, "elevation": 45 }}'
|
||||
|
||||
To start sending commands to the rotator:
|
||||
|
||||
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/run"
|
||||
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/feature/0/run"
|
||||
|
|
|
@ -97,8 +97,8 @@ The statistics fields display the statistics for the current test:
|
|||
|
||||
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to set the azimuth and elevation from the command line:
|
||||
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "PERTester", "PERTesterSettings": { "packetCount": 100 }}'
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "PERTester", "PERTesterSettings": { "packetCount": 100 }}'
|
||||
|
||||
To start sending the test:
|
||||
|
||||
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/run"
|
||||
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/feature/0/run"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -41,7 +41,8 @@ const char* const Radiosonde::m_featureIdURI = "sdrangel.feature.radiosonde";
|
|||
const char* const Radiosonde::m_featureId = "Radiosonde";
|
||||
|
||||
Radiosonde::Radiosonde(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface)
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface),
|
||||
m_availableChannelHandler({"sdrangel.channel.radiosondedemod"}, QStringList{"radiosonde"})
|
||||
{
|
||||
qDebug("Radiosonde::Radiosonde: webAPIAdapterInterface: %p", webAPIAdapterInterface);
|
||||
setObjectName(m_featureId);
|
||||
|
@ -54,22 +55,22 @@ Radiosonde::Radiosonde(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
|||
this,
|
||||
&Radiosonde::networkManagerFinished
|
||||
);
|
||||
scanAvailableChannels();
|
||||
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
&m_availableChannelHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&Radiosonde::handleChannelAdded
|
||||
);
|
||||
&Radiosonde::handleChannelMessageQueue);
|
||||
m_availableChannelHandler.scanAvailableChannelsAndFeatures();
|
||||
}
|
||||
|
||||
Radiosonde::~Radiosonde()
|
||||
{
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
&m_availableChannelHandler,
|
||||
&AvailableChannelOrFeatureHandler::messageEnqueued,
|
||||
this,
|
||||
&Radiosonde::handleChannelAdded
|
||||
&Radiosonde::handleChannelMessageQueue
|
||||
);
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
|
@ -362,90 +363,6 @@ void Radiosonde::networkManagerFinished(QNetworkReply *reply)
|
|||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void Radiosonde::scanAvailableChannels()
|
||||
{
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
MessagePipes& messagePipes = mainCore->getMessagePipes();
|
||||
std::vector<DeviceSet*>& deviceSets = mainCore->getDeviceSets();
|
||||
m_availableChannels.clear();
|
||||
|
||||
for (const auto& deviceSet : deviceSets)
|
||||
{
|
||||
DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine;
|
||||
|
||||
if (deviceSourceEngine)
|
||||
{
|
||||
for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++)
|
||||
{
|
||||
ChannelAPI *channel = deviceSet->getChannelAt(chi);
|
||||
|
||||
if ((channel->getURI() == "sdrangel.channel.radiosondedemod") && !m_availableChannels.contains(channel))
|
||||
{
|
||||
qDebug("Radiosonde::scanAvailableChannels: register %d:%d (%p)", deviceSet->getIndex(), chi, channel);
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(channel, this, "radiosonde");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handleChannelMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
QObject::connect(
|
||||
pipe,
|
||||
&ObjectPipe::toBeDeleted,
|
||||
this,
|
||||
&Radiosonde::handleMessagePipeToBeDeleted
|
||||
);
|
||||
m_availableChannels.insert(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Radiosonde::handleChannelAdded(int deviceSetIndex, ChannelAPI *channel)
|
||||
{
|
||||
qDebug("Radiosonde::handleChannelAdded: deviceSetIndex: %d:%d channel: %s (%p)",
|
||||
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
|
||||
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
|
||||
DeviceSet *deviceSet = deviceSets[deviceSetIndex];
|
||||
DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine;
|
||||
|
||||
if (deviceSourceEngine && (channel->getURI() == "sdrangel.channel.radiosondedemod"))
|
||||
{
|
||||
if (!m_availableChannels.contains(channel))
|
||||
{
|
||||
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(channel, this, "radiosonde");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handleChannelMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
QObject::connect(
|
||||
pipe,
|
||||
&ObjectPipe::toBeDeleted,
|
||||
this,
|
||||
&Radiosonde::handleMessagePipeToBeDeleted
|
||||
);
|
||||
m_availableChannels.insert(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Radiosonde::handleMessagePipeToBeDeleted(int reason, QObject* object)
|
||||
{
|
||||
if ((reason == 0) && m_availableChannels.contains((ChannelAPI*) object)) // producer (channel)
|
||||
{
|
||||
qDebug("Radiosonde::handleMessagePipeToBeDeleted: removing channel at (%p)", object);
|
||||
m_availableChannels.remove((ChannelAPI*) object);
|
||||
}
|
||||
}
|
||||
|
||||
void Radiosonde::handleChannelMessageQueue(MessageQueue* messageQueue)
|
||||
{
|
||||
Message* message;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
|
||||
// Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
#include "availablechannelorfeaturehandler.h"
|
||||
|
||||
#include "radiosondesettings.h"
|
||||
|
||||
|
@ -101,7 +102,8 @@ public:
|
|||
|
||||
private:
|
||||
RadiosondeSettings m_settings;
|
||||
QSet<ChannelAPI*> m_availableChannels;
|
||||
AvailableChannelOrFeatureHandler m_availableChannelHandler;
|
||||
AvailableChannelOrFeatureList m_availableChannels;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
@ -110,12 +112,9 @@ private:
|
|||
void stop();
|
||||
void applySettings(const RadiosondeSettings& settings, const QList<QString>& settingsKeys, bool force = false);
|
||||
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const RadiosondeSettings& settings, bool force);
|
||||
void scanAvailableChannels();
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
void handleChannelAdded(int deviceSetIndex, ChannelAPI *channel);
|
||||
void handleMessagePipeToBeDeleted(int reason, QObject* object);
|
||||
void handleChannelMessageQueue(MessageQueue* messageQueue);
|
||||
};
|
||||
|
||||
|
|
|
@ -216,12 +216,12 @@ Icons are by Freepik from Flaticon https://www.flaticon.com/
|
|||
|
||||
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to set the satellites to track:
|
||||
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "SatelliteTracker", "SatelliteTrackerSettings": { "satellites": [ "NOAA 15", "NOAA 19" ] }}'
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "SatelliteTracker", "SatelliteTrackerSettings": { "satellites": [ "NOAA 15", "NOAA 19" ] }}'
|
||||
|
||||
And how to set the target:
|
||||
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "SatelliteTracker", "SatelliteTrackerSettings": { "target": "NOAA 15" }}'
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "SatelliteTracker", "SatelliteTrackerSettings": { "target": "NOAA 15" }}'
|
||||
|
||||
Or, to set the device settings:
|
||||
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "SatelliteTracker", "SatelliteTrackerSettings": { "deviceSettings": [ { "satellite": "ISS", "deviceSettings": [ { "deviceSet": "R0", "doppler": [0], "frequency": 0, "presetDescription": Sat", "presetFrequency": 145.825, "presetGroup": "ISS Digi", "startOnAOS": 1, "startStopFileSinks": 1, "stopOnLOS": 1}] } ] }}'
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "SatelliteTracker", "SatelliteTrackerSettings": { "deviceSettings": [ { "satellite": "ISS", "deviceSettings": [ { "deviceSet": "R0", "doppler": [0], "frequency": 0, "presetDescription": Sat", "presetFrequency": 145.825, "presetGroup": "ISS Digi", "startOnAOS": 1, "startStopFileSinks": 1, "stopOnLOS": 1}] } ] }}'
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
project(skymap)
|
||||
|
||||
set(skymap_SOURCES
|
||||
skymap.cpp
|
||||
skymapsettings.cpp
|
||||
skymapplugin.cpp
|
||||
skymapwebapiadapter.cpp
|
||||
webserver.cpp
|
||||
)
|
||||
|
||||
set(skymap_HEADERS
|
||||
skymap.h
|
||||
skymapsettings.h
|
||||
skymapplugin.h
|
||||
skymapreport.h
|
||||
skymapwebapiadapter.h
|
||||
webserver.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
${Qt${QT_DEFAULT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
set(skymap_SOURCES
|
||||
${skymap_SOURCES}
|
||||
skymapgui.cpp
|
||||
skymapgui.ui
|
||||
skymapsettingsdialog.cpp
|
||||
skymapsettingsdialog.ui
|
||||
websocketserver.cpp
|
||||
skymap.qrc
|
||||
webinterface.cpp
|
||||
wtml.cpp
|
||||
webview.cpp
|
||||
icons.qrc
|
||||
)
|
||||
set(skymap_HEADERS
|
||||
${skymap_HEADERS}
|
||||
skymapgui.h
|
||||
skymapsettingsdialog.h
|
||||
skymapmodel.h
|
||||
skymapitem.h
|
||||
websocketserver.h
|
||||
skymaptileserver.h
|
||||
webinterface.h
|
||||
wtml.h
|
||||
webview.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME featureskymap)
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
|
||||
if(Qt${QT_DEFAULT_MAJOR_VERSION}WebEngineCore_FOUND)
|
||||
set(TARGET_LIB "Qt::Widgets" Qt::Positioning Qt::Location Qt::WebEngineCore Qt::WebEngineWidgets)
|
||||
elseif(Qt${QT_DEFAULT_MAJOR_VERSION}WebEngine_FOUND)
|
||||
set(TARGET_LIB "Qt::Widgets" Qt::Positioning Qt::Location Qt::WebEngine Qt::WebEngineCore Qt::WebEngineWidgets)
|
||||
else()
|
||||
set(TARGET_LIB "Qt::Widgets" Qt::Positioning Qt::Location)
|
||||
endif()
|
||||
else()
|
||||
set(TARGET_NAME featureskymapsrv)
|
||||
set(TARGET_LIB "")
|
||||
set(TARGET_LIB_GUI "")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||
endif()
|
||||
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${skymap_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 QtQuick etc
|
||||
include(DeployQt)
|
||||
windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} ${PROJECT_SOURCE_DIR}/skymap)
|
||||
endif()
|
||||
|
||||
# Install debug symbols
|
||||
if (WIN32)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
|
||||
endif()
|
|
@ -0,0 +1,166 @@
|
|||
<!doctype html>
|
||||
<html lang="en" style="height:100%;min-height:100%;">
|
||||
<head>
|
||||
<title>SDRangel Aladin</title>
|
||||
|
||||
<style>
|
||||
.aladin-fov {
|
||||
display: none;
|
||||
visibility: hidden
|
||||
}
|
||||
.aladin-location {
|
||||
display: none;
|
||||
visibility: hidden
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="margin:0;padding:0;height:100%;">
|
||||
|
||||
<div id="aladin-lite-div" style="width:100%;height:100%;"></div>
|
||||
<script type="text/javascript" src="https://aladin.cds.unistra.fr/AladinLite/api/v3/latest/aladin.js" charset="utf-8"></script>
|
||||
<script type="text/javascript">
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
// Create Aladin
|
||||
aladin = A.aladin('#aladin-lite-div', {
|
||||
survey: "P/DSS2/color",
|
||||
fov: 60,
|
||||
showProjectionControl: false,
|
||||
showFrame: false,
|
||||
fullScreen: true, // fullScreen in terms of the div element, not the entire monitor
|
||||
showFullscreenControl: false,
|
||||
showGotoControl: false,
|
||||
showZoomControl: false,
|
||||
showSimbadPointerControl: true,
|
||||
showContextMenu: true
|
||||
});
|
||||
|
||||
// Load some catalogs, but don't show them - displayLabel doesn't seem to work for these ProgressiveCat only Catalog
|
||||
let hips = A.catalogHiPS('https://axel.u-strasbg.fr/HiPSCatService/Simbad', { onClick: 'showTable', name: 'Simbad' });
|
||||
hips.isShowing = false;
|
||||
aladin.addCatalog(hips);
|
||||
hips = A.catalogHiPS('http://axel.u-strasbg.fr/HiPSCatService/I/345/gaia2', { onClick: 'showTable', color: 'orange', name: 'Gaia' });
|
||||
hips.isShowing = false;
|
||||
aladin.addCatalog(hips);
|
||||
|
||||
// Report ra/dec/time
|
||||
setInterval(function () { reportView(); }, 100);
|
||||
|
||||
reportReady();
|
||||
});
|
||||
|
||||
// Stop browser popup menu from appearing over the top of the Aladin menu
|
||||
document.addEventListener('contextmenu', event => event.preventDefault());
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// Use WebSockets for handling commands from SDRangel Plugin
|
||||
// and sending events back to it
|
||||
let socket = new WebSocket("ws://127.0.0.1:$WS_PORT$");
|
||||
|
||||
var antennaHpbw = 5.0;
|
||||
var antennaOverlay;
|
||||
var antennaCircle;
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
try {
|
||||
const command = JSON.parse(event.data);
|
||||
|
||||
if (command.command == "setView") {
|
||||
aladin.gotoRaDec(command.ra * 15.0, command.dec); // Convert RA from hours to degrees, J2000
|
||||
} else if (command.command == "setPosition") {
|
||||
// Not supported
|
||||
} else if (command.command == "projection") {
|
||||
aladin.setProjection(command.projection);
|
||||
} else if (command.command == "setDateTime") {
|
||||
// Not supported
|
||||
} else if (command.command == "track") {
|
||||
aladin.gotoObject(command.name);
|
||||
} else if (command.command == "setBackground") {
|
||||
aladin.setImageSurvey(command.background);
|
||||
} else if (command.command == "setProjection") {
|
||||
aladin.setProjection(command.projection);
|
||||
} else if (command.command == "showNames") {
|
||||
for (var i = 0; i < aladin.view.catalogs.length; i++) {
|
||||
aladin.view.catalogs[i].displayLabel = command.show;
|
||||
}
|
||||
aladin.view.requestRedraw();
|
||||
} else if (command.command == "showConstellations") {
|
||||
// Not supported
|
||||
} else if (command.command == "showReticle") {
|
||||
aladin.showReticle(command.show);
|
||||
} else if (command.command == "showGrid") {
|
||||
if (command.show) {
|
||||
aladin.showCooGrid();
|
||||
} else {
|
||||
aladin.hideCooGrid();
|
||||
}
|
||||
} else if (command.command == "showAntennaFoV") {
|
||||
if (command.show) {
|
||||
showAntennaFoV();
|
||||
} else {
|
||||
hideAntennaFoV();
|
||||
}
|
||||
} else if (command.command == "setAntennaFoV") {
|
||||
antennaHpbw = command.hpbw;
|
||||
if (antennaCircle) {
|
||||
antennaCircle.setRadius(antennaHpbw);
|
||||
}
|
||||
} else {
|
||||
console.log(`Unknown command ${command.command}`);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log(`Erroring processing received message:\n${e}\n${event.data}`);
|
||||
}
|
||||
};
|
||||
|
||||
function createAntennaFoV() {
|
||||
antennaOverlay = A.graphicOverlay({ color: '#ffffff', lineWidth: 1 });
|
||||
aladin.addOverlay(antennaOverlay);
|
||||
const [ra, dec] = aladin.getRaDec();
|
||||
antennaCircle = A.circle(ra, dec, antennaHpbw);
|
||||
antennaOverlay.add(antennaCircle);
|
||||
}
|
||||
|
||||
function showAntennaFoV() {
|
||||
antennaCircle.show();
|
||||
}
|
||||
|
||||
function hideAntennaFoV() {
|
||||
antennaCircle.hide();
|
||||
}
|
||||
|
||||
function reportReady() {
|
||||
console.log("reportReady " + socket.readyState);
|
||||
|
||||
createAntennaFoV();
|
||||
|
||||
if (socket.readyState === 1) {
|
||||
socket.send(JSON.stringify({
|
||||
event: "ready"
|
||||
}));
|
||||
} else {
|
||||
setTimeout(reportReady, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function reportView() {
|
||||
if (socket.readyState === 1) {
|
||||
const [ra, dec] = aladin.getRaDec();
|
||||
const [fovX, fovY] = aladin.getFov();
|
||||
socket.send(JSON.stringify({
|
||||
event: "view",
|
||||
ra: ra / 15.0, // Convert RA from degrees to hours
|
||||
dec: dec,
|
||||
fov: fovX
|
||||
}));
|
||||
|
||||
antennaCircle.setCenter([ra, dec]);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,181 @@
|
|||
<!doctype html>
|
||||
<html lang="en" style="height:100%;min-height:100%;">
|
||||
<head>
|
||||
<title>SDRangel ESASky</title>
|
||||
</head>
|
||||
|
||||
<body style="margin:0;padding:0;height:100%;">
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var esaskyFrame;
|
||||
var antennaHpbw = 5.0;
|
||||
var showAntenna = false;
|
||||
|
||||
// API reference: https://www.cosmos.esa.int/web/esdc/esasky-javascript-api
|
||||
function on_ready() {
|
||||
esaskyFrame = document.getElementsByClassName("iframe-container")[0].children[0];
|
||||
|
||||
var cmd = { event: 'setModuleVisibility', content: { modules: { jwst_planning_button: false } } };
|
||||
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
|
||||
|
||||
window.addEventListener("message", handleMessage);
|
||||
|
||||
setTimeout(function () { reportReady(); }, 500); // Add a delay, otherwise subsequent commands to set grid seem to get lost
|
||||
|
||||
// Can't access contentDocument due to same origin policy
|
||||
//console.log(esaskyFrame.contentDocument);
|
||||
//console.log("containers: " + esaskyFrame.contentDocument.getElementsByName("aladin-container"));
|
||||
|
||||
// Report ra/dec/time
|
||||
// We don't use events, as they are too slow (view_changed is only sent after all data has been calculated)
|
||||
setInterval(function () { reportView(); }, 100);
|
||||
//setTimeout(function () { requestEvents(); }, 1000);
|
||||
}
|
||||
|
||||
/*function requestEvents() {
|
||||
var cmd1 = { event: 'registerEventListener', content: {} };
|
||||
esaskyFrame.contentWindow.postMessage(cmd1, 'https://sky.esa.int');
|
||||
|
||||
var cmd2 = { event: 'registerFoVChangedListener', content: {} };
|
||||
esaskyFrame.contentWindow.postMessage(cmd2, 'https://sky.esa.int');
|
||||
}*/
|
||||
|
||||
// Use WebSockets for handling commands from SDRangel Plugin
|
||||
// and sending events back to it
|
||||
let socket = new WebSocket("ws://127.0.0.1:$WS_PORT$");
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
try {
|
||||
const command = JSON.parse(event.data);
|
||||
|
||||
if (command.command == "setView") {
|
||||
var cmd = { event: 'goToRaDec', content: { ra: command.ra * 15.0, dec: command.dec } }; // Convert RA hours to decimal degrees
|
||||
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
|
||||
} else if (command.command == "setPosition") {
|
||||
// Not supported
|
||||
} else if (command.command == "setDateTime") {
|
||||
// Not supported
|
||||
} else if (command.command == "track") {
|
||||
var cmd = { event: 'goToTargetName', content: { targetName: command.name } };
|
||||
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
|
||||
} else if (command.command == "setBackground") {
|
||||
// Leave to its own GUI
|
||||
} else if (command.command == "showConstellations") {
|
||||
// Not supported
|
||||
} else if (command.command == "showReticle") {
|
||||
// Not supported
|
||||
} else if (command.command == "showGrid") {
|
||||
var cmd = { event: 'showCoordinateGrid', content: { show: command.show } };
|
||||
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
|
||||
} else if (command.command == "showAntennaFoV") {
|
||||
showAntenna = command.show;
|
||||
if (!showAntenna) {
|
||||
hideAntennaFoV();
|
||||
}
|
||||
} else if (command.command == "setAntennaFoV") {
|
||||
antennaHpbw = command.hpbw;
|
||||
} else {
|
||||
console.log(`Unknown command ${command.command}`);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log(`Erroring processing received message:\n${e}\n${event.data}`);
|
||||
}
|
||||
};
|
||||
|
||||
function reportReady() {
|
||||
console.log("reportReady " + socket.readyState);
|
||||
if (socket.readyState === 1) {
|
||||
socket.send(JSON.stringify({
|
||||
event: "ready"
|
||||
}));
|
||||
} else {
|
||||
setTimeout(reportReady, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMessage(e) {
|
||||
//console.log(e);
|
||||
|
||||
var data = e.data;
|
||||
if (data.origin == "esasky") {
|
||||
|
||||
var ra, dec, fov;
|
||||
|
||||
if (data.hasOwnProperty('event')) {
|
||||
if (data.event.action == "view_changed") {
|
||||
ra = data.event.values["ra"];
|
||||
dec = data.event.values["dec"];
|
||||
fov = data.event.values["fov"];
|
||||
}
|
||||
} else {
|
||||
ra = data.values["ra"];
|
||||
dec = data.values["dec"];
|
||||
fov = data.values["fov"];
|
||||
}
|
||||
|
||||
//console.log("handleMessage esasky: " + ra + " " + dec + " " + fov);
|
||||
if (socket.readyState === 1) {
|
||||
socket.send(JSON.stringify({
|
||||
event: "view",
|
||||
ra: ra / 15.0, // convert decimal degrees to hours
|
||||
dec: dec,
|
||||
fov: fov
|
||||
}));
|
||||
}
|
||||
|
||||
if (showAntenna) {
|
||||
showAntennaFoV(ra, dec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reportView() {
|
||||
if (socket.readyState === 1) {
|
||||
var cmd = { event: 'getCenter', content: { cooFrame: 'J2000' } };
|
||||
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
|
||||
}
|
||||
}
|
||||
|
||||
function hideAntennaFoV() {
|
||||
var cmd = { event: 'deleteFootprintsOverlay', content: { 'overlayName': 'Antenna FoV' } };
|
||||
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
|
||||
}
|
||||
|
||||
function showAntennaFoV(ra, dec) {
|
||||
var cmd =
|
||||
{
|
||||
event: 'overlayFootprints',
|
||||
content: {
|
||||
'overlaySet':
|
||||
{
|
||||
'type': 'FootprintListOverlay',
|
||||
'overlayName': 'Antenna FoV',
|
||||
'cooframe': 'J2000',
|
||||
'color': 'white',
|
||||
'lineWidth': 5,
|
||||
'skyObjectList': [
|
||||
{
|
||||
'name': 'Antenna FoV',
|
||||
'id': 1,
|
||||
'stcs': 'CIRCLE J2000 ' + ra + ' ' + dec + ' ' + (antennaHpbw / 2.0),
|
||||
'ra_deg': ra,
|
||||
'dec_deg': dec
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
esaskyFrame.contentWindow.postMessage(cmd, 'https://sky.esa.int');
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="iframe-container" style="width: 100%; height: 100%">
|
||||
<iframe alt="" border="0" bordercolor="#000000" frameborder="0" height="" hspace="0" id="_ESAsky" longdesc="" name="_ESASky" onload="on_ready();" scrolling="auto" src="https://sky.esa.int?hide_welcome=true&&hide_banner_info=true" title="" vspace="0" width="100%" class="" style="height: 100%;">
|
||||
Frames not supported.
|
||||
</iframe>
|
||||
</div>
|
||||
|
||||
</body >
|
||||
</html >
|
|
@ -0,0 +1,828 @@
|
|||
<!doctype html>
|
||||
<html lang="en" style="height:100%;min-height:100%;">
|
||||
<head>
|
||||
<title>SDRangel WWT</title>
|
||||
<script src="https://web.wwtassets.org/engine/7/wwtsdk.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
overflow: hidden; /* Hide scrollbars if popup window near edge */
|
||||
}
|
||||
.popup {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background-color: #fff;
|
||||
border-radius: 25px;
|
||||
padding: 15px;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
.popup .popuptext {
|
||||
}
|
||||
.popup .close {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body style="margin:0;padding:0;height:100%;">
|
||||
|
||||
|
||||
<div class="popup" id="popupDiv" onmousedown="dragDown()">
|
||||
<button aria-hidden="true" class="close" type="button" onclick="hidePlace()">x</button>
|
||||
<span class="popuptext" id="myPopup"></span>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="wwtcanvas" style="width: 100%; height: 100%; background-color: #000"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var script_interface, wwt;
|
||||
var circle, antennaFoV;
|
||||
var antennaHpbw = 5.0;
|
||||
var wwtSettings = {
|
||||
constellationBoundaries: "false",
|
||||
constellationFigures: "true",
|
||||
constellationLabels: "true",
|
||||
constellationPictures: "false",
|
||||
constellationSelection: "false",
|
||||
ecliptic: "false",
|
||||
eclipticOverviewText: "false",
|
||||
eclipticGrid: "false",
|
||||
eclipticGridText: "true",
|
||||
altAzGrid: "true",
|
||||
altAzGridText: "true",
|
||||
equatorialGrid: "false",
|
||||
equatorialGridText: "true",
|
||||
galacticGrid: "false",
|
||||
galacticGridText: "true",
|
||||
precessionChart: "false",
|
||||
iss: "false",
|
||||
solarSystemCosmos: "false",
|
||||
solarSystemLighting: "true",
|
||||
solarSystemMilkyWay: "true",
|
||||
solarSystemMinorOrbits: "false",
|
||||
solarSystemMinorPlanets: "false",
|
||||
solarSystemMultiRes: "true",
|
||||
solarSystemOrbits: "true",
|
||||
solarSystemOverlays: "false",
|
||||
solarSystemPlanets: "true",
|
||||
solarSystemStars: "true",
|
||||
};
|
||||
var showNames = true;
|
||||
var showGrid = false;
|
||||
var showConstellations = false;
|
||||
|
||||
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
||||
var popup = document.getElementById("popupDiv");
|
||||
|
||||
function dragDown(e) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// get the mouse cursor position at startup:
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
document.onmouseup = closeDragElement;
|
||||
// call a function whenever the cursor moves:
|
||||
document.onmousemove = elementDrag;
|
||||
}
|
||||
|
||||
function elementDrag(e) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// calculate the new cursor position:
|
||||
pos1 = pos3 - e.clientX;
|
||||
pos2 = pos4 - e.clientY;
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
// set the element's new position:
|
||||
popup.style.top = (popup.offsetTop - pos2) + "px";
|
||||
popup.style.left = (popup.offsetLeft - pos1) + "px";
|
||||
}
|
||||
|
||||
function closeDragElement() {
|
||||
// stop moving when mouse button is released:
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
}
|
||||
|
||||
function init_wwt() {
|
||||
const builder = new wwtlib.WWTControlBuilder("wwtcanvas");
|
||||
builder.startRenderLoop(true);
|
||||
script_interface = builder.create();
|
||||
script_interface.add_ready(on_ready);
|
||||
}
|
||||
|
||||
// API reference: https://docs.worldwidetelescope.org/webgl-reference/latest/apiref/engine/classes/WWTControl-1.html
|
||||
function on_ready() {
|
||||
wwt = wwtlib.WWTControl.singleton;
|
||||
reportReady();
|
||||
|
||||
// Report ra/dec/time
|
||||
setInterval(function () { reportView(); }, 100);
|
||||
|
||||
// Load search data
|
||||
// This sets the wwt.searchData variable to what's in https://web.wwtassets.org/data/searchdata_v2.min.js
|
||||
var scr = document.createElement('script');
|
||||
scr.setAttribute("src", wwtlib.URLHelpers.singleton.coreStaticUrl('data/searchdata_v2.min.js'));
|
||||
document.getElementsByTagName("head")[0].appendChild(scr);
|
||||
setTimeout(initSearch, 333);
|
||||
}
|
||||
|
||||
window.addEventListener("load", init_wwt);
|
||||
|
||||
// Use WebSockets for handling commands from WorldwideTelescope SDRangel Plugin
|
||||
// and sending events back to it
|
||||
let socket = new WebSocket("ws://127.0.0.1:$WS_PORT$");
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
try {
|
||||
const command = JSON.parse(event.data);
|
||||
|
||||
if (command.command == "setView") {
|
||||
wwt.gotoRADecZoom(command.ra, command.dec, wwt.renderContext.viewCamera.zoom, true); // Requires RA in hours, J2000
|
||||
} else if (command.command == "setPosition") {
|
||||
script_interface.settings.set_locationLat(command.latitude);
|
||||
script_interface.settings.set_locationLng(command.longitude);
|
||||
script_interface.settings.set_locationAltitude(command.altitude);
|
||||
} else if (command.command == "setProjection") {
|
||||
if (command.projection == "Solar system") {
|
||||
wwt.setBackgroundImageByName("Solar System");
|
||||
}
|
||||
// In other cases, we just use setBackground which will follow
|
||||
} else if (command.command == "setDateTime") {
|
||||
// Set current date and time of viewer
|
||||
//console.log("setDateTime " + command.dateTime);
|
||||
//const dt = new Date(command.dateTime);
|
||||
const dt = Date.parse(command.dateTime);
|
||||
wwtlib.SpaceTimeController.set_now(dt);
|
||||
} else if (command.command == "track") {
|
||||
//console.log("Finding " + command.name);
|
||||
const place = search(command.name);
|
||||
if (place) {
|
||||
//console.log(place);
|
||||
wwt.gotoTarget(place, false, true, true);
|
||||
} else {
|
||||
console.log("Can't find " + command.name);
|
||||
}
|
||||
} else if (command.command == "setBackground") {
|
||||
if (command.background != "") {
|
||||
wwt.setBackgroundImageByName(command.background);
|
||||
}
|
||||
} else if (command.command == "showNames") {
|
||||
showNames = command.show;
|
||||
script_interface.settings.set_showConstellationLabels(showNames && showConstellations && (wwtSettings.constellationLabels === "true"));
|
||||
script_interface.settings.set_showEclipticOverviewText(showNames && (wwtSettings.eclipticOverviewText === "true"))
|
||||
} else if (command.command == "showConstellations") {
|
||||
showConstellations = command.show;
|
||||
const ok = script_interface.settings.set_showConstellations(showConstellations);
|
||||
script_interface.settings.set_showConstellationBoundries(showConstellations && (wwtSettings.constellationBoundaries === "true"));
|
||||
script_interface.settings.set_showConstellationFigures(showConstellations && (wwtSettings.constellationFigures === "true"));
|
||||
script_interface.settings.set_showConstellationLabels(showNames && showConstellations && (wwtSettings.constellationLabels === "true"));
|
||||
script_interface.settings.set_showConstellationPictures(showConstellations && (wwtSettings.constellationPictures === "true"));
|
||||
script_interface.settings.set_showConstellationSelection(showConstellations && (wwtSettings.constellationSelection === "true"));
|
||||
} else if (command.command == "showReticle") {
|
||||
script_interface.settings.set_showCrosshairs(command.show);
|
||||
} else if (command.command == "showGrid") {
|
||||
showGrid = command.show;
|
||||
script_interface.settings.set_showEclipticGrid(showGrid && (wwtSettings.eclipticGrid === "true"));
|
||||
script_interface.settings.set_showEclipticGridText(showGrid && (wwtSettings.eclipticGridText === "true"));
|
||||
script_interface.settings.set_showAltAzGrid(showGrid && (wwtSettings.altAzGrid === "true"));
|
||||
script_interface.settings.set_showAltAzGridText(showGrid && (wwtSettings.altAzGridText === "true"));
|
||||
script_interface.settings.set_showGrid(showGrid && (wwtSettings.equatorialGrid === "true"));
|
||||
script_interface.settings.set_showEquatorialGridText(showGrid && (wwtSettings.equatorialGridText === "true"));
|
||||
script_interface.settings.set_showGalacticGrid(showGrid && (wwtSettings.galacticGrid === "true"));
|
||||
script_interface.settings.set_showGalacticGridText(showGrid && (wwtSettings.galacticGridText === "true"));
|
||||
} else if (command.command == "showAntennaFoV") {
|
||||
if (command.show) {
|
||||
showAntennaFoV();
|
||||
} else {
|
||||
hideAntennaFoV();
|
||||
}
|
||||
} else if (command.command == "setAntennaFoV") {
|
||||
antennaHpbw = command.hpbw;
|
||||
if (antennaFoV) {
|
||||
antennaFoV.set_radius(radiusAdjust(antennaHpbw, script_interface.getDec()));
|
||||
}
|
||||
} else if (command.command == "setWWTSettings") {
|
||||
Object.assign(wwtSettings, command);
|
||||
script_interface.settings.set_showConstellationBoundries(showConstellations && (wwtSettings.constellationBoundaries === "true"));
|
||||
script_interface.settings.set_showConstellationFigures(showConstellations && (wwtSettings.constellationFigures === "true"));
|
||||
script_interface.settings.set_showConstellationLabels(showNames && showConstellations && (wwtSettings.constellationLabels === "true"));
|
||||
script_interface.settings.set_showConstellationPictures(showConstellations && (wwtSettings.constellationPictures === "true"));
|
||||
script_interface.settings.set_showConstellationSelection(showConstellations && (wwtSettings.constellationSelection === "true"));
|
||||
|
||||
script_interface.settings.set_showEcliptic(wwtSettings.ecliptic === "true")
|
||||
script_interface.settings.set_showEclipticOverviewText(showNames && (wwtSettings.eclipticOverviewText === "true"))
|
||||
|
||||
script_interface.settings.set_showEclipticGrid(showGrid && (wwtSettings.eclipticGrid === "true"));
|
||||
script_interface.settings.set_showEclipticGridText(showGrid && (wwtSettings.eclipticGridText === "true"));
|
||||
script_interface.settings.set_showAltAzGrid(showGrid && (wwtSettings.altAzGrid === "true"));
|
||||
script_interface.settings.set_showAltAzGridText(showGrid && (wwtSettings.altAzGridText === "true"));
|
||||
script_interface.settings.set_showGrid(showGrid && (wwtSettings.equatorialGrid === "true"));
|
||||
script_interface.settings.set_showEquatorialGridText(showGrid && (wwtSettings.equatorialGridText === "true"));
|
||||
script_interface.settings.set_showGalacticGrid(showGrid && (wwtSettings.galacticGrid === "true"));
|
||||
script_interface.settings.set_showGalacticGridText(showGrid && (wwtSettings.galacticGridText === "true"));
|
||||
script_interface.settings.set_showPrecessionChart(showGrid && (wwtSettings.precessionChart === "true"));
|
||||
|
||||
// Solar System view
|
||||
// Need to zoom a long way out to see Cosmos
|
||||
script_interface.settings.set_solarSystemCosmos(wwtSettings.solarSystemCosmos === "true");
|
||||
script_interface.settings.set_solarSystemLighting(wwtSettings.solarSystemLighting === "true");
|
||||
script_interface.settings.set_solarSystemMilkyWay(wwtSettings.solarSystemMilkyWay === "true");
|
||||
script_interface.settings.set_solarSystemMinorOrbits(wwtSettings.solarSystemMinorOrbits === "true");
|
||||
script_interface.settings.set_solarSystemMinorPlanets(wwtSettings.solarSystemMinorPlanets === "true");
|
||||
script_interface.settings.set_solarSystemMultiRes(wwtSettings.solarSystemMultiRes === "true");
|
||||
script_interface.settings.set_solarSystemOrbits(wwtSettings.solarSystemOrbits === "true");
|
||||
script_interface.settings.set_solarSystemOverlays(wwtSettings.solarSystemOverlays === "true");
|
||||
script_interface.settings.set_solarSystemPlanets(wwtSettings.solarSystemPlanets === "true");
|
||||
script_interface.settings.set_solarSystemStars(wwtSettings.solarSystemStars === "true");
|
||||
|
||||
script_interface.settings.set_showISSModel(wwtSettings.iss === "true");
|
||||
|
||||
// These only supported in Windows client, not WebGL client
|
||||
//script_interface.settings.set_showClouds(wwtSettings.clouds === "true");
|
||||
//script_interface.settings.set_showElevationModel(wwtSettings.elevationModel === "true");
|
||||
//script_interface.settings.set_showEarthSky(wwtSettings.earthSky === "true");
|
||||
//script_interface.settings.set_earthCutawayView(wwtSettings.earthCutawayView === "true");
|
||||
//script_interface.settings.get_showFieldOfView(wwtSettings.fieldOfView === "true")
|
||||
//script_interface.settings.set_solarSystemCMB(wwtSettings.solarSystemCMB === "true");
|
||||
|
||||
} else {
|
||||
console.log(`Unknown command ${command.command}`);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log(`Erroring processing received message:\n${e}\n${event.data}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Workaround for https://github.com/WorldWideTelescope/wwt-webgl-engine/issues/297
|
||||
// Although circles not drawn when abs(dec)=90
|
||||
function radiusAdjust(radius, dec) {
|
||||
if (Math.abs(dec) >= 89.9999999) {
|
||||
return radius * 0.769 / Math.cos(89.9999999 * Math.PI / 180.0);
|
||||
} else {
|
||||
return radius * 0.769 / Math.cos(dec * Math.PI / 180.0);
|
||||
}
|
||||
}
|
||||
|
||||
function search(name) {
|
||||
const firstChar = name.charAt(0).toLowerCase();
|
||||
const searchData = wwt.searchDataIndexed[firstChar];
|
||||
const nameLower = name.toLowerCase();
|
||||
if (searchData) {
|
||||
for (var i = 0; i < searchData.length; i++) {
|
||||
var names = searchData[i].get_names();
|
||||
for (var j = 0; j < names.length; j++) {
|
||||
if (names[j].toLowerCase() == nameLower) {
|
||||
return searchData[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("No search index: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
function reportReady() {
|
||||
if (socket.readyState === 1) {
|
||||
socket.send(JSON.stringify({
|
||||
event: "ready"
|
||||
}));
|
||||
} else {
|
||||
setTimeout(reportReady, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function reportView() {
|
||||
if (socket.readyState === 1) {
|
||||
socket.send(JSON.stringify({
|
||||
event: "view",
|
||||
ra: script_interface.getRA(), // RA in hours
|
||||
dec: script_interface.getDec(),
|
||||
fov: wwt.renderContext.get_fovAngle(),
|
||||
dateTime: wwtlib.SpaceTimeController.get_now().toISOString(),
|
||||
latitude: script_interface.settings.get_locationLat(),
|
||||
longitude: script_interface.settings.get_locationLng(),
|
||||
}));
|
||||
}
|
||||
if (antennaFoV) {
|
||||
showAntennaFoV();
|
||||
}
|
||||
}
|
||||
|
||||
// 'click' event doesn't work for right button, so use mousedown
|
||||
// Based on code from www-web-client FinderScope.js (MIT license)
|
||||
var div = document.getElementById('wwtcanvas');
|
||||
div.addEventListener('mousedown', function (event) {
|
||||
if (event.button == 2) {
|
||||
var rect = div.getBoundingClientRect();
|
||||
var x = event.clientX - rect.left;
|
||||
var y = event.clientY - rect.top;
|
||||
const obj = wwt.getCoordinatesForScreenPoint(x, y);
|
||||
var scope = wwtlib.Coordinates.raDecTo3d(obj.x, obj.y);
|
||||
|
||||
// findConstellationForPoint can return error if x < 0, but not always
|
||||
// Also this isn't in the original code, but seems to work better
|
||||
var ob = obj.x;
|
||||
if (ob < 0) {
|
||||
ob = ob + 24;
|
||||
}
|
||||
|
||||
var constellation = wwtlib.Constellations.containment.findConstellationForPoint(ob, obj.y);
|
||||
var closestDist, closestPlace;
|
||||
var constellationPlaces, ssPlaces;
|
||||
for (var i = 0; i < wwt.searchData.Constellations.length; i++) {
|
||||
var item = wwt.searchData.Constellations[i];
|
||||
if (item.name === constellation) {
|
||||
constellationPlaces = item.places;
|
||||
} else if (item.name === 'SolarSystem') {
|
||||
ssPlaces = item.places;
|
||||
}
|
||||
}
|
||||
var searchPlaces = ssPlaces.concat(constellationPlaces);
|
||||
for (var i = 0; i < searchPlaces.length; i++) {
|
||||
var place = searchPlaces[i];
|
||||
try {
|
||||
var placeDist = wwtlib.Vector3d.subtractVectors(place.get_location3d(), scope);
|
||||
if ((i === 0) || closestDist.length() > placeDist.length()) {
|
||||
closestPlace = place;
|
||||
closestDist = placeDist;
|
||||
}
|
||||
} catch (er) {
|
||||
if (place && place.get_name() != 'Earth') {
|
||||
console.log(er);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAstroDetails(closestPlace);
|
||||
|
||||
//console.log("Closest place " + JSON.stringify(closestPlace));
|
||||
|
||||
showPlace(event.clientX, event.clientY, closestPlace);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function hideAntennaFoV() {
|
||||
if (antennaFoV) {
|
||||
script_interface.removeAnnotation(antennaFoV);
|
||||
antennaFoV = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function showAntennaFoV() {
|
||||
if (!antennaFoV) {
|
||||
antennaFoV = new wwtlib.Circle();
|
||||
antennaFoV.set_id('antennaFoV');
|
||||
antennaFoV.setCenter(script_interface.getRA() * 15, script_interface.getDec());
|
||||
antennaFoV.set_skyRelative(false);
|
||||
antennaFoV.set_radius(radiusAdjust(antennaHpbw, script_interface.getDec()));
|
||||
antennaFoV.set_lineWidth(3);
|
||||
script_interface.addAnnotation(antennaFoV);
|
||||
} else {
|
||||
antennaFoV.setCenter(script_interface.getRA() * 15, script_interface.getDec());
|
||||
antennaFoV.set_radius(radiusAdjust(antennaHpbw, script_interface.getDec()));
|
||||
}
|
||||
}
|
||||
|
||||
function hidePlace() {
|
||||
var popupDiv = document.getElementById("popupDiv");
|
||||
popupDiv.style.visibility = "hidden";
|
||||
// Remove annotation
|
||||
if (circle) {
|
||||
script_interface.removeAnnotation(circle);
|
||||
}
|
||||
}
|
||||
|
||||
function showPlace(x, y, place) {
|
||||
|
||||
// Set text to display for the place
|
||||
var str = "<p>";
|
||||
|
||||
if (place.get_names()) {
|
||||
str += "Names: " + place.get_names().join(', ');
|
||||
} else {
|
||||
str += "Name: " + place.get_name();
|
||||
}
|
||||
str += "<p>";
|
||||
str += "Classification: " + getClassificationText(place.get_classification());
|
||||
if (!place.isSurvey && (typeof place.get_constellation() !== "undefined")) {
|
||||
if (place.get_constellation() != "SolarSystem") {
|
||||
str += " in " + wwtlib.Constellations.fullNames[place.get_constellation()];
|
||||
}
|
||||
}
|
||||
|
||||
str += "<p>";
|
||||
str += "<img src=" + place.get_thumbnailUrl() + ">";
|
||||
|
||||
str += "<p>";
|
||||
str += "<table>";
|
||||
if (!place.isSurvey) {
|
||||
str += "<tr><td>RA<td>" + formatHms(place.get_RA(), true, false, false, 1);
|
||||
str += "<tr><td>Dec<td>" + formatHms(place.get_dec(), false, true, true);
|
||||
str += "<tr><td>Az<td>" + formatHms(place.altAz.get_az(), false, false, true);
|
||||
str += "<tr><td>Alt<td>" + formatHms(place.altAz.get_alt(), false, false, true);
|
||||
}
|
||||
if (place.get_magnitude() != 0) {
|
||||
str += "<tr><td>Magnitude<td>" + place.get_magnitude().toFixed(2);
|
||||
}
|
||||
if (place.get_distance() != 0) {
|
||||
str += "<tr><td>Distance<td>" + wwtlib.UiTools.formatDistance(place.get_distance());
|
||||
}
|
||||
if (!place.riseSet.bNeverRises) {
|
||||
str += "<tr><td>Rise<td>" + formatDecimalHours(place.riseSet.rise, false, false, true);
|
||||
}
|
||||
if (!place.riseSet.bNeverSets) {
|
||||
str += "<tr><td>Set<td>" + formatDecimalHours(place.riseSet.set, false, false, true);
|
||||
}
|
||||
str += "</table>";
|
||||
|
||||
// Add links to research databases
|
||||
const name = placeNameForQueryString(place);
|
||||
str += "<p>";
|
||||
str += "<a href=\"//wikipedia.org/wiki/Special:Search?search=" + name + "\" target=\"_blank\">Wiki</a>";
|
||||
str += " <a href=\"//ui.adsabs.harvard.edu/search/q=object:%22" + name + "%22\" target=\"_blank\">ADS</a>";
|
||||
str += " <a href=\"//simbad.u-strasbg.fr/simbad/sim-id?Ident=" + name + "\" target=\"_blank\">SIMBAD</a>";
|
||||
str += " <a href=\"//ned.ipac.caltech.edu/cgi-bin/nph-imgdata?objname=" + name + "\" target=\"_blank\">NED</a>";
|
||||
str += " <a href=\"//cdsportal.u-strasbg.fr/gadgets/ifr?url=http://cdsportal.unistra.fr/widgets/SED_plotter.xml&SED_plot_object=" + name + "&SED_plot_radius=5\" target=\"_blank\">VizieR</a>";
|
||||
|
||||
var popup = document.getElementById("myPopup");
|
||||
popup.innerHTML = str;
|
||||
|
||||
// Get location of place on screen, as may not be exactly where clicked
|
||||
const pos = wwt.getScreenPointForCoordinates(place.get_RA(), place.get_dec());
|
||||
|
||||
// Position and show
|
||||
var popupDiv = document.getElementById("popupDiv");
|
||||
popupDiv.style.visibility = "visible";
|
||||
popupDiv.style.left = pos.x + "px";
|
||||
popupDiv.style.top = pos.y + "px";
|
||||
|
||||
// Remove old annotation
|
||||
if (circle) {
|
||||
script_interface.removeAnnotation(circle);
|
||||
}
|
||||
|
||||
// Create circle annotation
|
||||
circle = new wwtlib.Circle();
|
||||
circle.set_id('focused');
|
||||
circle.setCenter(place.get_RA() * 15, place.get_dec());
|
||||
circle.set_skyRelative(false);
|
||||
circle.set_radius(radiusAdjust(.22, place.get_dec()));
|
||||
circle.set_lineWidth(3);
|
||||
script_interface.addAnnotation(circle);
|
||||
}
|
||||
|
||||
function placeNameForQueryString(item) {
|
||||
// Various "research" menus like to put an item's name into URL query
|
||||
// strings. Compute the proper (well, good-enough) representation. When
|
||||
// the name has multiple semicolon-separated identifiers, usually the
|
||||
// first one is a description of a particular image, and the subsequent
|
||||
// ones (if any) are names for the object.
|
||||
|
||||
var name_segments = item.get_name().split(';');
|
||||
var name_to_use = (name_segments.length > 1) ? name_segments[1] : name_segments[0];
|
||||
name_to_use = encodeURIComponent(name_to_use);
|
||||
return name_to_use.replace(/%20/g, '+');
|
||||
}
|
||||
|
||||
// The following code based on code from www-web-client Util.js (MIT license)
|
||||
|
||||
function formatHms(angle, isHmsFormat, signed, spaced, extraPrecision) {
|
||||
var sign = '';
|
||||
|
||||
if (angle < 0) {
|
||||
sign = '-';
|
||||
angle = -angle;
|
||||
} else if (signed) {
|
||||
sign = '+';
|
||||
}
|
||||
|
||||
var seps = [':', ':', ''];
|
||||
|
||||
if (isHmsFormat) {
|
||||
seps = ['h', 'm', 's'];
|
||||
} else if (spaced) {
|
||||
seps = [' : ', ' : ', ''];
|
||||
}
|
||||
|
||||
var values = ['??', '??', '??'];
|
||||
|
||||
if (!isNaN(angle)) {
|
||||
var hourlike = Math.floor(angle);
|
||||
var remainder = (angle - hourlike) * 60;
|
||||
var minutes = Math.floor(remainder);
|
||||
var seconds = (remainder - minutes) * 60;
|
||||
|
||||
if (isNaN(extraPrecision)) {
|
||||
extraPrecision = 0;
|
||||
}
|
||||
var secondsStr = seconds.toFixed(extraPrecision);
|
||||
|
||||
if (secondsStr.startsWith('60')) {
|
||||
seconds = 0;
|
||||
secondsStr = seconds.toFixed(extraPrecision);
|
||||
minutes += 1;
|
||||
|
||||
if (minutes == 60) {
|
||||
minutes = 0;
|
||||
hourlike += 1;
|
||||
}
|
||||
}
|
||||
|
||||
values[0] = hourlike.toFixed(0);
|
||||
if (hourlike < 10) {
|
||||
values[0] = '0' + values[0];
|
||||
}
|
||||
|
||||
values[1] = minutes.toFixed(0);
|
||||
if (minutes < 10) {
|
||||
values[1] = '0' + values[1];
|
||||
}
|
||||
|
||||
values[2] = secondsStr;
|
||||
if (seconds < 10) {
|
||||
values[2] = '0' + values[2];
|
||||
}
|
||||
}
|
||||
|
||||
return sign.concat(values[0], seps[0], values[1], seps[1], values[2], seps[2]);
|
||||
};
|
||||
|
||||
function formatDecimalHours(dayFraction, spaced) {
|
||||
var ts = new Date(new Date().toUTCString()).valueOf() - new Date().valueOf();
|
||||
var hr = ts / (1000 * 60 * 60);
|
||||
var day = (dayFraction - hr) + 0.0083333334;
|
||||
while (day > 24) {
|
||||
day -= 24;
|
||||
}
|
||||
while (day < 0) {
|
||||
day += 24;
|
||||
}
|
||||
var hours = day.toFixed(0);
|
||||
var minutes = ((day * 60) - (hours * 60)).toFixed(0);
|
||||
|
||||
var join = spaced ? ' : ' : ':';
|
||||
//var seconds = ((day * 3600) - (((hours * 3600) + ((double)minutes * 60.0)));
|
||||
|
||||
return ([int2(hours), int2(minutes)]).join(join);
|
||||
}
|
||||
|
||||
function int2(dec) {
|
||||
var sign = dec < 0 ? '-' : '';
|
||||
var int = Math.floor(Math.abs(dec));
|
||||
var pad = int < 10 ? '0' : '';
|
||||
return sign + pad + int;
|
||||
}
|
||||
|
||||
function getClassificationText(clsid) {
|
||||
if (clsid && !isNaN(parseInt(clsid))) {
|
||||
var str;
|
||||
for (const [k, v] of Object.entries(wwtlib.Classification)) {
|
||||
if (v === clsid) {
|
||||
str = k;
|
||||
}
|
||||
}
|
||||
var out = str.replace(/^\s*/, ""); // strip leading spaces
|
||||
out = out.replace(/^[a-z]|[^\s][A-Z]/g, function (str, offset) {
|
||||
if (offset == 0) {
|
||||
return (str.toUpperCase());
|
||||
} else {
|
||||
return (str.substr(0, 1) + " " + str.substr(1).toUpperCase());
|
||||
}
|
||||
});
|
||||
return (out);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
function getAstroDetails(place) {
|
||||
var coords = wwtlib.Coordinates.fromRaDec(place.get_RA(), place.get_dec());
|
||||
var stc = wwtlib.SpaceTimeController;
|
||||
var altAz = wwtlib.Coordinates.equitorialToHorizon(coords, stc.get_location(), stc.get_now());
|
||||
place.altAz = altAz;
|
||||
var classificationText = getClassificationText(place.get_classification());
|
||||
var riseSet;
|
||||
if (classificationText == 'Solar System') {
|
||||
var jNow = stc.get_jNow() + .5;
|
||||
var p1 = wwtlib.Planets.getPlanetLocation(place.get_name(), jNow - 1);
|
||||
var p2 = wwtlib.Planets.getPlanetLocation(place.get_name(), jNow);
|
||||
var p3 = wwtlib.Planets.getPlanetLocation(place.get_name(), jNow + 1);
|
||||
|
||||
var type = 0;
|
||||
switch (place.get_name()) {
|
||||
case "Sun":
|
||||
type = 1;
|
||||
break;
|
||||
case "Moon":
|
||||
type = 2;
|
||||
break;
|
||||
default:
|
||||
type = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
riseSet = wwtlib.AstroCalc.getRiseTrinsitSet(
|
||||
jNow,
|
||||
stc.get_location().get_lat(),
|
||||
-stc.get_location().get_lng(),
|
||||
p1.RA, p1.dec,
|
||||
p2.RA, p2.dec,
|
||||
p3.RA, p3.dec,
|
||||
type
|
||||
);
|
||||
} else {
|
||||
riseSet = wwtlib.AstroCalc.getRiseTrinsitSet(
|
||||
stc.get_jNow() + .5,
|
||||
stc.get_location().get_lat(),
|
||||
-stc.get_location().get_lng(),
|
||||
place.get_RA(), place.get_dec(),
|
||||
place.get_RA(), place.get_dec(),
|
||||
place.get_RA(), place.get_dec(),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
if (!riseSet.bValid && !riseSet.bNeverRises) {
|
||||
riseSet.bNeverSets = true;
|
||||
}
|
||||
|
||||
place.riseSet = riseSet;
|
||||
}
|
||||
|
||||
|
||||
// The following code based on code from www-web-client SearchUtil.js (MIT license)
|
||||
|
||||
// searchData gets inserted here
|
||||
var wwt = {};
|
||||
var searchIndex = {};
|
||||
|
||||
var imageset_id = 100;
|
||||
|
||||
function initSearch() {
|
||||
// The special `wwt.searchData` global is assigned in the JS file that
|
||||
// the main webclient app loads asynchronously (see `app.js`).
|
||||
if (!wwt.searchData) {
|
||||
setTimeout(initSearch, 333);
|
||||
} else {
|
||||
// searchDataIndexed is used in `factories/SearchUtil.js`.
|
||||
wwt.searchDataIndexed = [];
|
||||
|
||||
data = wwt.searchData;
|
||||
var start = new Date();
|
||||
|
||||
for (var i = 0; i < data.Constellations.length; i++) {
|
||||
var item = data.Constellations[i];
|
||||
//constellations[i] = item.name;
|
||||
for (var j = 0; j < item.places.length; j++) {
|
||||
var place = item.places[j];
|
||||
var fgi = place.fgi,
|
||||
imgSet;
|
||||
|
||||
if (fgi) {
|
||||
imageset_id++;
|
||||
|
||||
var band_pass = (fgi.bp !== undefined) ? fgi.bp : wwtlib.BandPass.visible;
|
||||
var projection = (fgi.pr !== undefined) ? fgi.pr : wwtlib.ProjectionType.tan;
|
||||
var base_tile_level = (fgi.bl !== undefined) ? fgi.bl : 0;
|
||||
var file_type = (fgi.ft !== undefined) ? fgi.ft : ".png";
|
||||
var tile_levels = (fgi.lv !== undefined) ? fgi.lv : 4;
|
||||
var bottoms_up = (fgi.bu !== undefined) ? fgi.bu : false;
|
||||
var quad_tree_map = (fgi.q !== undefined) ? fgi.q : "";
|
||||
var offset_x = (fgi.oX !== undefined) ? fgi.oX : 0;
|
||||
var offset_y = (fgi.oY !== undefined) ? fgi.oY : 0;
|
||||
var default_set = (fgi.ds !== undefined) ? fgi.ds : false; // "StockSet" in XML
|
||||
var rotation = (fgi.r !== undefined) ? fgi.r : 0;
|
||||
var width_factor = (fgi.wf !== undefined) ? fgi.wf : 2;
|
||||
imgSet = wwtlib.Imageset.create(
|
||||
fgi.n, // name
|
||||
fgi.u, // url
|
||||
wwtlib.ImageSetType.sky, // data_set_type -- never changes (for now?)
|
||||
band_pass,
|
||||
projection,
|
||||
imageset_id, // imageset id
|
||||
base_tile_level,
|
||||
tile_levels,
|
||||
null, // tile_size
|
||||
fgi.bd, // baseTileDegrees
|
||||
file_type,
|
||||
bottoms_up,
|
||||
quad_tree_map,
|
||||
fgi.cX, // centerX
|
||||
fgi.cY, // centerY
|
||||
rotation,
|
||||
true, // sparse
|
||||
fgi.tu, // thumbnailUrl,
|
||||
default_set,
|
||||
false, // elevationModel
|
||||
width_factor,
|
||||
offset_x,
|
||||
offset_y,
|
||||
fgi.ct, // creditsText
|
||||
fgi.cu, // creditsUrl
|
||||
'', // demUrl
|
||||
'', // altUrl
|
||||
0, // meanRadius
|
||||
null // referenceFrame
|
||||
);
|
||||
|
||||
rewritePlaceUrls(imgSet);
|
||||
}
|
||||
|
||||
var classification = (place.c !== undefined) ? place.c : wwtlib.Classification.unidentified;
|
||||
var zoom_factor = (place.z !== undefined) ? place.z : -1;
|
||||
|
||||
//console.log("Creating place " + place.n + " of type " + classification + " in " + item.name);
|
||||
var pl = wwtlib.Place.create(
|
||||
place.n, // name
|
||||
place.d, // dec
|
||||
place.r, // ra
|
||||
classification,
|
||||
item.name, // constellation
|
||||
wwtlib.ImageSetType.sky, // type -- never changes (for now?)
|
||||
zoom_factor,
|
||||
);
|
||||
|
||||
if (imgSet) {
|
||||
pl.set_studyImageset(imgSet);
|
||||
}
|
||||
|
||||
if (item.name === 'SolarSystem') {
|
||||
for (const [k, member] of Object.entries(pl)) {
|
||||
if (wwtlib.ss.canCast(member, wwtlib.CameraParameters)) {
|
||||
member.target = wwtlib.SolarSystemObjects.undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pl.guid = i + "." + j;
|
||||
rewritePlaceUrls(pl);
|
||||
item.places[j] = pl;
|
||||
indexPlaceNames(pl);
|
||||
}
|
||||
}
|
||||
|
||||
var end = new Date();
|
||||
//console.log('parsed places in ' + (end.valueOf() - start.valueOf()) + 'ms', data);
|
||||
}
|
||||
};
|
||||
|
||||
function addPlace(s, place) {
|
||||
var firstChar = s.charAt(0).toLowerCase();
|
||||
|
||||
if (firstChar === "'")
|
||||
firstChar = s.charAt(1).toLowerCase();
|
||||
|
||||
if (searchIndex[firstChar]) {
|
||||
if (searchIndex[firstChar][searchIndex[firstChar].length - 1] !== place) {
|
||||
searchIndex[firstChar].push(place);
|
||||
wwt.searchDataIndexed = searchIndex;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
wwt.searchDataIndexed[firstChar] = searchIndex[firstChar] = [place];
|
||||
} catch (er) {
|
||||
console.error(er);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function indexPlaceNames(pl) {
|
||||
|
||||
var names = pl.get_names();
|
||||
for (var n = 0; n < names.length; n++) {
|
||||
var name = names[n];
|
||||
if (name.indexOf(' ') !== -1) {
|
||||
var words = name.split(' ');
|
||||
for (var w = 0; w < words.length; w++) {
|
||||
var word = words[w];
|
||||
addPlace(word, pl);
|
||||
}
|
||||
} else if (name.charAt(0)) {
|
||||
addPlace(name, pl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function rewritePlaceUrls(item) {
|
||||
if (item.get_thumbnailUrl) {
|
||||
var u = item.get_thumbnailUrl();
|
||||
if (u)
|
||||
item.thumb = wwtlib.URLHelpers.singleton.rewrite(u, wwtlib.URLRewriteMode.asIfAbsolute);
|
||||
}
|
||||
|
||||
if (item.get_url) {
|
||||
var u = item.get_url();
|
||||
if (u)
|
||||
item.url = wwtlib.URLHelpers.singleton.rewrite(u, wwtlib.URLRewriteMode.asIfAbsolute);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/skymap/">
|
||||
<file>icons/constellation.png</file>
|
||||
<file>icons/reticle.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,141 @@
|
|||
<h1>Sky Map Feature Plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
The Sky Map Feature provides visualization of the sky in multiple wavelengths (radio, IR, optical, UV, X-Ray, gamma).
|
||||
Multiple Sky Maps are supported, including the [World Wide Telescope](https://www.worldwidetelescope.org/), [ESASky](https://www.esa.int/About_Us/ESAC/Explore_the_cosmos_with_ESASky) and [Aladin Lite](https://aladin.cds.unistra.fr/).
|
||||
|
||||
The Rotator Controller and Star Tracker plugins can track the position viewed in the Sky Map. The Sky Map can also be set to track coordinates from the Star Tracker, Satellite Tracker, Rotator Controller or Map.
|
||||
|
||||
![Sky Map feature WWT](../../../doc/img/SkyMap_wwt.png)
|
||||
|
||||
With the ESASky map, a host of astronomical data is available from JWST, Hubble, Gaia, Herschel and others, including images, spectra and publications:
|
||||
|
||||
![Sky Map feature ESASky](../../../doc/img/SkyMap_ESASky.png)
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![Sky Map feature plugin GUI](../../../doc/img/SkyMap_settings.png)
|
||||
|
||||
<h3>1: Find</h3>
|
||||
|
||||
Enter an astronomical object name and press enter to centre the sky map on that object.
|
||||
|
||||
You can also enter J2000 RA and Dec coordinates, in either HMS/DMS or decimal:
|
||||
|
||||
* 12 05 12.23 +17 06 21.0
|
||||
* 12 05 12 17 06 21
|
||||
* 12:05:12.23 -17:06:21.20
|
||||
* 12h05m12.23s +17d06m21.10s
|
||||
* 107.1324 -34.233
|
||||
|
||||
Note that the supported object names depend on the chosen map (2).
|
||||
|
||||
<h3>2: Map Type</h3>
|
||||
|
||||
Allows you to select the sky map:
|
||||
|
||||
* WWT - World Wide Telescope
|
||||
* ESASky
|
||||
* Aladin
|
||||
|
||||
Each map provides different features, image and data sets.
|
||||
|
||||
<h3>3: Background Image Set</h3>
|
||||
|
||||
For WWT and Aladin, this sets the background image set the sky map will display. For ESASky, this must be set within the ESASky GUI (using the Manage Skies button), as this can use multiple image sets.
|
||||
|
||||
Image sets are available for a variety of wavelengths (radio, IR, optical, UV, X-Ray, gamma) from a number of different surveys.
|
||||
|
||||
<h3>4: Projection</h3>
|
||||
|
||||
For World Wide Telescope:
|
||||
|
||||
* Sky - Views the sky from a position on Earth,
|
||||
* Solar System - Views the Solar System, with the ability to show orbits,
|
||||
* Planet - Views one of the planets or major moons.
|
||||
|
||||
For Aladin:
|
||||
|
||||
* SIN - [orthographic](https://en.wikipedia.org/wiki/Orthographic_map_projection),
|
||||
* AIT - [Hammer-Aitoff](https://en.wikipedia.org/wiki/Hammer_projection),
|
||||
* MOL - [Mollweide](https://en.wikipedia.org/wiki/Mollweide_projection),
|
||||
* MER - [Mercator](https://en.wikipedia.org/wiki/Mercator_projection),
|
||||
* ARC - zenithal/azimuthal equidistant,
|
||||
* TAN - [gnomonic](https://en.wikipedia.org/wiki/Gnomonic_projection),
|
||||
* HPX - [HEALPix](https://en.wikipedia.org/wiki/HEALPix).
|
||||
|
||||
This option is not available for ESASky, which is currently fixed to orthographic.
|
||||
|
||||
See [List of map projections](https://en.wikipedia.org/wiki/List_of_map_projections).
|
||||
|
||||
<h3>5: Display Names</h3>
|
||||
|
||||
For WWT, when checked, displays names of constellations and Ecliptic text (when the constellations and Ecliptic are visible).
|
||||
|
||||
<h3>6: Display Constellations</h3>
|
||||
|
||||
For WWT, this option enables the display of constellations. How the constellations are drawn can be customised in the Display Settings dialog (12).
|
||||
|
||||
<h3>7: Display gird</h3>
|
||||
|
||||
When checked, displays a coordinate grid.
|
||||
|
||||
For WWT, Ecliptic, Alt/Az, Equatorial and Galactic grids can be displayed. These can be selected in the Display Settings dialog (12).
|
||||
|
||||
For ESASky and Aladin, the grid will be Equatorial or Galactic, depending on the coordinate mode selected.
|
||||
|
||||
<h3>8: Display reticle</h3>
|
||||
|
||||
When checked, displays a reticle (cross hair) at the centre of the view. Coordinates for the reticle are displayed in the status bar in the bottom of the window.
|
||||
|
||||
<h3>9: Display antenna field-of-view</h3>
|
||||
|
||||
When checked, displays the antenna field-of-view. The antenna beamwidth can be set in the Display Settings dialog (12) or from a Star Tracker source (11).
|
||||
|
||||
<h3>10: Track</h3>
|
||||
|
||||
When checked, the centre of view will track the coordinates received from the selected Source plugin (11).
|
||||
|
||||
<h3>11: Source</h3>
|
||||
|
||||
Select a Star Tracker, Rotator Controller, Satellite Tracker or Map plugin to read viewing coordinates from.
|
||||
|
||||
* When a Star Tracker is selected, target RA/Dec, observation position, antenna beamwith and date/time will be read from the selected plugin.
|
||||
* For other plugin types, Alt/El will be read and other parameters are taken from the the Display Settings dialog (12).
|
||||
|
||||
<h3>12: Display settings</h3>
|
||||
|
||||
When clicked, opens the Display Settings dialog. The Display Settings dialog allows the user to set:
|
||||
|
||||
* Observation location (latitude and longitude in degreees).
|
||||
* Antenna beamwidth in degrees.
|
||||
* Settings for WWT, such as how the constellations are drawn, what grids are displayed and how the Solar System view appears.
|
||||
|
||||
<h2>Sky Map Controls</h2>
|
||||
|
||||
* Click and drag using the left mouse button to rotate through right ascension or declination.
|
||||
* Use the mouse wheel to zoom in and out.
|
||||
* In WWT, right click to display information about the nearest astronomical object. The object will be circled and a popup information window will appear with links to relevant astronomical databases (ADS, SIMBAD, NED, VizieR).
|
||||
The popup window can be moved by left clicking and dragging it.
|
||||
* In ESASky, right click to display a popup window, with links to various astronomical databases (SIMBAD, NED, VizieR) for the corresponding coordinates.
|
||||
* In Aladin, right click to display a popup menu. Use the "What is this?" menu to display a window with a link to the CDS astronomical database.
|
||||
|
||||
ESASky and Aladin are able to overlay catalog data:
|
||||
|
||||
* In ESASky, zoom in to the area of interest, then press the "Explore catalog data for this region" button. A window will appear showing available catalogs. Left click and boxes will be overlaid on the map for each catalog entry. The data is also displayed in tabular form. You can left click on a box to view its data in the table.
|
||||
* In Aladin, zoom in to the areae of interest, then press the "Manage layers" button. In the popup window, select one or more of the available catalogs, or press Add catalogue to add a new one. Boxes will be overlaid on the map for each catalog entry. Left click on a box to display the data for it in tabular form.
|
||||
|
||||
<h2>Attribution</h2>
|
||||
|
||||
Constellation icons created by Freepik - https://www.flaticon.com
|
||||
|
||||
<h2>API</h2>
|
||||
|
||||
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to centre the sky map on an object from the command line:
|
||||
|
||||
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/feature/0/actions" -d '{"featureType": "SkyMap", "SkyMapActions": { "find": "polaris" }}'
|
||||
|
||||
And to centre the sky map at a particular RA and dec (Not for WWT):
|
||||
|
||||
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/feature/0/actions" -d '{"featureType": "SkyMap", "SkyMapActions": { "find": "18 36 56 +38 47 01" }}'
|
|
@ -0,0 +1,510 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
|
||||
// //
|
||||
// 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 <QTimer>
|
||||
|
||||
#include "SWGFeatureSettings.h"
|
||||
#include "SWGFeatureReport.h"
|
||||
#include "SWGFeatureActions.h"
|
||||
#include "SWGDeviceState.h"
|
||||
|
||||
#include "dsp/dspengine.h"
|
||||
|
||||
#include "device/deviceset.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "feature/featureset.h"
|
||||
#include "settings/serializable.h"
|
||||
#include "maincore.h"
|
||||
#include "skymap.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(SkyMap::MsgConfigureSkyMap, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SkyMap::MsgFind, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SkyMap::MsgSetDateTime, Message)
|
||||
MESSAGE_CLASS_DEFINITION(SkyMap::MsgReportViewDetails, Message)
|
||||
|
||||
const char* const SkyMap::m_featureIdURI = "sdrangel.feature.skymap";
|
||||
const char* const SkyMap::m_featureId = "SkyMap";
|
||||
|
||||
SkyMap::SkyMap(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface),
|
||||
m_multiplier(0.0)
|
||||
{
|
||||
qDebug("SkyMap::SkyMap: webAPIAdapterInterface: %p", webAPIAdapterInterface);
|
||||
setObjectName(m_featureId);
|
||||
m_state = StIdle;
|
||||
m_errorMessage = "SkyMap error";
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
QObject::connect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&SkyMap::networkManagerFinished
|
||||
);
|
||||
}
|
||||
|
||||
SkyMap::~SkyMap()
|
||||
{
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&SkyMap::networkManagerFinished
|
||||
);
|
||||
delete m_networkManager;
|
||||
}
|
||||
|
||||
bool SkyMap::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigureSkyMap::match(cmd))
|
||||
{
|
||||
MsgConfigureSkyMap& cfg = (MsgConfigureSkyMap&) cmd;
|
||||
qDebug() << "SkyMap::handleMessage: MsgConfigureSkyMap";
|
||||
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgReportViewDetails::match(cmd))
|
||||
{
|
||||
MsgReportViewDetails& report = (MsgReportViewDetails&) cmd;
|
||||
m_viewDetails = report.getViewDetails();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray SkyMap::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool SkyMap::deserialize(const QByteArray& data)
|
||||
{
|
||||
if (m_settings.deserialize(data))
|
||||
{
|
||||
MsgConfigureSkyMap *msg = MsgConfigureSkyMap::create(m_settings, QList<QString>(), true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
MsgConfigureSkyMap *msg = MsgConfigureSkyMap::create(m_settings, QList<QString>(), true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SkyMap::applySettings(const SkyMapSettings& settings, const QList<QString>& settingsKeys, bool force)
|
||||
{
|
||||
qDebug() << "SkyMap::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
|
||||
|
||||
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 SkyMap::webapiRun(bool run,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) run;
|
||||
(void) errorMessage;
|
||||
getFeatureStateStr(*response.getState());
|
||||
return 202;
|
||||
}
|
||||
|
||||
int SkyMap::webapiSettingsGet(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setSkyMapSettings(new SWGSDRangel::SWGSkyMapSettings());
|
||||
response.getSkyMapSettings()->init();
|
||||
webapiFormatFeatureSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int SkyMap::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
SkyMapSettings settings = m_settings;
|
||||
webapiUpdateFeatureSettings(settings, featureSettingsKeys, response);
|
||||
|
||||
MsgConfigureSkyMap *msg = MsgConfigureSkyMap::create(settings, featureSettingsKeys, force);
|
||||
m_inputMessageQueue.push(msg);
|
||||
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgConfigureSkyMap *msgToGUI = MsgConfigureSkyMap::create(settings, featureSettingsKeys, force);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
webapiFormatFeatureSettings(response, settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
int SkyMap::webapiReportGet(
|
||||
SWGSDRangel::SWGFeatureReport& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setSkyMapReport(new SWGSDRangel::SWGSkyMapReport());
|
||||
response.getSkyMapReport()->init();
|
||||
webapiFormatFeatureReport(response);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int SkyMap::webapiActionsPost(
|
||||
const QStringList& featureActionsKeys,
|
||||
SWGSDRangel::SWGFeatureActions& query,
|
||||
QString& errorMessage)
|
||||
{
|
||||
SWGSDRangel::SWGSkyMapActions *swgSkyMapActions = query.getSkyMapActions();
|
||||
|
||||
if (swgSkyMapActions)
|
||||
{
|
||||
if (featureActionsKeys.contains("find"))
|
||||
{
|
||||
QString id = *swgSkyMapActions->getFind();
|
||||
|
||||
if (getMessageQueueToGUI()) {
|
||||
getMessageQueueToGUI()->push(MsgFind::create(id));
|
||||
}
|
||||
}
|
||||
/*if (featureActionsKeys.contains("setDateTime"))
|
||||
{
|
||||
QString dateTimeString = *swgSkyMapActions->getSetDateTime();
|
||||
QDateTime dateTime = QDateTime::fromString(dateTimeString, Qt::ISODateWithMs);
|
||||
|
||||
if (getMessageQueueToGUI()) {
|
||||
getMessageQueueToGUI()->push(MsgSetDateTime::create(dateTime));
|
||||
}
|
||||
}*/
|
||||
return 202;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "Missing SkyMapActions in query";
|
||||
return 400;
|
||||
}
|
||||
return 400;
|
||||
}
|
||||
|
||||
void SkyMap::webapiFormatFeatureSettings(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
const SkyMapSettings& settings)
|
||||
{
|
||||
response.getSkyMapSettings()->setDisplayNames(settings.m_displayNames ? 1 : 0);
|
||||
response.getSkyMapSettings()->setDisplayConstellations(settings.m_displayConstellations ? 1 : 0);
|
||||
response.getSkyMapSettings()->setDisplayReticle(settings.m_displayReticle ? 1 : 0);
|
||||
response.getSkyMapSettings()->setDisplayGrid(settings.m_displayGrid ? 1 : 0);
|
||||
response.getSkyMapSettings()->setDisplayAntennaFoV(settings.m_displayAntennaFoV ? 1 : 0);
|
||||
response.getSkyMapSettings()->setMap(new QString(settings.m_map));
|
||||
response.getSkyMapSettings()->setBackground(new QString(settings.m_background));
|
||||
response.getSkyMapSettings()->setProjection(new QString(settings.m_projection));
|
||||
response.getSkyMapSettings()->setSource(new QString(settings.m_source));
|
||||
response.getSkyMapSettings()->setTrack(settings.m_track ? 1 : 0);
|
||||
response.getSkyMapSettings()->setLatitude(settings.m_latitude);
|
||||
response.getSkyMapSettings()->setLongitude(settings.m_longitude);
|
||||
response.getSkyMapSettings()->setAltitude(settings.m_altitude);
|
||||
response.getSkyMapSettings()->setHpbw(settings.m_hpbw);
|
||||
response.getSkyMapSettings()->setUseMyPosition(settings.m_useMyPosition);
|
||||
|
||||
if (response.getSkyMapSettings()->getTitle()) {
|
||||
*response.getSkyMapSettings()->getTitle() = settings.m_title;
|
||||
} else {
|
||||
response.getSkyMapSettings()->setTitle(new QString(settings.m_title));
|
||||
}
|
||||
|
||||
response.getSkyMapSettings()->setRgbColor(settings.m_rgbColor);
|
||||
response.getSkyMapSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getSkyMapSettings()->getReverseApiAddress()) {
|
||||
*response.getSkyMapSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getSkyMapSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getSkyMapSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getSkyMapSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex);
|
||||
response.getSkyMapSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex);
|
||||
|
||||
if (settings.m_rollupState)
|
||||
{
|
||||
if (response.getSkyMapSettings()->getRollupState())
|
||||
{
|
||||
settings.m_rollupState->formatTo(response.getSkyMapSettings()->getRollupState());
|
||||
}
|
||||
else
|
||||
{
|
||||
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
|
||||
settings.m_rollupState->formatTo(swgRollupState);
|
||||
response.getSkyMapSettings()->setRollupState(swgRollupState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkyMap::webapiUpdateFeatureSettings(
|
||||
SkyMapSettings& settings,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response)
|
||||
{
|
||||
if (featureSettingsKeys.contains("displayNames")) {
|
||||
settings.m_displayNames = response.getSkyMapSettings()->getDisplayNames();
|
||||
}
|
||||
if (featureSettingsKeys.contains("displayConstellations")) {
|
||||
settings.m_displayConstellations = response.getSkyMapSettings()->getDisplayConstellations();
|
||||
}
|
||||
if (featureSettingsKeys.contains("displayReticle")) {
|
||||
settings.m_displayReticle = response.getSkyMapSettings()->getDisplayReticle();
|
||||
}
|
||||
if (featureSettingsKeys.contains("displayGrid")) {
|
||||
settings.m_displayGrid = response.getSkyMapSettings()->getDisplayGrid();
|
||||
}
|
||||
if (featureSettingsKeys.contains("displayAntennaFoV")) {
|
||||
settings.m_displayAntennaFoV = response.getSkyMapSettings()->getDisplayAntennaFoV();
|
||||
}
|
||||
if (featureSettingsKeys.contains("map")) {
|
||||
settings.m_map = *response.getSkyMapSettings()->getMap();
|
||||
}
|
||||
if (featureSettingsKeys.contains("background")) {
|
||||
settings.m_background = *response.getSkyMapSettings()->getBackground();
|
||||
}
|
||||
if (featureSettingsKeys.contains("projection")) {
|
||||
settings.m_projection = *response.getSkyMapSettings()->getProjection();
|
||||
}
|
||||
if (featureSettingsKeys.contains("source")) {
|
||||
settings.m_source = *response.getSkyMapSettings()->getSource();
|
||||
}
|
||||
if (featureSettingsKeys.contains("track")) {
|
||||
settings.m_track = response.getSkyMapSettings()->getTrack();
|
||||
}
|
||||
if (featureSettingsKeys.contains("latitude")) {
|
||||
settings.m_latitude = response.getSkyMapSettings()->getLatitude();
|
||||
}
|
||||
if (featureSettingsKeys.contains("longitude")) {
|
||||
settings.m_longitude = response.getSkyMapSettings()->getLongitude();
|
||||
}
|
||||
if (featureSettingsKeys.contains("altitude")) {
|
||||
settings.m_altitude = response.getSkyMapSettings()->getAltitude();
|
||||
}
|
||||
if (featureSettingsKeys.contains("hpbw")) {
|
||||
settings.m_hpbw = response.getSkyMapSettings()->getHpbw();
|
||||
}
|
||||
if (featureSettingsKeys.contains("useMyPosition")) {
|
||||
settings.m_useMyPosition = response.getSkyMapSettings()->getUseMyPosition();
|
||||
}
|
||||
if (featureSettingsKeys.contains("title")) {
|
||||
settings.m_title = *response.getSkyMapSettings()->getTitle();
|
||||
}
|
||||
if (featureSettingsKeys.contains("rgbColor")) {
|
||||
settings.m_rgbColor = response.getSkyMapSettings()->getRgbColor();
|
||||
}
|
||||
if (featureSettingsKeys.contains("useReverseAPI")) {
|
||||
settings.m_useReverseAPI = response.getSkyMapSettings()->getUseReverseApi() != 0;
|
||||
}
|
||||
if (featureSettingsKeys.contains("reverseAPIAddress")) {
|
||||
settings.m_reverseAPIAddress = *response.getSkyMapSettings()->getReverseApiAddress();
|
||||
}
|
||||
if (featureSettingsKeys.contains("reverseAPIPort")) {
|
||||
settings.m_reverseAPIPort = response.getSkyMapSettings()->getReverseApiPort();
|
||||
}
|
||||
if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) {
|
||||
settings.m_reverseAPIFeatureSetIndex = response.getSkyMapSettings()->getReverseApiFeatureSetIndex();
|
||||
}
|
||||
if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) {
|
||||
settings.m_reverseAPIFeatureIndex = response.getSkyMapSettings()->getReverseApiFeatureIndex();
|
||||
}
|
||||
if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) {
|
||||
settings.m_rollupState->updateFrom(featureSettingsKeys, response.getSkyMapSettings()->getRollupState());
|
||||
}
|
||||
}
|
||||
|
||||
void SkyMap::webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const SkyMapSettings& settings, bool force)
|
||||
{
|
||||
SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings();
|
||||
// swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet());
|
||||
// swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex());
|
||||
swgFeatureSettings->setFeatureType(new QString("SkyMap"));
|
||||
swgFeatureSettings->setSkyMapSettings(new SWGSDRangel::SWGSkyMapSettings());
|
||||
SWGSDRangel::SWGSkyMapSettings *swgSkyMapSettings = swgFeatureSettings->getSkyMapSettings();
|
||||
|
||||
// transfer data that has been modified. When force is on transfer all data except reverse API data
|
||||
|
||||
if (featureSettingsKeys.contains("displayNames") || force) {
|
||||
swgSkyMapSettings->setDisplayNames(settings.m_displayNames);
|
||||
}
|
||||
if (featureSettingsKeys.contains("displayConstellations") || force) {
|
||||
swgSkyMapSettings->setDisplayConstellations(settings.m_displayConstellations);
|
||||
}
|
||||
if (featureSettingsKeys.contains("displayReticle") || force) {
|
||||
swgSkyMapSettings->setDisplayReticle(settings.m_displayReticle);
|
||||
}
|
||||
if (featureSettingsKeys.contains("displayGrid") || force) {
|
||||
swgSkyMapSettings->setDisplayGrid(settings.m_displayGrid);
|
||||
}
|
||||
if (featureSettingsKeys.contains("displayAntennaFoV") || force) {
|
||||
swgSkyMapSettings->setDisplayAntennaFoV(settings.m_displayAntennaFoV);
|
||||
}
|
||||
if (featureSettingsKeys.contains("map") || force) {
|
||||
swgSkyMapSettings->setMap(new QString(settings.m_map));
|
||||
}
|
||||
if (featureSettingsKeys.contains("background") || force) {
|
||||
swgSkyMapSettings->setBackground(new QString(settings.m_background));
|
||||
}
|
||||
if (featureSettingsKeys.contains("projection") || force) {
|
||||
swgSkyMapSettings->setProjection(new QString(settings.m_projection));
|
||||
}
|
||||
if (featureSettingsKeys.contains("source") || force) {
|
||||
swgSkyMapSettings->setSource(new QString(settings.m_source));
|
||||
}
|
||||
if (featureSettingsKeys.contains("track") || force) {
|
||||
swgSkyMapSettings->setTrack(settings.m_track);
|
||||
}
|
||||
if (featureSettingsKeys.contains("latitude") || force) {
|
||||
swgSkyMapSettings->setLatitude(settings.m_latitude);
|
||||
}
|
||||
if (featureSettingsKeys.contains("longitude") || force) {
|
||||
swgSkyMapSettings->setLongitude(settings.m_longitude);
|
||||
}
|
||||
if (featureSettingsKeys.contains("altitude") || force) {
|
||||
swgSkyMapSettings->setAltitude(settings.m_altitude);
|
||||
}
|
||||
if (featureSettingsKeys.contains("hpbw") || force) {
|
||||
swgSkyMapSettings->setHpbw(settings.m_hpbw);
|
||||
}
|
||||
if (featureSettingsKeys.contains("useMyPosition") || force) {
|
||||
swgSkyMapSettings->setTrack(settings.m_useMyPosition);
|
||||
}
|
||||
if (featureSettingsKeys.contains("title") || force) {
|
||||
swgSkyMapSettings->setTitle(new QString(settings.m_title));
|
||||
}
|
||||
if (featureSettingsKeys.contains("rgbColor") || force) {
|
||||
swgSkyMapSettings->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 SkyMap::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response)
|
||||
{
|
||||
QString skymapDateTime = getSkyMapDateTime().toString(Qt::ISODateWithMs);
|
||||
if (response.getSkyMapReport()->getDateTime()) {
|
||||
//*response.getSkyMapReport()->getDateTime() = skymapDateTime;
|
||||
*response.getSkyMapReport()->getDateTime() = m_viewDetails.m_dateTime.toString(Qt::ISODateWithMs);
|
||||
} else {
|
||||
//response.getSkyMapReport()->setDateTime(new QString(skymapDateTime));
|
||||
response.getSkyMapReport()->setDateTime(new QString(m_viewDetails.m_dateTime.toString(Qt::ISODateWithMs)));
|
||||
}
|
||||
response.getSkyMapReport()->setRa(m_viewDetails.m_ra);
|
||||
response.getSkyMapReport()->setDec(m_viewDetails.m_dec);
|
||||
response.getSkyMapReport()->setAzimuth(m_viewDetails.m_azimuth);
|
||||
response.getSkyMapReport()->setElevation(m_viewDetails.m_elevation);
|
||||
response.getSkyMapReport()->setFov(m_viewDetails.m_fov);
|
||||
response.getSkyMapReport()->setLatitude(m_viewDetails.m_latitude);
|
||||
response.getSkyMapReport()->setLongitude(m_viewDetails.m_longitude);
|
||||
}
|
||||
|
||||
void SkyMap::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "SkyMap::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("SkyMap::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void SkyMap::setSkyMapDateTime(QDateTime skymapDateTime, QDateTime systemDateTime, double multiplier)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_dateTimeMutex);
|
||||
m_skymapDateTime = skymapDateTime;
|
||||
m_systemDateTime = systemDateTime;
|
||||
m_multiplier = multiplier;
|
||||
}
|
||||
|
||||
QDateTime SkyMap::getSkyMapDateTime()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_dateTimeMutex);
|
||||
if (m_multiplier == 0.0)
|
||||
{
|
||||
return m_skymapDateTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's not possible to synchronously get the time from Cesium
|
||||
// so we calculate it based on the system clock difference from
|
||||
// when changes were made to the clock GUI elements
|
||||
// Should be accurate enough for satellite tracker
|
||||
qint64 diffMsecs = m_systemDateTime.msecsTo(QDateTime::currentDateTime());
|
||||
return m_skymapDateTime.addMSecs(diffMsecs * m_multiplier);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
|
||||
// //
|
||||
// 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_SKYMAP_H_
|
||||
#define INCLUDE_FEATURE_SKYMAP_H_
|
||||
|
||||
#include <QHash>
|
||||
#include <QNetworkRequest>
|
||||
#include <QDateTime>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
|
||||
#include "skymapsettings.h"
|
||||
|
||||
class WebAPIAdapterInterface;
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
namespace SWGSDRangel {
|
||||
class SWGDeviceState;
|
||||
}
|
||||
|
||||
class SkyMap : public Feature
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
class MsgConfigureSkyMap : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const SkyMapSettings& getSettings() const { return m_settings; }
|
||||
const QList<QString>& getSettingsKeys() const { return m_settingsKeys; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureSkyMap* create(const SkyMapSettings& settings, const QList<QString>& settingsKeys, bool force) {
|
||||
return new MsgConfigureSkyMap(settings, settingsKeys, force);
|
||||
}
|
||||
|
||||
private:
|
||||
SkyMapSettings m_settings;
|
||||
QList<QString> m_settingsKeys;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureSkyMap(const SkyMapSettings& settings, const QList<QString>& settingsKeys, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_settingsKeys(settingsKeys),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgFind : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QString getTarget() const { return m_target; }
|
||||
|
||||
static MsgFind* create(const QString& target) {
|
||||
return new MsgFind(target);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_target;
|
||||
|
||||
MsgFind(const QString& target) :
|
||||
Message(),
|
||||
m_target(target)
|
||||
{}
|
||||
};
|
||||
|
||||
class MsgSetDateTime : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QDateTime getDateTime() const { return m_dateTime; }
|
||||
|
||||
static MsgSetDateTime* create(const QDateTime& dateTime) {
|
||||
return new MsgSetDateTime(dateTime);
|
||||
}
|
||||
|
||||
private:
|
||||
QDateTime m_dateTime;
|
||||
|
||||
MsgSetDateTime(const QDateTime& dateTime) :
|
||||
Message(),
|
||||
m_dateTime(dateTime)
|
||||
{}
|
||||
};
|
||||
|
||||
struct ViewDetails {
|
||||
double m_ra;
|
||||
double m_dec;
|
||||
float m_azimuth;
|
||||
float m_elevation;
|
||||
float m_fov;
|
||||
float m_latitude;
|
||||
float m_longitude;
|
||||
QDateTime m_dateTime;
|
||||
|
||||
ViewDetails() :
|
||||
m_ra(0.0),
|
||||
m_dec(0.0),
|
||||
m_azimuth(0.0f),
|
||||
m_elevation(0.0f),
|
||||
m_fov(0.0f),
|
||||
m_latitude(0.0f),
|
||||
m_longitude(0.0f)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class MsgReportViewDetails : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const ViewDetails& getViewDetails() { return m_viewDetails; }
|
||||
|
||||
static MsgReportViewDetails* create(const ViewDetails& viewDetails) {
|
||||
return new MsgReportViewDetails(viewDetails);
|
||||
}
|
||||
|
||||
private:
|
||||
ViewDetails m_viewDetails;
|
||||
|
||||
MsgReportViewDetails(const ViewDetails& viewDetails) :
|
||||
Message(),
|
||||
m_viewDetails(viewDetails)
|
||||
{}
|
||||
};
|
||||
|
||||
SkyMap(WebAPIAdapterInterface *webAPIAdapterInterface);
|
||||
virtual ~SkyMap();
|
||||
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);
|
||||
|
||||
virtual int webapiReportGet(
|
||||
SWGSDRangel::SWGFeatureReport& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiActionsPost(
|
||||
const QStringList& featureActionsKeys,
|
||||
SWGSDRangel::SWGFeatureActions& query,
|
||||
QString& errorMessage);
|
||||
|
||||
static void webapiFormatFeatureSettings(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
const SkyMapSettings& settings);
|
||||
|
||||
static void webapiUpdateFeatureSettings(
|
||||
SkyMapSettings& settings,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response);
|
||||
|
||||
void setSkyMapDateTime(QDateTime skymapDateTime, QDateTime systemDateTime, double multiplier);
|
||||
QDateTime getSkyMapDateTime();
|
||||
|
||||
static const char* const m_featureIdURI;
|
||||
static const char* const m_featureId;
|
||||
|
||||
private:
|
||||
SkyMapSettings m_settings;
|
||||
ViewDetails m_viewDetails;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
void applySettings(const SkyMapSettings& settings, const QList<QString>& settingsKeys, bool force = false);
|
||||
void webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response);
|
||||
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const SkyMapSettings& settings, bool force);
|
||||
|
||||
QDateTime m_skymapDateTime;
|
||||
QDateTime m_systemDateTime;
|
||||
double m_multiplier;
|
||||
QRecursiveMutex m_dateTimeMutex;
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_SKYMAP_H_
|
|
@ -0,0 +1,7 @@
|
|||
<RCC>
|
||||
<qresource prefix="/skymap/">
|
||||
<file>html/wwt.html</file>
|
||||
<file>html/esasky.html</file>
|
||||
<file>html/aladin.html</file>
|
||||
</qresource>
|
||||
</RCC>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,151 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// //
|
||||
// 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_SKYMAPGUI_H_
|
||||
#define INCLUDE_FEATURE_SKYMAPGUI_H_
|
||||
|
||||
#include <QTimer>
|
||||
#include <QJsonObject>
|
||||
#include <QWebEngineFullScreenRequest>
|
||||
#include <QWebEnginePage>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QWebEngineLoadingInfo>
|
||||
#endif
|
||||
#include <QGeoCoordinate>
|
||||
|
||||
#include <math.h>
|
||||
#include <limits>
|
||||
|
||||
#include "feature/featuregui.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "settings/rollupstate.h"
|
||||
#include "availablechannelorfeaturehandler.h"
|
||||
#include "maincore.h"
|
||||
|
||||
#include "skymapsettings.h"
|
||||
#include "webinterface.h"
|
||||
#include "webserver.h"
|
||||
#include "wtml.h"
|
||||
|
||||
class PluginAPI;
|
||||
class FeatureUISet;
|
||||
class SkyMap;
|
||||
|
||||
namespace Ui {
|
||||
class SkyMapGUI;
|
||||
}
|
||||
|
||||
class SkyMapGUI;
|
||||
|
||||
class SkyMapGUI : public FeatureGUI {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static SkyMapGUI* 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; }
|
||||
SkyMap *getSkyMap() { return m_skymap; }
|
||||
|
||||
private:
|
||||
Ui::SkyMapGUI* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
FeatureUISet* m_featureUISet;
|
||||
SkyMapSettings m_settings;
|
||||
QList<QString> m_settingsKeys;
|
||||
RollupState m_rollupState;
|
||||
bool m_doApplySettings;
|
||||
QObject *m_source;
|
||||
AvailableChannelOrFeatureList m_availableChannelOrFeatures;
|
||||
AvailableChannelOrFeatureHandler m_availableChannelOrFeatureHandler;
|
||||
|
||||
SkyMap* m_skymap;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
quint16 m_skymapTileServerPort;
|
||||
|
||||
WebServer *m_webServer;
|
||||
quint16 m_webPort;
|
||||
WTML m_wtml;
|
||||
WebInterface *m_webInterface;
|
||||
|
||||
double m_ra; //!< Target from source plugin
|
||||
double m_dec;
|
||||
QDateTime m_dateTime; //!< Date time from source plugin
|
||||
|
||||
QStringList m_wwtBackgrounds;
|
||||
|
||||
explicit SkyMapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
|
||||
virtual ~SkyMapGUI();
|
||||
|
||||
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 setDateTime(QDateTime dateTime);
|
||||
QDateTime getDateTime() const;
|
||||
void setPosition(float latitude, float longitude, float altitude);
|
||||
QGeoCoordinate getPosition() const;
|
||||
void initSkyMap();
|
||||
QString backgroundID(const QString& name);
|
||||
void updateBackgrounds();
|
||||
void updateToolbar();
|
||||
void updateProjection();
|
||||
void find(const QString& text);
|
||||
void sendToRotator(const QString& name, double az, double alt);
|
||||
|
||||
private slots:
|
||||
void onMenuDialogCalled(const QPoint &p);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void handleInputMessages();
|
||||
void on_tabs_tabCloseRequested(int index);
|
||||
void on_find_returnPressed();
|
||||
void on_displaySettings_clicked();
|
||||
void on_displayNames_clicked(bool checked);
|
||||
void on_displayConstellations_clicked(bool checked);
|
||||
void on_displayReticle_clicked(bool checked);
|
||||
void on_displayGrid_clicked(bool checked);
|
||||
void on_displayAntennaFoV_clicked(bool checked);
|
||||
void on_map_currentIndexChanged(int index);
|
||||
void on_background_currentIndexChanged(int index);
|
||||
void on_projection_currentIndexChanged(int index);
|
||||
void on_source_currentIndexChanged(int index);
|
||||
void on_track_clicked(bool checked);
|
||||
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
|
||||
void renderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
void loadingChanged(const QWebEngineLoadingInfo &loadingInfo);
|
||||
#endif
|
||||
void preferenceChanged(int elementType);
|
||||
void receivedEvent(const QJsonObject &obj);
|
||||
void wtmlUpdated(const QList<WTML::ImageSet>& dataSets);
|
||||
void updateSourceList(const QStringList& renameFrom, const QStringList& renameTo);
|
||||
void handlePipeMessageQueue(MessageQueue* messageQueue);
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_SKYMAPGUI_H_
|
|
@ -0,0 +1,431 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SkyMapGUI</class>
|
||||
<widget class="RollupContents" name="SkyMapGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>881</width>
|
||||
<height>293</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SkyMap</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="settingsContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>871</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</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="buttonLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="findLabel">
|
||||
<property name="text">
|
||||
<string>Find</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="find">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enter name of object to find</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="map">
|
||||
<property name="toolTip">
|
||||
<string>Sky map</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>WWT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ESASky</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Aladin</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="background">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select background image set</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="projection">
|
||||
<property name="toolTip">
|
||||
<string>Projection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="displayNames">
|
||||
<property name="toolTip">
|
||||
<string>Display names</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/info.png</normaloff>:/info.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="displayConstellations">
|
||||
<property name="toolTip">
|
||||
<string>Display constellations</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/skymap/icons/constellation.png</normaloff>:/skymap/icons/constellation.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="displayGrid">
|
||||
<property name="toolTip">
|
||||
<string>Display grid</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/gridrect.png</normaloff>:/gridrect.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="displayReticle">
|
||||
<property name="toolTip">
|
||||
<string>Display reticle</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/skymap/icons/reticle.png</normaloff>:/skymap/icons/reticle.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="displayAntennaFoV">
|
||||
<property name="toolTip">
|
||||
<string>Display antenna field-of-view</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>^</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/antenna.png</normaloff>:/antenna.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="track">
|
||||
<property name="toolTip">
|
||||
<string>Track source</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/gridpolar.png</normaloff>:/gridpolar.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="source">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Source feature to get target from</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>F0:1 StarTracker</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="displaySettings">
|
||||
<property name="toolTip">
|
||||
<string>Show 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>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="skymapContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>60</y>
|
||||
<width>591</width>
|
||||
<height>211</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SkyMap</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</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="QTabWidget" name="tabs">
|
||||
<property name="tabsClosable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tabBarAutoHide">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab0">
|
||||
<attribute name="title">
|
||||
<string>Map</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>3</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="WebView" name="web" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<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>
|
||||
</layout>
|
||||
</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>QWebEngineView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header location="global">QtWebEngineWidgets/QWebEngineView</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>WebView</class>
|
||||
<extends>QWebEngineView</extends>
|
||||
<header>webview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>find</tabstop>
|
||||
<tabstop>background</tabstop>
|
||||
<tabstop>displayNames</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
<include location="icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,84 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2019 Davide Gerhard <rainbow@irh.it> //
|
||||
// Copyright (C) 2020-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
|
||||
// //
|
||||
// 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 "skymapgui.h"
|
||||
#endif
|
||||
#include "skymap.h"
|
||||
#include "skymapplugin.h"
|
||||
#include "skymapwebapiadapter.h"
|
||||
|
||||
const PluginDescriptor SkyMapPlugin::m_pluginDescriptor = {
|
||||
SkyMap::m_featureId,
|
||||
QStringLiteral("Sky Map"),
|
||||
QStringLiteral("7.18.0"),
|
||||
QStringLiteral("(c) Jon Beniston, M7RCE"),
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
SkyMapPlugin::SkyMapPlugin(QObject* parent) :
|
||||
QObject(parent),
|
||||
m_pluginAPI(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& SkyMapPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void SkyMapPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
m_pluginAPI->registerFeature(SkyMap::m_featureIdURI, SkyMap::m_featureId, this);
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
FeatureGUI* SkyMapPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
|
||||
{
|
||||
(void) featureUISet;
|
||||
(void) feature;
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
FeatureGUI* SkyMapPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
|
||||
{
|
||||
return SkyMapGUI::create(m_pluginAPI, featureUISet, feature);
|
||||
}
|
||||
#endif
|
||||
|
||||
Feature* SkyMapPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
|
||||
{
|
||||
return new SkyMap(webAPIAdapterInterface);
|
||||
}
|
||||
|
||||
FeatureWebAPIAdapter* SkyMapPlugin::createFeatureWebAPIAdapter() const
|
||||
{
|
||||
return new SkyMapWebAPIAdapter();
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2015 John Greb <hexameron@spam.no> //
|
||||
// Copyright (C) 2020-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_SKYMAPPLUGIN_H
|
||||
#define INCLUDE_FEATURE_SKYMAPPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class FeatureGUI;
|
||||
class WebAPIAdapterInterface;
|
||||
|
||||
class SkyMapPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "sdrangel.feature.skymap")
|
||||
|
||||
public:
|
||||
explicit SkyMapPlugin(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_SKYMAPPLUGIN_H
|
|
@ -0,0 +1,361 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2017, 2019-2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2022 CRD716 <crd716@gmail.com> //
|
||||
// //
|
||||
// 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 <QDebug>
|
||||
|
||||
#include "util/simpleserializer.h"
|
||||
#include "util/httpdownloadmanager.h"
|
||||
#include "settings/serializable.h"
|
||||
|
||||
#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"),
|
||||
QStringLiteral("sdrangel.feature.gs232controller"),
|
||||
QStringLiteral("sdrangel.feature.map")
|
||||
};
|
||||
|
||||
SkyMapSettings::SkyMapSettings() :
|
||||
m_rollupState(nullptr)
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
SkyMapSettings::~SkyMapSettings()
|
||||
{
|
||||
}
|
||||
|
||||
void SkyMapSettings::resetToDefaults()
|
||||
{
|
||||
m_map = "WWT";
|
||||
m_displayNames = true;
|
||||
m_displayConstellations = true;
|
||||
m_displayReticle = true;
|
||||
m_displayGrid = false;
|
||||
m_displayAntennaFoV = false;
|
||||
m_projection = "";
|
||||
m_source = "";
|
||||
m_track = false;
|
||||
m_hpbw = 10.0f;
|
||||
m_latitude = 0.0f;
|
||||
m_longitude = 0.0f;
|
||||
m_altitude = 0.0f;
|
||||
m_useMyPosition = false;
|
||||
m_wwtSettings = QHash<QString, QVariant>({
|
||||
{"constellationBoundaries", false},
|
||||
{"constellationFigures", true},
|
||||
{"constellationLabels", true},
|
||||
{"constellationPictures", false},
|
||||
{"constellationSelection", false},
|
||||
{"ecliptic", false},
|
||||
{"eclipticOverviewText", false},
|
||||
{"eclipticGrid", false},
|
||||
{"eclipticGridText", true},
|
||||
{"altAzGrid", true},
|
||||
{"altAzGridText", true},
|
||||
{"galacticGrid", false},
|
||||
{"galacticGridText", true},
|
||||
{"elevationModel", false},
|
||||
{"earthSky", false},
|
||||
{"horizon", false},
|
||||
{"iss", false},
|
||||
{"precessionChart", false},
|
||||
{"skyGrids", false},
|
||||
{"skyNode", false},
|
||||
{"skyOverlays", false},
|
||||
{"solarSystemCosmos", false},
|
||||
{"solarSystemLighting", true},
|
||||
{"solarSystemMilkyWay", true},
|
||||
{"solarSystemMinorOrbits", false},
|
||||
{"solarSystemMinorPlanets", false},
|
||||
{"solarSystemMultiRes", true},
|
||||
{"solarSystemOrbits", true},
|
||||
{"solarSystemOverlays", false},
|
||||
{"solarSystemPlanets", true},
|
||||
{"solarSystemStars", true},
|
||||
|
||||
});
|
||||
m_title = "Sky Map";
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIFeatureSetIndex = 0;
|
||||
m_reverseAPIFeatureIndex = 0;
|
||||
m_workspaceIndex = 0;
|
||||
}
|
||||
|
||||
QByteArray SkyMapSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeString(2, m_map);
|
||||
s.writeBool(1, m_displayNames);
|
||||
s.writeBool(15, m_displayConstellations);
|
||||
s.writeBool(17, m_displayReticle);
|
||||
s.writeBool(18, m_displayGrid);
|
||||
s.writeBool(21, m_displayAntennaFoV);
|
||||
s.writeString(3, m_projection);
|
||||
s.writeString(4, m_source);
|
||||
s.writeBool(20, m_track);
|
||||
s.writeFloat(22, m_hpbw);
|
||||
s.writeFloat(23, m_latitude);
|
||||
s.writeFloat(24, m_longitude);
|
||||
s.writeFloat(25, m_altitude);
|
||||
s.writeBool(26, m_useMyPosition);
|
||||
s.writeHash(27, m_wwtSettings);
|
||||
|
||||
s.writeString(8, m_title);
|
||||
s.writeU32(9, m_rgbColor);
|
||||
s.writeBool(10, m_useReverseAPI);
|
||||
s.writeString(11, m_reverseAPIAddress);
|
||||
s.writeU32(12, m_reverseAPIPort);
|
||||
s.writeU32(13, m_reverseAPIFeatureSetIndex);
|
||||
s.writeU32(14, m_reverseAPIFeatureIndex);
|
||||
|
||||
if (m_rollupState) {
|
||||
s.writeBlob(19, m_rollupState->serialize());
|
||||
}
|
||||
|
||||
s.writeS32(33, m_workspaceIndex);
|
||||
s.writeBlob(34, m_geometryBytes);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool SkyMapSettings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if (!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.getVersion() == 1)
|
||||
{
|
||||
QByteArray bytetmp;
|
||||
uint32_t utmp;
|
||||
QString strtmp;
|
||||
QByteArray blob;
|
||||
QString string;
|
||||
|
||||
d.readString(2, &m_map, "WWT");
|
||||
d.readBool(1, &m_displayNames, true);
|
||||
d.readBool(15, &m_displayConstellations, true);
|
||||
d.readBool(17, &m_displayReticle, true);
|
||||
d.readBool(18, &m_displayGrid, true);
|
||||
d.readBool(21, &m_displayAntennaFoV, true);
|
||||
d.readString(3, &m_projection, "");
|
||||
d.readString(4, &m_source, "");
|
||||
d.readBool(20, &m_track, false);
|
||||
d.readFloat(22, &m_hpbw, 10.0f);
|
||||
d.readFloat(23, &m_latitude, 0.0f);
|
||||
d.readFloat(24, &m_longitude, 0.0f);
|
||||
d.readFloat(25, &m_altitude, 0.0f);
|
||||
d.readBool(26, &m_useMyPosition, false);
|
||||
d.readHash(27, &m_wwtSettings);
|
||||
|
||||
d.readString(8, &m_title, "Sky Map");
|
||||
d.readU32(9, &m_rgbColor, QColor(225, 25, 99).rgba());
|
||||
d.readBool(10, &m_useReverseAPI, false);
|
||||
d.readString(11, &m_reverseAPIAddress, "127.0.0.1");
|
||||
d.readU32(12, &utmp, 0);
|
||||
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_reverseAPIPort = utmp;
|
||||
} else {
|
||||
m_reverseAPIPort = 8888;
|
||||
}
|
||||
|
||||
d.readU32(13, &utmp, 0);
|
||||
m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
|
||||
d.readU32(14, &utmp, 0);
|
||||
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
|
||||
|
||||
if (m_rollupState)
|
||||
{
|
||||
d.readBlob(19, &bytetmp);
|
||||
m_rollupState->deserialize(bytetmp);
|
||||
}
|
||||
|
||||
d.readS32(33, &m_workspaceIndex, 0);
|
||||
d.readBlob(34, &m_geometryBytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SkyMapSettings::applySettings(const QStringList& settingsKeys, const SkyMapSettings& settings)
|
||||
{
|
||||
if (settingsKeys.contains("map")) {
|
||||
m_map = settings.m_map;
|
||||
}
|
||||
if (settingsKeys.contains("displayNames")) {
|
||||
m_displayNames = settings.m_displayNames;
|
||||
}
|
||||
if (settingsKeys.contains("displayConstellations")) {
|
||||
m_displayConstellations = settings.m_displayConstellations;
|
||||
}
|
||||
if (settingsKeys.contains("displayReticle")) {
|
||||
m_displayReticle = settings.m_displayReticle;
|
||||
}
|
||||
if (settingsKeys.contains("displayGrid")) {
|
||||
m_displayGrid = settings.m_displayGrid;
|
||||
}
|
||||
if (settingsKeys.contains("displayAntennaFoV")) {
|
||||
m_displayAntennaFoV = settings.m_displayAntennaFoV;
|
||||
}
|
||||
if (settingsKeys.contains("background")) {
|
||||
m_background = settings.m_background;
|
||||
}
|
||||
if (settingsKeys.contains("projection")) {
|
||||
m_projection = settings.m_projection;
|
||||
}
|
||||
if (settingsKeys.contains("source")) {
|
||||
m_source = settings.m_source;
|
||||
}
|
||||
if (settingsKeys.contains("track")) {
|
||||
m_track = settings.m_track;
|
||||
}
|
||||
if (settingsKeys.contains("hpbw")) {
|
||||
m_hpbw = settings.m_hpbw;
|
||||
}
|
||||
if (settingsKeys.contains("latitude")) {
|
||||
m_latitude = settings.m_latitude;
|
||||
}
|
||||
if (settingsKeys.contains("longitude")) {
|
||||
m_longitude = settings.m_longitude;
|
||||
}
|
||||
if (settingsKeys.contains("altitude")) {
|
||||
m_altitude = settings.m_altitude;
|
||||
}
|
||||
if (settingsKeys.contains("useMyPosition")) {
|
||||
m_useMyPosition = settings.m_useMyPosition;
|
||||
}
|
||||
if (settingsKeys.contains("title")) {
|
||||
m_title = settings.m_title;
|
||||
}
|
||||
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 SkyMapSettings::getDebugString(const QStringList& settingsKeys, bool force) const
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
|
||||
if (settingsKeys.contains("map") || force) {
|
||||
ostr << " m_map: " << m_map.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("displayNames") || force) {
|
||||
ostr << " m_displayNames: " << m_displayNames;
|
||||
}
|
||||
if (settingsKeys.contains("displayConstellations") || force) {
|
||||
ostr << " m_displayConstellations: " << m_displayConstellations;
|
||||
}
|
||||
if (settingsKeys.contains("displayReticle") || force) {
|
||||
ostr << " m_displayReticle: " << m_displayReticle;
|
||||
}
|
||||
if (settingsKeys.contains("displayAntennaFoV") || force) {
|
||||
ostr << " m_displayAntennaFoV: " << m_displayAntennaFoV;
|
||||
}
|
||||
if (settingsKeys.contains("background") || force) {
|
||||
ostr << " m_background: " << m_background.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("projection") || force) {
|
||||
ostr << " m_projection: " << m_projection.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("source") || force) {
|
||||
ostr << " m_source: " << m_source.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("track") || force) {
|
||||
ostr << " m_track: " << m_track;
|
||||
}
|
||||
if (settingsKeys.contains("hpbw") || force) {
|
||||
ostr << " m_hpbw: " << m_hpbw;
|
||||
}
|
||||
if (settingsKeys.contains("latitude") || force) {
|
||||
ostr << " m_latitude: " << m_latitude;
|
||||
}
|
||||
if (settingsKeys.contains("longitude") || force) {
|
||||
ostr << " m_longitude: " << m_longitude;
|
||||
}
|
||||
if (settingsKeys.contains("altitude") || force) {
|
||||
ostr << " m_altitude: " << m_altitude;
|
||||
}
|
||||
if (settingsKeys.contains("useMyPosition") || force) {
|
||||
ostr << " m_useMyPosition: " << m_useMyPosition;
|
||||
}
|
||||
if (settingsKeys.contains("title") || force) {
|
||||
ostr << " m_title: " << m_title.toStdString();
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2020, 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_SKYMAPSETTINGS_H_
|
||||
#define INCLUDE_FEATURE_SKYMAPSETTINGS_H_
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
|
||||
class Serializable;
|
||||
|
||||
struct SkyMapSettings
|
||||
{
|
||||
QString m_map; //!< "WWT", "ESASky" or "Aladin"
|
||||
bool m_displayNames;
|
||||
bool m_displayConstellations;
|
||||
bool m_displayReticle;
|
||||
bool m_displayGrid;
|
||||
bool m_displayAntennaFoV;
|
||||
QString m_background;
|
||||
QString m_projection;
|
||||
QString m_source; //!< Plugin to get target coords from
|
||||
bool m_track; //!< Track source
|
||||
float m_hpbw; //!< Antenna HPBW
|
||||
float m_latitude; //!< Antenna position
|
||||
float m_longitude;
|
||||
float m_altitude;
|
||||
bool m_useMyPosition;
|
||||
QHash<QString, QVariant> m_wwtSettings; //!< WWT specific settings
|
||||
|
||||
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;
|
||||
|
||||
SkyMapSettings();
|
||||
~SkyMapSettings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_SKYMAPSETTINGS_H_
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 <QToolButton>
|
||||
|
||||
#include "skymapsettingsdialog.h"
|
||||
|
||||
SkyMapSettingsDialog::SkyMapSettingsDialog(SkyMapSettings *settings, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
m_settings(settings),
|
||||
ui(new Ui::SkyMapSettingsDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->hpbw->setValue(m_settings->m_hpbw);
|
||||
ui->latitude->setText(QString::number(m_settings->m_latitude, 'f', 6));
|
||||
ui->longitude->setText(QString::number(m_settings->m_longitude, 'f', 6));
|
||||
ui->altitude->setValue(m_settings->m_altitude);
|
||||
ui->useMyPosition->setChecked(m_settings->m_useMyPosition);
|
||||
|
||||
// WWT
|
||||
ui->constellationBoundaries->setChecked(m_settings->m_wwtSettings.value("constellationBoundaries").toBool());
|
||||
ui->constellationFigures->setChecked(m_settings->m_wwtSettings.value("constellationFigures").toBool());
|
||||
ui->constellationLabels->setChecked(m_settings->m_wwtSettings.value("constellationLabels").toBool());
|
||||
ui->constellationPictures->setChecked(m_settings->m_wwtSettings.value("constellationPictures").toBool());
|
||||
ui->constellationSelection->setChecked(m_settings->m_wwtSettings.value("constellationSelection").toBool());
|
||||
|
||||
ui->ecliptic->setChecked(m_settings->m_wwtSettings.value("ecliptic").toBool());
|
||||
ui->eclipticOverviewText->setChecked(m_settings->m_wwtSettings.value("eclipticOverviewText").toBool());
|
||||
ui->eclipticGrid->setChecked(m_settings->m_wwtSettings.value("eclipticGrid").toBool());
|
||||
ui->eclipticGridText->setChecked(m_settings->m_wwtSettings.value("eclipticGridText").toBool());
|
||||
ui->altAzGrid->setChecked(m_settings->m_wwtSettings.value("altAzGrid").toBool());
|
||||
ui->altAzGridText->setChecked(m_settings->m_wwtSettings.value("altAzGridText").toBool());
|
||||
ui->equatorialGrid->setChecked(m_settings->m_wwtSettings.value("equatorialGrid").toBool());
|
||||
ui->equatorialGridText->setChecked(m_settings->m_wwtSettings.value("equatorialGridText").toBool());
|
||||
ui->galacticGrid->setChecked(m_settings->m_wwtSettings.value("galacticGrid").toBool());
|
||||
ui->galacticGridText->setChecked(m_settings->m_wwtSettings.value("galacticGridText").toBool());
|
||||
ui->precessionChart->setChecked(m_settings->m_wwtSettings.value("precessionChart").toBool());
|
||||
|
||||
ui->solarSystemCosmos->setChecked(m_settings->m_wwtSettings.value("solarSystemCosmos").toBool());
|
||||
ui->solarSystemLighting->setChecked(m_settings->m_wwtSettings.value("solarSystemLighting").toBool());
|
||||
ui->solarSystemMilkyWay->setChecked(m_settings->m_wwtSettings.value("solarSystemMilkyWay").toBool());
|
||||
ui->solarSystemMinorOrbits->setChecked(m_settings->m_wwtSettings.value("solarSystemMinorOrbits").toBool());
|
||||
ui->solarSystemMinorPlanets->setChecked(m_settings->m_wwtSettings.value("solarSystemMinorPlanets").toBool());
|
||||
ui->solarSystemMultiRes->setChecked(m_settings->m_wwtSettings.value("solarSystemMultiRes").toBool());
|
||||
ui->solarSystemOrbits->setChecked(m_settings->m_wwtSettings.value("solarSystemOrbits").toBool());
|
||||
//ui->solarSystemOverlays->setChecked(m_settings->m_wwtSettings.value("solarSystemOverlays").toBool());
|
||||
ui->solarSystemPlanets->setChecked(m_settings->m_wwtSettings.value("solarSystemPlanets").toBool());
|
||||
ui->solarSystemStars->setChecked(m_settings->m_wwtSettings.value("solarSystemStars").toBool());
|
||||
|
||||
ui->iss->setChecked(m_settings->m_wwtSettings.value("iss").toBool());
|
||||
}
|
||||
|
||||
SkyMapSettingsDialog::~SkyMapSettingsDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SkyMapSettingsDialog::accept()
|
||||
{
|
||||
QDialog::accept();
|
||||
|
||||
if (m_settings->m_hpbw != ui->hpbw->value())
|
||||
{
|
||||
m_settings->m_hpbw = ui->hpbw->value();
|
||||
m_settingsKeysChanged.append("hpbw");
|
||||
}
|
||||
if (m_settings->m_latitude != ui->latitude->text().toFloat())
|
||||
{
|
||||
m_settings->m_latitude = ui->latitude->text().toFloat();
|
||||
m_settingsKeysChanged.append("latitude");
|
||||
}
|
||||
if (m_settings->m_longitude != ui->longitude->text().toFloat())
|
||||
{
|
||||
m_settings->m_longitude = ui->longitude->text().toFloat();
|
||||
m_settingsKeysChanged.append("longitude");
|
||||
}
|
||||
if (m_settings->m_altitude != ui->altitude->value())
|
||||
{
|
||||
m_settings->m_altitude = ui->altitude->value();
|
||||
m_settingsKeysChanged.append("altitude");
|
||||
}
|
||||
if (m_settings->m_useMyPosition != ui->useMyPosition->isChecked())
|
||||
{
|
||||
m_settings->m_useMyPosition = ui->useMyPosition->isChecked();
|
||||
m_settingsKeysChanged.append("useMyPosition");
|
||||
}
|
||||
|
||||
m_settings->m_wwtSettings.insert("constellationBoundaries", ui->constellationBoundaries->isChecked());
|
||||
m_settings->m_wwtSettings.insert("constellationFigures", ui->constellationFigures->isChecked());
|
||||
m_settings->m_wwtSettings.insert("constellationLabels", ui->constellationLabels->isChecked());
|
||||
m_settings->m_wwtSettings.insert("constellationPictures", ui->constellationPictures->isChecked());
|
||||
m_settings->m_wwtSettings.insert("constellationSelection", ui->constellationSelection->isChecked());
|
||||
|
||||
m_settings->m_wwtSettings.insert("ecliptic", ui->ecliptic->isChecked());
|
||||
m_settings->m_wwtSettings.insert("eclipticOverviewText", ui->eclipticOverviewText->isChecked());
|
||||
m_settings->m_wwtSettings.insert("eclipticGrid", ui->eclipticGrid->isChecked());
|
||||
m_settings->m_wwtSettings.insert("eclipticGridText", ui->eclipticGridText->isChecked());
|
||||
m_settings->m_wwtSettings.insert("altAzGrid", ui->altAzGrid->isChecked());
|
||||
m_settings->m_wwtSettings.insert("altAzGridText", ui->altAzGridText->isChecked());
|
||||
m_settings->m_wwtSettings.insert("equatorialGrid", ui->equatorialGrid->isChecked());
|
||||
m_settings->m_wwtSettings.insert("equatorialGridText", ui->equatorialGridText->isChecked());
|
||||
m_settings->m_wwtSettings.insert("galacticGrid", ui->galacticGrid->isChecked());
|
||||
m_settings->m_wwtSettings.insert("galacticGridText", ui->galacticGridText->isChecked());
|
||||
m_settings->m_wwtSettings.insert("precessionChart", ui->precessionChart->isChecked());
|
||||
|
||||
m_settings->m_wwtSettings.insert("solarSystemCosmos", ui->solarSystemCosmos->isChecked());
|
||||
m_settings->m_wwtSettings.insert("solarSystemLighting", ui->solarSystemLighting->isChecked());
|
||||
m_settings->m_wwtSettings.insert("solarSystemMilkyWay", ui->solarSystemMilkyWay->isChecked());
|
||||
m_settings->m_wwtSettings.insert("solarSystemMinorOrbits", ui->solarSystemMinorOrbits->isChecked());
|
||||
m_settings->m_wwtSettings.insert("solarSystemMinorPlanets", ui->solarSystemMinorPlanets->isChecked());
|
||||
m_settings->m_wwtSettings.insert("solarSystemMultiRes", ui->solarSystemMultiRes->isChecked());
|
||||
m_settings->m_wwtSettings.insert("solarSystemOrbits", ui->solarSystemOrbits->isChecked());
|
||||
//m_settings->m_wwtSettings.insert("solarSystemOverlays", ui->solarSystemOverlays->isChecked());
|
||||
m_settings->m_wwtSettings.insert("solarSystemPlanets", ui->solarSystemPlanets->isChecked());
|
||||
m_settings->m_wwtSettings.insert("solarSystemStars", ui->solarSystemStars->isChecked());
|
||||
|
||||
m_settings->m_wwtSettings.insert("iss", ui->iss->isChecked());
|
||||
|
||||
m_settingsKeysChanged.append("wwtSettings"); // Being lazy here
|
||||
}
|
||||
|
||||
void SkyMapSettingsDialog::on_constellationBoundaries_toggled(bool checked)
|
||||
{
|
||||
ui->constellationSelection->setEnabled(checked);
|
||||
}
|
||||
|
||||
void SkyMapSettingsDialog::on_ecliptic_toggled(bool checked)
|
||||
{
|
||||
ui->eclipticOverviewText->setEnabled(checked);
|
||||
}
|
||||
|
||||
void SkyMapSettingsDialog::on_eclipticGrid_toggled(bool checked)
|
||||
{
|
||||
ui->eclipticGridText->setEnabled(checked);
|
||||
}
|
||||
|
||||
void SkyMapSettingsDialog::on_altAzGrid_toggled(bool checked)
|
||||
{
|
||||
ui->altAzGridText->setEnabled(checked);
|
||||
}
|
||||
|
||||
void SkyMapSettingsDialog::on_equatorialGrid_toggled(bool checked)
|
||||
{
|
||||
ui->equatorialGridText->setEnabled(checked);
|
||||
}
|
||||
|
||||
void SkyMapSettingsDialog::on_galacticGrid_toggled(bool checked)
|
||||
{
|
||||
ui->galacticGridText->setEnabled(checked);
|
||||
}
|
||||
|
||||
void SkyMapSettingsDialog::on_useMyPosition_toggled(bool checked)
|
||||
{
|
||||
ui->latitude->setEnabled(!checked);
|
||||
ui->latitudeLabel->setEnabled(!checked);
|
||||
ui->longitude->setEnabled(!checked);
|
||||
ui->longitudeLabel->setEnabled(!checked);
|
||||
ui->altitude->setEnabled(!checked);
|
||||
ui->altitudeLabel->setEnabled(!checked);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_SKYMAPSETTINGSDIALOG_H
|
||||
#define INCLUDE_FEATURE_SKYMAPSETTINGSDIALOG_H
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "ui_skymapsettingsdialog.h"
|
||||
#include "skymapsettings.h"
|
||||
|
||||
class SkyMapSettingsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SkyMapSettingsDialog(SkyMapSettings *settings, QWidget* parent = 0);
|
||||
~SkyMapSettingsDialog();
|
||||
|
||||
public:
|
||||
QStringList m_settingsKeysChanged; // List of setting keys that have been changed
|
||||
|
||||
private:
|
||||
SkyMapSettings *m_settings;
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
void on_constellationBoundaries_toggled(bool checked);
|
||||
void on_ecliptic_toggled(bool checked);
|
||||
void on_eclipticGrid_toggled(bool checked);
|
||||
void on_altAzGrid_toggled(bool checked);
|
||||
void on_equatorialGrid_toggled(bool checked);
|
||||
void on_galacticGrid_toggled(bool checked);
|
||||
void on_useMyPosition_toggled(bool checked);
|
||||
|
||||
private:
|
||||
Ui::SkyMapSettingsDialog* ui;
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_SKYMAPSETTINGSDIALOG_H
|
|
@ -0,0 +1,480 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SkyMapSettingsDialog</class>
|
||||
<widget class="QDialog" name="SkyMapSettingsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>441</width>
|
||||
<height>671</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SkyMap Display Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Settings</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="useMyPosition">
|
||||
<property name="text">
|
||||
<string>Use observation location from Preferences > My Position</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="latitudeLabel">
|
||||
<property name="text">
|
||||
<string>Latitude (°)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="longitudeLabel">
|
||||
<property name="text">
|
||||
<string>Longitude (°)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="altitudeLabel">
|
||||
<property name="text">
|
||||
<string>Altitude (m)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="altitude">
|
||||
<property name="toolTip">
|
||||
<string>Antenna/observation altitude in metres</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-100</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="hpbwLabel">
|
||||
<property name="text">
|
||||
<string>HPBW (°)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QDoubleSpinBox" name="hpbw">
|
||||
<property name="toolTip">
|
||||
<string>Antenna half-power beamwidth in degrees</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>360.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="latitude">
|
||||
<property name="toolTip">
|
||||
<string>Latitude of antenna/observation location in degrees. North positive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="longitude">
|
||||
<property name="toolTip">
|
||||
<string>Longitude of antenna/observation location in degrees. East positive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="skymapsTab">
|
||||
<attribute name="title">
|
||||
<string>WWT</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="gridGroupBox">
|
||||
<property name="title">
|
||||
<string>Grids</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="ecliptic">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>160</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ecliptic</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="eclipticGrid">
|
||||
<property name="text">
|
||||
<string>Ecliptic Grid</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="eclipticOverviewText">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ecliptic Overview Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="eclipticGridText">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ecliptic Grid Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="altAzGrid">
|
||||
<property name="text">
|
||||
<string>Alt Az Grid</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="altAzGridText">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Alt Az Grid Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="equatorialGrid">
|
||||
<property name="text">
|
||||
<string>Equatorial Grid</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="equatorialGridText">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Equatorial Grid Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="galacticGrid">
|
||||
<property name="text">
|
||||
<string>Galactic Grid</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="galacticGridText">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Galactic Grid Text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="precessionChart">
|
||||
<property name="text">
|
||||
<string>Precession Chart</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="23" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="constellationGroupBox">
|
||||
<property name="title">
|
||||
<string>Constellations</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="constellationFigures">
|
||||
<property name="toolTip">
|
||||
<string>Display constellation stick figures</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Figures</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="constellationBoundaries">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>160</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Display boundaries between constellations</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Boundaries</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="constellationSelection">
|
||||
<property name="toolTip">
|
||||
<string>Displays boundary only for constellation at the center of view</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Centre boundary only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="constellationLabels">
|
||||
<property name="toolTip">
|
||||
<string>Display constellation names</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Labels</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="constellationPictures">
|
||||
<property name="toolTip">
|
||||
<string>Display constellation pictures</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Pictures</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="0">
|
||||
<widget class="QCheckBox" name="iss">
|
||||
<property name="toolTip">
|
||||
<string>Display ISS model</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ISS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QGroupBox" name="solarSystemGroupBox">
|
||||
<property name="toolTip">
|
||||
<string>Settings that apply only to the Solar System view</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Solar System View</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="solarSystemCosmos">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>160</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Displays cosmos (when zoomed far out)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cosmos</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="solarSystemMilkyWay">
|
||||
<property name="toolTip">
|
||||
<string>Displays Milky Way</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Milky Way</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="solarSystemStars">
|
||||
<property name="toolTip">
|
||||
<string>Displays stars</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stars</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="solarSystemPlanets">
|
||||
<property name="toolTip">
|
||||
<string>Displays planets</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Planets</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="solarSystemMinorPlanets">
|
||||
<property name="toolTip">
|
||||
<string>Displays asteroids</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Asteroids</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="solarSystemOrbits">
|
||||
<property name="toolTip">
|
||||
<string>Displays planetary orbits</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Planetary Orbits</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="solarSystemMinorOrbits">
|
||||
<property name="toolTip">
|
||||
<string>Displays orbits of moons and minor satellites</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Moon and Satellite Orbits</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="solarSystemLighting">
|
||||
<property name="toolTip">
|
||||
<string>Applies lighting and shadows to planets</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lighting and Shadows</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="solarSystemMultiRes">
|
||||
<property name="toolTip">
|
||||
<string>Uses higher resolution planetary imagery when zooming in</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mulit Res Planets</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
<include location="icons.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SkyMapSettingsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SkyMapSettingsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -0,0 +1,54 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 "skymap.h"
|
||||
#include "skymapwebapiadapter.h"
|
||||
|
||||
SkyMapWebAPIAdapter::SkyMapWebAPIAdapter()
|
||||
{}
|
||||
|
||||
SkyMapWebAPIAdapter::~SkyMapWebAPIAdapter()
|
||||
{}
|
||||
|
||||
int SkyMapWebAPIAdapter::webapiSettingsGet(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setSkyMapSettings(new SWGSDRangel::SWGSkyMapSettings());
|
||||
response.getSkyMapSettings()->init();
|
||||
SkyMap::webapiFormatFeatureSettings(response, m_settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
int SkyMapWebAPIAdapter::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) force; // no action
|
||||
(void) errorMessage;
|
||||
SkyMap::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response);
|
||||
|
||||
return 200;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_SKYMAP_WEBAPIADAPTER_H
|
||||
#define INCLUDE_SKYMAP_WEBAPIADAPTER_H
|
||||
|
||||
#include "feature/featurewebapiadapter.h"
|
||||
#include "skymapsettings.h"
|
||||
|
||||
/**
|
||||
* Standalone API adapter only for the settings
|
||||
*/
|
||||
class SkyMapWebAPIAdapter : public FeatureWebAPIAdapter {
|
||||
public:
|
||||
SkyMapWebAPIAdapter();
|
||||
virtual ~SkyMapWebAPIAdapter();
|
||||
|
||||
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:
|
||||
SkyMapSettings m_settings;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_SKYMAP_WEBAPIADAPTER_H
|
|
@ -0,0 +1,160 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 <QJsonObject>
|
||||
#include <QHashIterator>
|
||||
|
||||
#include "webinterface.h"
|
||||
|
||||
WebInterface::WebInterface(QObject *parent) :
|
||||
WebSocketServer(parent)
|
||||
{
|
||||
}
|
||||
|
||||
// Set the current camera view to a given coordinates
|
||||
void WebInterface::setView(double ra, double dec, float zoom)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "setView"},
|
||||
{"ra", ra},
|
||||
{"dec", dec},
|
||||
{"zoom", zoom}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
// Set observation position
|
||||
void WebInterface::setPosition(const QGeoCoordinate& position)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "setPosition"},
|
||||
{"latitude", position.latitude()},
|
||||
{"longitude", position.longitude()},
|
||||
{"altitude", position.altitude()}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
// Set date and time
|
||||
void WebInterface::setDateTime(QDateTime dateTime)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "setDateTime"},
|
||||
{"dateTime", dateTime.toUTC().toString(Qt::ISODateWithMs)}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
// Set the camera to track the item with the specified name
|
||||
void WebInterface::track(const QString& name)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "track"},
|
||||
{"name", name}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::setBackground(const QString &background)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "setBackground"},
|
||||
{"background", background},
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::setProjection(const QString &projection)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "setProjection"},
|
||||
{"projection", projection},
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::showConstellations(bool show)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "showConstellations"},
|
||||
{"show", show}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::showReticle(bool show)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "showReticle"},
|
||||
{"show", show}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::showGrid(bool show)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "showGrid"},
|
||||
{"show", show}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::showNames(bool show)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "showNames"},
|
||||
{"show", show}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::showAntennaFoV(bool show)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "showAntennaFoV"},
|
||||
{"show", show}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::setAntennaFoV(float hpbw)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "setAntennaFoV"},
|
||||
{"hpbw", hpbw}
|
||||
};
|
||||
send(obj);
|
||||
}
|
||||
|
||||
void WebInterface::setWWTSettings(const QHash<QString, QVariant>& settings)
|
||||
{
|
||||
QJsonObject obj {
|
||||
{"command", "setWWTSettings"},
|
||||
};
|
||||
|
||||
QHashIterator<QString, QVariant> itr(settings);
|
||||
while (itr.hasNext())
|
||||
{
|
||||
itr.next();
|
||||
QString key = itr.key();
|
||||
QVariant value = itr.value();
|
||||
obj.insert(key, value.toString());
|
||||
}
|
||||
|
||||
send(obj);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_WEBINTERFACE_H_
|
||||
#define INCLUDE_FEATURE_WEBINTERFACE_H_
|
||||
|
||||
#include <QGeoCoordinate>
|
||||
|
||||
#include "websocketserver.h"
|
||||
|
||||
// Interface between C++ code and Web page via Web Socket using JSON
|
||||
class WebInterface : public WebSocketServer
|
||||
{
|
||||
public:
|
||||
|
||||
WebInterface(QObject *parent = nullptr);
|
||||
void setView(double ra, double dec, float zoom=1.0f);
|
||||
void setPosition(const QGeoCoordinate& position);
|
||||
void setDateTime(QDateTime dateTime);
|
||||
void track(const QString &name);
|
||||
void setBackground(const QString& name);
|
||||
void setProjection(const QString& name);
|
||||
void showConstellations(bool show);
|
||||
void showReticle(bool show);
|
||||
void showGrid(bool show);
|
||||
void showAntennaFoV(bool show);
|
||||
void showNames(bool show);
|
||||
void setAntennaFoV(float hpbw);
|
||||
void setWWTSettings(const QHash<QString, QVariant>& settings);
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_WEBINTERFACE_H_
|
|
@ -0,0 +1,222 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 <QResource>
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QDebug>
|
||||
|
||||
#include "webserver.h"
|
||||
|
||||
// port - port to listen on / is listening on. Use 0 for any free port.
|
||||
WebServer::WebServer(quint16 &port, QObject* parent) :
|
||||
QTcpServer(parent),
|
||||
m_defaultMimeType("application/octet-stream")
|
||||
{
|
||||
listen(QHostAddress::Any, port);
|
||||
port = serverPort();
|
||||
qDebug() << "WebServer on port " << port;
|
||||
|
||||
m_mimeTypes.insert(".html", new MimeType("text/html; charset=\"utf-8\"", false));
|
||||
m_mimeTypes.insert(".png", new MimeType("image/png"));
|
||||
m_mimeTypes.insert(".glb", new MimeType("model/gltf-binary"));
|
||||
m_mimeTypes.insert(".glbe", new MimeType("model/gltf-binary"));
|
||||
m_mimeTypes.insert(".js", new MimeType("text/javascript"));
|
||||
m_mimeTypes.insert(".css", new MimeType("text/css"));
|
||||
m_mimeTypes.insert(".json", new MimeType("application/json"));
|
||||
m_mimeTypes.insert(".geojson", new MimeType("application/geo+json"));
|
||||
}
|
||||
|
||||
void WebServer::incomingConnection(qintptr socket)
|
||||
{
|
||||
QTcpSocket* s = new QTcpSocket(this);
|
||||
connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
|
||||
connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
|
||||
s->setSocketDescriptor(socket);
|
||||
//addPendingConnection(socket);
|
||||
}
|
||||
|
||||
// Don't include leading or trailing / in from
|
||||
void WebServer::addPathSubstitution(const QString &from, const QString &to)
|
||||
{
|
||||
qDebug() << "Mapping " << from << " to " << to;
|
||||
m_pathSubstitutions.insert(from, to);
|
||||
}
|
||||
|
||||
void WebServer::addSubstitution(QString path, QString from, QString to)
|
||||
{
|
||||
Substitution *s = new Substitution(from, to);
|
||||
if (m_substitutions.contains(path))
|
||||
{
|
||||
QList<Substitution *> *list = m_substitutions.value(path);
|
||||
QMutableListIterator<Substitution *> i(*list);
|
||||
while (i.hasNext()) {
|
||||
Substitution *sub = i.next();
|
||||
if (sub->m_from == from) {
|
||||
i.remove();
|
||||
delete sub;
|
||||
}
|
||||
}
|
||||
list->append(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
QList<Substitution *> *list = new QList<Substitution *>();
|
||||
list->append(s);
|
||||
m_substitutions.insert(path, list);
|
||||
}
|
||||
}
|
||||
|
||||
QString WebServer::substitute(QString path, QString html)
|
||||
{
|
||||
QList<Substitution *> *list = m_substitutions.value(path);
|
||||
for (const auto s : *list) {
|
||||
html = html.replace(s->m_from, s->m_to);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
void WebServer::addFile(const QString &path, const QByteArray &data)
|
||||
{
|
||||
m_files.insert(path, data);
|
||||
}
|
||||
|
||||
void WebServer::sendFile(QTcpSocket* socket, const QByteArray &data, MimeType *mimeType, const QString &path)
|
||||
{
|
||||
QString header = QString("HTTP/1.0 200 Ok\r\nContent-Type: %1\r\nAccess-Control-Allow-Origin \"*\"\r\n\r\n").arg(mimeType->m_type);
|
||||
if (mimeType->m_binary)
|
||||
{
|
||||
// Send file as binary
|
||||
QByteArray headerUtf8 = header.toUtf8();
|
||||
socket->write(headerUtf8);
|
||||
socket->write(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send file as text
|
||||
QString html = QString(data);
|
||||
// Make any substitutions in the content of the file
|
||||
if (m_substitutions.contains(path)) {
|
||||
html = substitute(path, html);
|
||||
}
|
||||
QTextStream os(socket);
|
||||
os.setAutoDetectUnicode(true);
|
||||
os << header << html;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::readClient()
|
||||
{
|
||||
QTcpSocket* socket = (QTcpSocket*)sender();
|
||||
if (socket->canReadLine())
|
||||
{
|
||||
QString line = socket->readLine();
|
||||
//qDebug() << "WebServer HTTP Request: " << line;
|
||||
|
||||
QStringList tokens = QString(line).split(QRegularExpression("[ \r\n][ \r\n]*"));
|
||||
if (tokens[0] == "GET")
|
||||
{
|
||||
// Get file type from extension
|
||||
QString path = tokens[1];
|
||||
MimeType *mimeType = &m_defaultMimeType;
|
||||
int extensionIdx = path.lastIndexOf(".");
|
||||
if (extensionIdx != -1) {
|
||||
QString extension = path.mid(extensionIdx);
|
||||
if (m_mimeTypes.contains(extension)) {
|
||||
mimeType = m_mimeTypes[extension];
|
||||
}
|
||||
}
|
||||
|
||||
// Try skymapping path
|
||||
QStringList dirs = path.split('/');
|
||||
if ((dirs.length() >= 2) && m_pathSubstitutions.contains(dirs[1]))
|
||||
{
|
||||
dirs[1] = m_pathSubstitutions.value(dirs[1]);
|
||||
dirs.removeFirst();
|
||||
QString newPath = dirs.join('/');
|
||||
//qDebug() << "SkyMapping " << path << " to " << newPath;
|
||||
path = newPath;
|
||||
}
|
||||
|
||||
// See if we can find the file in our resources
|
||||
QResource res(path);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
if (res.isValid() && (res.uncompressedSize() > 0))
|
||||
{
|
||||
QByteArray data = res.uncompressedData();
|
||||
sendFile(socket, data, mimeType, path);
|
||||
}
|
||||
#else
|
||||
if (res.isValid() && (res.size() > 0))
|
||||
{
|
||||
QByteArray data = QByteArray::fromRawData((const char *)res.data(), res.size());
|
||||
if (res.isCompressed()) {
|
||||
data = qUncompress(data);
|
||||
}
|
||||
sendFile(socket, data, mimeType, path);
|
||||
}
|
||||
#endif
|
||||
else if (m_files.contains(path))
|
||||
{
|
||||
// Path is a file held in memory
|
||||
sendFile(socket, m_files.value(path).data(), mimeType, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if we can find a file on disk
|
||||
QFile file(path);
|
||||
if (file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QByteArray data = file.readAll();
|
||||
if (path.endsWith(".glbe")) {
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
data[i] = data[i] ^ 0x55;
|
||||
}
|
||||
}
|
||||
sendFile(socket, data, mimeType, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "WebServer " << path << " not found";
|
||||
// File not found
|
||||
QTextStream os(socket);
|
||||
os.setAutoDetectUnicode(true);
|
||||
os << "HTTP/1.0 404 Not Found\r\n"
|
||||
"Content-Type: text/html; charset=\"utf-8\"\r\n"
|
||||
"\r\n"
|
||||
"<html>\n"
|
||||
"<body>\n"
|
||||
"<h1>404 Not Found</h1>\n"
|
||||
"</body>\n"
|
||||
"</html>\n";
|
||||
}
|
||||
}
|
||||
|
||||
socket->close();
|
||||
|
||||
if (socket->state() == QTcpSocket::UnconnectedState) {
|
||||
delete socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::discardClient()
|
||||
{
|
||||
QTcpSocket* socket = (QTcpSocket*)sender();
|
||||
socket->deleteLater();
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_WEB_SERVER_H_
|
||||
#define INCLUDE_WEB_SERVER_H_
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
|
||||
// WebServer for making simple dynamic html pages and serving binaries from
|
||||
// resources or local disk
|
||||
class WebServer : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
struct Substitution {
|
||||
QString m_from;
|
||||
QString m_to;
|
||||
Substitution(const QString& from, const QString& to) :
|
||||
m_from(from),
|
||||
m_to(to)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct MimeType {
|
||||
QString m_type;
|
||||
bool m_binary;
|
||||
MimeType(const QString& type, bool binary=true) :
|
||||
m_type(type),
|
||||
m_binary(binary)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// Hash of a list of paths to substitute
|
||||
QHash<QString, QString> m_pathSubstitutions;
|
||||
|
||||
// Hash of path to a list of substitutions to make in the file
|
||||
QHash<QString, QList<Substitution *>*> m_substitutions;
|
||||
|
||||
// Hash of files held in memory
|
||||
QHash<QString, QByteArray> m_files;
|
||||
|
||||
// Hash of filename extension to MIME type information
|
||||
QHash<QString, MimeType *> m_mimeTypes;
|
||||
MimeType m_defaultMimeType;
|
||||
|
||||
public:
|
||||
WebServer(quint16 &port, QObject* parent = 0);
|
||||
void incomingConnection(qintptr socket) override;
|
||||
void addPathSubstitution(const QString &from, const QString &to);
|
||||
void addSubstitution(QString path, QString from, QString to);
|
||||
void addFile(const QString &path, const QByteArray &data);
|
||||
QString substitute(QString path, QString html);
|
||||
void sendFile(QTcpSocket* socket, const QByteArray &data, MimeType *mimeType, const QString &path);
|
||||
|
||||
private slots:
|
||||
void readClient();
|
||||
void discardClient();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,101 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 <QJsonDocument>
|
||||
|
||||
#include "websocketserver.h"
|
||||
|
||||
WebSocketServer::WebSocketServer(QObject *parent) :
|
||||
QObject(parent),
|
||||
m_socket("", QWebSocketServer::NonSecureMode, this),
|
||||
m_client(nullptr)
|
||||
{
|
||||
connect(&m_socket, &QWebSocketServer::newConnection, this, &WebSocketServer::onNewConnection);
|
||||
int port = 0;
|
||||
if (!m_socket.listen(QHostAddress::Any, port)) {
|
||||
qCritical() << "WebSocketServer - Unable to listen on port " << port;
|
||||
}
|
||||
}
|
||||
|
||||
quint16 WebSocketServer::serverPort()
|
||||
{
|
||||
return m_socket.serverPort();
|
||||
}
|
||||
|
||||
void WebSocketServer::onNewConnection()
|
||||
{
|
||||
QWebSocket *socket = m_socket.nextPendingConnection();
|
||||
|
||||
connect(socket, &QWebSocket::textMessageReceived, this, &WebSocketServer::processTextMessage);
|
||||
connect(socket, &QWebSocket::binaryMessageReceived, this, &WebSocketServer::processBinaryMessage);
|
||||
connect(socket, &QWebSocket::disconnected, this, &WebSocketServer::socketDisconnected);
|
||||
|
||||
m_client = socket;
|
||||
|
||||
emit connected();
|
||||
}
|
||||
|
||||
void WebSocketServer::processTextMessage(QString message)
|
||||
{
|
||||
//QWebSocket *client = qobject_cast<QWebSocket *>(sender());
|
||||
//qDebug() << "WebSocketServer::processTextMessage - Received text " << message;
|
||||
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error);
|
||||
if (!doc.isNull() && doc.isObject()) {
|
||||
emit received(doc.object());
|
||||
} else {
|
||||
qDebug() << "WebSocketServer::processTextMessage: " << error.errorString();
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketServer::processBinaryMessage(QByteArray message)
|
||||
{
|
||||
//QWebSocket *client = qobject_cast<QWebSocket *>(sender());
|
||||
// Shouldn't receive any binary messages for now
|
||||
qDebug() << "WebSocketServer::processBinaryMessage - Received binary " << message;
|
||||
}
|
||||
|
||||
void WebSocketServer::socketDisconnected()
|
||||
{
|
||||
QWebSocket *client = qobject_cast<QWebSocket *>(sender());
|
||||
if (client) {
|
||||
client->deleteLater();
|
||||
m_client = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool WebSocketServer::isConnected()
|
||||
{
|
||||
return m_client != nullptr;
|
||||
}
|
||||
|
||||
void WebSocketServer::send(const QJsonObject &obj)
|
||||
{
|
||||
if (m_client)
|
||||
{
|
||||
QJsonDocument doc(obj);
|
||||
QByteArray bytes = doc.toJson();
|
||||
qint64 bytesSent = m_client->sendTextMessage(bytes);
|
||||
m_client->flush(); // Try to reduce latency
|
||||
if (bytesSent != bytes.size()) {
|
||||
qDebug() << "WebSocketServer::update - Sent only " << bytesSent << " bytes out of " << bytes.size();
|
||||
}
|
||||
//qDebug() << obj;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2019 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_WEBSOCKERSERVER_H_
|
||||
#define INCLUDE_FEATURE_WEBSOCKERSERVER_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QWebSocketServer>
|
||||
#include <QWebSocket>
|
||||
#include <QJsonObject>
|
||||
|
||||
class WebSocketServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
QWebSocketServer m_socket;
|
||||
QWebSocket *m_client;
|
||||
|
||||
public:
|
||||
|
||||
WebSocketServer(QObject *parent = nullptr);
|
||||
quint16 serverPort();
|
||||
|
||||
bool isConnected();
|
||||
void send(const QJsonObject &obj);
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void received(const QJsonObject &obj);
|
||||
|
||||
public slots:
|
||||
|
||||
void onNewConnection();
|
||||
void processTextMessage(QString message);
|
||||
void processBinaryMessage(QByteArray message);
|
||||
void socketDisconnected();
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_MAPWEBSOCKERSERVER_H_
|
|
@ -0,0 +1,53 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 "webview.h"
|
||||
|
||||
#include <QTabWidget>
|
||||
#include <QWebEngineView>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
WebView::WebView(QWidget *parent) :
|
||||
QWebEngineView(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QWebEngineView *WebView::createWindow(QWebEnginePage::WebWindowType type)
|
||||
{
|
||||
(void) type;
|
||||
QWebEngineView *view = new QWebEngineView();
|
||||
connect(view, &QWebEngineView::titleChanged, this, &WebView::on_titleChanged);
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->addWidget(view);
|
||||
|
||||
int tab = m_tabs->addTab(view, "Web");
|
||||
m_tabs->setCurrentIndex(tab);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
void WebView::on_titleChanged(const QString& title)
|
||||
{
|
||||
QWebEngineView *view = qobject_cast<QWebEngineView *>(sender());
|
||||
for (int i = 0; i < m_tabs->count(); i++)
|
||||
{
|
||||
if (m_tabs->widget(i) == view) {
|
||||
m_tabs->setTabText(i, title);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_WEBVIEW_H_
|
||||
#define INCLUDE_FEATURE_WEBVIEW_H_
|
||||
|
||||
#include <QWebEngineView>
|
||||
|
||||
#include "websocketserver.h"
|
||||
|
||||
class QTabWidget;
|
||||
|
||||
class WebView : public QWebEngineView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
WebView(QWidget *parent = nullptr);
|
||||
|
||||
QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override;
|
||||
|
||||
void setTabs(QTabWidget *tabs) { m_tabs = tabs; }
|
||||
|
||||
private slots:
|
||||
void on_titleChanged(const QString& title);
|
||||
|
||||
private:
|
||||
QTabWidget *m_tabs;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_WEBVIEW_H_
|
|
@ -0,0 +1,105 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 "wtml.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QNetworkReply>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QDebug>
|
||||
|
||||
WTML::WTML()
|
||||
{
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
QObject::connect(m_networkManager, &QNetworkAccessManager::finished, this, &WTML::handleReply);
|
||||
}
|
||||
|
||||
WTML::~WTML()
|
||||
{
|
||||
QObject::disconnect(m_networkManager, &QNetworkAccessManager::finished, this, &WTML::handleReply);
|
||||
delete m_networkManager;
|
||||
}
|
||||
|
||||
void WTML::getData()
|
||||
{
|
||||
// https://worldwidetelescope.org/wwtweb/catalog.aspx?X=ImageSets6
|
||||
// https://worldwidetelescope.org/wwtweb/catalog.aspx?W=exploreroot6
|
||||
QUrl url(QString("https://worldwidetelescope.org/wwtweb/catalog.aspx?X=ImageSets6"));
|
||||
m_networkManager->get(QNetworkRequest(url));
|
||||
}
|
||||
|
||||
void WTML::handleReply(QNetworkReply* reply)
|
||||
{
|
||||
if (!reply->error())
|
||||
{
|
||||
QXmlStreamReader xmlReader(reply->readAll());
|
||||
|
||||
QList<ImageSet> dataSets;
|
||||
|
||||
while (!xmlReader.atEnd() && !xmlReader.hasError())
|
||||
{
|
||||
while (xmlReader.readNextStartElement())
|
||||
{
|
||||
if (xmlReader.name() == QLatin1String("Folder"))
|
||||
{
|
||||
while(xmlReader.readNextStartElement())
|
||||
{
|
||||
if (xmlReader.name() == QLatin1String("ImageSet"))
|
||||
{
|
||||
QString name = xmlReader.attributes().value("Name").toString();
|
||||
QString dataSetType = xmlReader.attributes().value("DataSetType").toString();
|
||||
|
||||
if (!name.isEmpty() && !dataSetType.isEmpty())
|
||||
{
|
||||
ImageSet imageSet;
|
||||
imageSet.m_name = name;
|
||||
imageSet.m_dataSetType = dataSetType;
|
||||
dataSets.append(imageSet);
|
||||
//qDebug() << "Adding ImageSet " << name << dataSetType;
|
||||
}
|
||||
|
||||
// Credits, Thumbnail etc
|
||||
while(xmlReader.readNextStartElement())
|
||||
{
|
||||
xmlReader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlReader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlReader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ignore "Premature end of document." here even if ok
|
||||
if (!xmlReader.atEnd() && xmlReader.hasError()) {
|
||||
qDebug() << "WTML::handleReply: Error parsing XML: " << xmlReader.errorString();
|
||||
}
|
||||
|
||||
emit dataUpdated(dataSets);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "WTML::handleReply: error: " << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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_WTML_H
|
||||
#define INCLUDE_WTML_H
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
// World Wide Telescope WTML files containing imageset catalogs
|
||||
class WTML : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
struct ImageSet {
|
||||
QString m_name;
|
||||
QString m_dataSetType;
|
||||
};
|
||||
|
||||
WTML();
|
||||
~WTML();
|
||||
|
||||
void getData();
|
||||
|
||||
public slots:
|
||||
void handleReply(QNetworkReply* reply);
|
||||
|
||||
signals:
|
||||
void dataUpdated(const QList<ImageSet>& dataSets); // Emitted when new data are available.
|
||||
|
||||
private:
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
|
||||
};
|
||||
|
||||
#endif /* INCLUDE_WTML_H */
|
|
@ -9,6 +9,7 @@ The Star Tracker feature plugin is for use in radio astronomy and EME (Earth-Moo
|
|||
* The overhead position of the Sun, Moon and selected star can be displayed on the Map Feature.
|
||||
* It can display local Sidereal time, solar flux density and sky temperature.
|
||||
* It can plot the line of sight through the Milky Way.
|
||||
* It can send the target to the Sky Map plugin, to display associated imagery in a variety of wavelengths. It can also use the Sky Map to set the target.
|
||||
* The plugin can communicate with Stellarium, allowing Stellarium to control SDRangel as though it was a telescope and for the direction the antenna is pointing to be displayed in Stellarium.
|
||||
|
||||
<h2>Settings</h2>
|
||||
|
@ -66,7 +67,7 @@ Specifies the longitude in decimal degrees (East positive) of the observation po
|
|||
|
||||
<h3>8: Time</h3>
|
||||
|
||||
Select the date and time at which the position of the target should be calculated. Select either Now, for the current time, or Custom to manually enter a date and time.
|
||||
Select the local date and time at which the position of the target should be calculated. Select either Now, for the current time, or Custom to manually enter a date and time.
|
||||
|
||||
<h3>9: LST - Local Sidereal Time</h3>
|
||||
|
||||
|
@ -100,6 +101,7 @@ To allow Stellarium to set the RA and Dec, select Custom RA/Dec, and ensure the
|
|||
| S8 | HI | IAU primary calibration region (l=207,b=-15) | Tb=72 peak |
|
||||
| S9 | HI | IAU secondary calibration region (l=356,b=-4) | Tb=85 peak |
|
||||
| SatelliteTracker | | Gets target Az/El from Satellite Tracker | |
|
||||
| SkyMap | | Gets target RA/Dec from Sky Map Tracker | |
|
||||
|
||||
References:
|
||||
|
||||
|
@ -240,6 +242,12 @@ The Star Tracker feature can send the overhead position of the Sun, Moon and tar
|
|||
|
||||
When using the Find feature in the Map GUI, you can search for "Sun", "Moon" or "Star".
|
||||
|
||||
<h2>Sky Map</h2>
|
||||
|
||||
The Star Tracker feature will send the target RA/Dec, observation point (antenna location) and antenna beamwidth to the Sky Map.
|
||||
If the Star Tracker is set as the Source plugin in the Sky Map, pressing the Track button in the Sky Map will result in the Sky Map tracking the target
|
||||
selected in the Star Tracker.
|
||||
|
||||
<h2>Stellarium Interface</h2>
|
||||
|
||||
In Star Tracker:
|
||||
|
@ -288,15 +296,15 @@ Icons are by Freepik from Flaticon https://www.flaticon.com/
|
|||
|
||||
Full details of the API can be found in the Swagger documentation. Here is a quick example of how to set the target to the Moon at the current time:
|
||||
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "StarTracker", "StarTrackerSettings": { "target": "Moon", "dateTime": "" }}'
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "StarTracker", "StarTrackerSettings": { "target": "Moon", "dateTime": "" }}'
|
||||
|
||||
Or to a custom RA and declination on a given date and time:
|
||||
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "StarTracker", "StarTrackerSettings": { "target": "Custom", "ra": "03h32m59.35s", "dec": "54d34m45.05s", "dateTime": "1921-04-15T10:17:05" }}'
|
||||
curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/feature/0/settings" -d '{"featureType": "StarTracker", "StarTrackerSettings": { "target": "Custom", "ra": "03h32m59.35s", "dec": "54d34m45.05s", "dateTime": "1921-04-15T10:17:05" }}'
|
||||
|
||||
To start tracking:
|
||||
|
||||
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/run"
|
||||
curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/feature/0/run"
|
||||
|
||||
<h2>Developer Notes</h2>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
|
@ -42,7 +42,8 @@
|
|||
MESSAGE_CLASS_DEFINITION(StarTracker::MsgConfigureStarTracker, Message)
|
||||
MESSAGE_CLASS_DEFINITION(StarTracker::MsgStartStop, Message)
|
||||
MESSAGE_CLASS_DEFINITION(StarTracker::MsgSetSolarFlux, Message)
|
||||
MESSAGE_CLASS_DEFINITION(StarTracker::MsgReportAvailableSatelliteTrackers, Message)
|
||||
MESSAGE_CLASS_DEFINITION(StarTracker::MsgRequestAvailableFeatures, Message)
|
||||
MESSAGE_CLASS_DEFINITION(StarTracker::MsgReportAvailableFeatures, Message)
|
||||
|
||||
const char* const StarTracker::m_featureIdURI = "sdrangel.feature.startracker";
|
||||
const char* const StarTracker::m_featureId = "StarTracker";
|
||||
|
@ -50,7 +51,9 @@ const char* const StarTracker::m_featureId = "StarTracker";
|
|||
StarTracker::StarTracker(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface),
|
||||
m_thread(nullptr),
|
||||
m_worker(nullptr)
|
||||
m_worker(nullptr),
|
||||
m_availableChannelHandler({"sdrangel.channel.radioastronomy"}, QStringList{"startracker.display"}),
|
||||
m_availableFeatureHandler({"sdrangel.feature.satellitetracker", "sdrangel.feature.skymap"})
|
||||
{
|
||||
qDebug("StarTracker::StarTracker: webAPIAdapterInterface: %p", webAPIAdapterInterface);
|
||||
setObjectName(m_featureId);
|
||||
|
@ -70,36 +73,17 @@ StarTracker::StarTracker(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
|||
m_temps.append(new FITS(":/startracker/startracker/408mhz_ra_dec.fits"));
|
||||
m_temps.append(new FITS(":/startracker/startracker/1420mhz_ra_dec.fits"));
|
||||
m_spectralIndex = new FITS(":/startracker/startracker/408mhz_ra_dec_spectral_index.fits");
|
||||
scanAvailableChannels();
|
||||
scanAvailableFeatures();
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
this,
|
||||
&StarTracker::handleChannelAdded
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureAdded,
|
||||
this,
|
||||
&StarTracker::handleFeatureAdded
|
||||
);
|
||||
QObject::connect(
|
||||
MainCore::instance(),
|
||||
&MainCore::featureRemoved,
|
||||
this,
|
||||
&StarTracker::handleFeatureRemoved
|
||||
);
|
||||
|
||||
QObject::connect(&m_availableChannelHandler, &AvailableChannelOrFeatureHandler::messageEnqueued, this, &StarTracker::handleChannelMessageQueue);
|
||||
m_availableChannelHandler.scanAvailableChannelsAndFeatures();
|
||||
QObject::connect(&m_availableFeatureHandler, &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged, this, &StarTracker::featuresChanged);
|
||||
m_availableFeatureHandler.scanAvailableChannelsAndFeatures();
|
||||
}
|
||||
|
||||
StarTracker::~StarTracker()
|
||||
{
|
||||
QObject::disconnect(
|
||||
MainCore::instance(),
|
||||
&MainCore::channelAdded,
|
||||
this,
|
||||
&StarTracker::handleChannelAdded
|
||||
);
|
||||
QObject::disconnect(&m_availableChannelHandler, &AvailableChannelOrFeatureHandler::messageEnqueued, this, &StarTracker::handleChannelMessageQueue);
|
||||
QObject::disconnect(&m_availableFeatureHandler, &AvailableChannelOrFeatureHandler::channelsOrFeaturesChanged, this, &StarTracker::featuresChanged);
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
|
@ -176,6 +160,11 @@ bool StarTracker::handleMessage(const Message& cmd)
|
|||
|
||||
return true;
|
||||
}
|
||||
else if (MsgRequestAvailableFeatures::match(cmd))
|
||||
{
|
||||
notifyUpdateFeatures();
|
||||
return true;
|
||||
}
|
||||
else if (MsgSetSolarFlux::match(cmd))
|
||||
{
|
||||
MsgSetSolarFlux& msg = (MsgSetSolarFlux&) cmd;
|
||||
|
@ -839,137 +828,18 @@ bool StarTracker::calcSkyTemperature(double frequency, double beamwidth, double
|
|||
}
|
||||
}
|
||||
|
||||
void StarTracker::scanAvailableChannels()
|
||||
void StarTracker::featuresChanged()
|
||||
{
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
MessagePipes& messagePipes = mainCore->getMessagePipes();
|
||||
std::vector<DeviceSet*>& deviceSets = mainCore->getDeviceSets();
|
||||
m_availableChannels.clear();
|
||||
|
||||
for (const auto& deviceSet : deviceSets)
|
||||
{
|
||||
DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine;
|
||||
|
||||
if (deviceSourceEngine)
|
||||
{
|
||||
for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++)
|
||||
{
|
||||
ChannelAPI *channel = deviceSet->getChannelAt(chi);
|
||||
|
||||
if (StarTrackerSettings::m_pipeURIs.contains(channel->getURI()) && !m_availableChannels.contains(channel))
|
||||
{
|
||||
qDebug("StarTracker::scanAvailableChannels: register %d:%d %s (%p)",
|
||||
deviceSet->getIndex(), chi, qPrintable(channel->getURI()), channel);
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(channel, this, "startracker.display");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handleChannelMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
QObject::connect(
|
||||
pipe,
|
||||
&ObjectPipe::toBeDeleted,
|
||||
this,
|
||||
&StarTracker::handleMessagePipeToBeDeleted
|
||||
);
|
||||
m_availableChannels.insert(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_availableFeatures = m_availableFeatureHandler.getAvailableChannelOrFeatureList();
|
||||
notifyUpdateFeatures();
|
||||
}
|
||||
|
||||
void StarTracker::handleChannelAdded(int deviceSetIndex, ChannelAPI *channel)
|
||||
{
|
||||
qDebug("StarTracker::handleChannelAdded: deviceSetIndex: %d:%d channel: %s (%p)",
|
||||
deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel);
|
||||
DeviceSet *deviceSet = MainCore::instance()->getDeviceSets()[deviceSetIndex];
|
||||
DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine;
|
||||
|
||||
if (deviceSourceEngine && StarTrackerSettings::m_pipeURIs.contains(channel->getURI()))
|
||||
{
|
||||
if (!m_availableChannels.contains(channel))
|
||||
{
|
||||
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(channel, this, "startracker.display");
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ this->handleChannelMessageQueue(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
QObject::connect(
|
||||
pipe,
|
||||
&ObjectPipe::toBeDeleted,
|
||||
this,
|
||||
&StarTracker::handleMessagePipeToBeDeleted
|
||||
);
|
||||
m_availableChannels.insert(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StarTracker::handleMessagePipeToBeDeleted(int reason, QObject* object)
|
||||
{
|
||||
if ((reason == 0) && m_availableChannels.contains((ChannelAPI*) object)) // producer (channel)
|
||||
{
|
||||
qDebug("StarTracker::handleMessagePipeToBeDeleted: removing channel at (%p)", object);
|
||||
m_availableChannels.remove((ChannelAPI*) object);
|
||||
}
|
||||
}
|
||||
|
||||
void StarTracker::scanAvailableFeatures()
|
||||
{
|
||||
qDebug("StarTracker::scanAvailableFeatures");
|
||||
MainCore *mainCore = MainCore::instance();
|
||||
std::vector<FeatureSet*>& featureSets = mainCore->getFeatureeSets();
|
||||
m_satelliteTrackers.clear();
|
||||
|
||||
for (const auto& featureSet : featureSets)
|
||||
{
|
||||
for (int fei = 0; fei < featureSet->getNumberOfFeatures(); fei++)
|
||||
{
|
||||
Feature *feature = featureSet->getFeatureAt(fei);
|
||||
|
||||
if (feature->getURI() == "sdrangel.feature.satellitetracker")
|
||||
{
|
||||
StarTrackerSettings::AvailableFeature satelliteTracker =
|
||||
StarTrackerSettings::AvailableFeature{featureSet->getIndex(), fei, feature->getIdentifier()};
|
||||
m_satelliteTrackers[feature] = satelliteTracker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyUpdateSatelliteTrackers();
|
||||
}
|
||||
|
||||
void StarTracker::handleFeatureAdded(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
(void) featureSetIndex;
|
||||
(void) feature;
|
||||
|
||||
scanAvailableFeatures();
|
||||
}
|
||||
|
||||
void StarTracker::handleFeatureRemoved(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
(void) featureSetIndex;
|
||||
(void) feature;
|
||||
|
||||
scanAvailableFeatures();
|
||||
}
|
||||
|
||||
void StarTracker::notifyUpdateSatelliteTrackers()
|
||||
void StarTracker::notifyUpdateFeatures()
|
||||
{
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportAvailableSatelliteTrackers *msg = MsgReportAvailableSatelliteTrackers::create();
|
||||
msg->getFeatures() = m_satelliteTrackers.values();
|
||||
MsgReportAvailableFeatures *msg = MsgReportAvailableFeatures::create();
|
||||
msg->getFeatures() = m_availableFeatures;
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020 Kacper Michajłow <kasper93@gmail.com> //
|
||||
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -22,11 +22,11 @@
|
|||
|
||||
#include <QThread>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSet>
|
||||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
#include "util/fits.h"
|
||||
#include "availablechannelorfeaturehandler.h"
|
||||
|
||||
#include "startrackersettings.h"
|
||||
|
||||
|
@ -107,20 +107,34 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
class MsgReportAvailableSatelliteTrackers : public Message {
|
||||
class MsgRequestAvailableFeatures : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QList<StarTrackerSettings::AvailableFeature>& getFeatures() { return m_availableFeatures; }
|
||||
|
||||
static MsgReportAvailableSatelliteTrackers* create() {
|
||||
return new MsgReportAvailableSatelliteTrackers();
|
||||
static MsgRequestAvailableFeatures* create() {
|
||||
return new MsgRequestAvailableFeatures();
|
||||
}
|
||||
|
||||
private:
|
||||
QList<StarTrackerSettings::AvailableFeature> m_availableFeatures;
|
||||
MsgRequestAvailableFeatures() :
|
||||
Message()
|
||||
{}
|
||||
};
|
||||
|
||||
MsgReportAvailableSatelliteTrackers() :
|
||||
class MsgReportAvailableFeatures : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
AvailableChannelOrFeatureList& getFeatures() { return m_availableFeatures; }
|
||||
|
||||
static MsgReportAvailableFeatures* create() {
|
||||
return new MsgReportAvailableFeatures();
|
||||
}
|
||||
|
||||
private:
|
||||
AvailableChannelOrFeatureList m_availableFeatures;
|
||||
|
||||
MsgReportAvailableFeatures() :
|
||||
Message()
|
||||
{}
|
||||
};
|
||||
|
@ -183,11 +197,13 @@ private:
|
|||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
QSet<ChannelAPI*> m_availableChannels;
|
||||
QHash<Feature*, StarTrackerSettings::AvailableFeature> m_satelliteTrackers;
|
||||
Weather *m_weather;
|
||||
float m_solarFlux;
|
||||
|
||||
AvailableChannelOrFeatureHandler m_availableChannelHandler;
|
||||
AvailableChannelOrFeatureHandler m_availableFeatureHandler;
|
||||
AvailableChannelOrFeatureList m_availableFeatures;
|
||||
|
||||
QList<FITS*> m_temps;
|
||||
FITS *m_spectralIndex;
|
||||
|
||||
|
@ -197,17 +213,12 @@ private:
|
|||
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const StarTrackerSettings& settings, bool force);
|
||||
void webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response);
|
||||
double applyBeam(const FITS *fits, double beamwidth, double ra, double dec, int& imgX, int& imgY) const;
|
||||
void scanAvailableChannels();
|
||||
void scanAvailableFeatures();
|
||||
void notifyUpdateSatelliteTrackers();
|
||||
void notifyUpdateFeatures();
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
void weatherUpdated(float temperature, float pressure, float humidity);
|
||||
void handleChannelAdded(int deviceSetIndex, ChannelAPI *channel);
|
||||
void handleMessagePipeToBeDeleted(int reason, QObject* object);
|
||||
void handleFeatureAdded(int featureSetIndex, Feature *feature);
|
||||
void handleFeatureRemoved(int featureSetIndex, Feature *feature);
|
||||
void featuresChanged();
|
||||
void handleChannelMessageQueue(MessageQueue* messageQueue);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
|
@ -220,31 +220,31 @@ bool StarTrackerGUI::handleMessage(const Message& message)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
else if (StarTracker::MsgReportAvailableSatelliteTrackers::match(message))
|
||||
else if (StarTracker::MsgReportAvailableFeatures::match(message))
|
||||
{
|
||||
StarTracker::MsgReportAvailableSatelliteTrackers& report = (StarTracker::MsgReportAvailableSatelliteTrackers&) message;
|
||||
updateSatelliteTrackerList(report.getFeatures());
|
||||
StarTracker::MsgReportAvailableFeatures& report = (StarTracker::MsgReportAvailableFeatures&) message;
|
||||
updateFeatureList(report.getFeatures());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void StarTrackerGUI::updateSatelliteTrackerList(const QList<StarTrackerSettings::AvailableFeature>& satelliteTrackers)
|
||||
void StarTrackerGUI::updateFeatureList(const AvailableChannelOrFeatureList& features)
|
||||
{
|
||||
// Update list of satellite trackers
|
||||
// Update list of plugins we can get target from
|
||||
ui->target->blockSignals(true);
|
||||
|
||||
// Remove Satellite Trackers no longer available
|
||||
// Remove targets no longer available
|
||||
for (int i = 0; i < ui->target->count(); )
|
||||
{
|
||||
QString text = ui->target->itemText(i);
|
||||
bool found = false;
|
||||
if (text.contains("SatelliteTracker"))
|
||||
if (text.contains("SatelliteTracker") || text.contains("SkyMap"))
|
||||
{
|
||||
for (const auto& satelliteTracker : satelliteTrackers)
|
||||
for (const auto& feature : features)
|
||||
{
|
||||
if (satelliteTracker.getName() == text)
|
||||
if (feature.getLongId() == text)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
|
@ -262,16 +262,16 @@ void StarTrackerGUI::updateSatelliteTrackerList(const QList<StarTrackerSettings:
|
|||
}
|
||||
}
|
||||
|
||||
// Add new Satellite Trackers
|
||||
for (const auto& satelliteTracker : satelliteTrackers)
|
||||
// Add new targets
|
||||
for (const auto& feature : features)
|
||||
{
|
||||
QString name = satelliteTracker.getName();
|
||||
QString name = feature.getLongId();
|
||||
if (ui->target->findText(name) == -1) {
|
||||
ui->target->addItem(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Satellite Tracker feature can be created after this plugin, so select it
|
||||
// Features can be created after this plugin, so select it
|
||||
// if the chosen tracker appears
|
||||
int index = ui->target->findText(m_settings.m_target);
|
||||
if (index >= 0) {
|
||||
|
@ -454,6 +454,9 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet,
|
|||
|
||||
createGalacticLineOfSightScene();
|
||||
plotChart();
|
||||
|
||||
StarTracker::MsgRequestAvailableFeatures *message = StarTracker::MsgRequestAvailableFeatures::create();
|
||||
m_starTracker->getInputMessageQueue()->push(message);
|
||||
}
|
||||
|
||||
StarTrackerGUI::~StarTrackerGUI()
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "util/fits.h"
|
||||
#include "gui/httpdownloadmanagergui.h"
|
||||
#include "settings/rollupstate.h"
|
||||
#include "availablechannelorfeature.h"
|
||||
|
||||
#include "startrackersettings.h"
|
||||
|
||||
|
@ -169,7 +170,7 @@ private:
|
|||
void updateSolarFlux(bool all);
|
||||
void makeUIConnections();
|
||||
void limitAzElRange(double& azimuth, double& elevation) const;
|
||||
void updateSatelliteTrackerList(const QList<StarTrackerSettings::AvailableFeature>& satelliteTrackers);
|
||||
void updateFeatureList(const AvailableChannelOrFeatureList& features);
|
||||
|
||||
private slots:
|
||||
void onMenuDialogCalled(const QPoint &p);
|
||||
|
|
|
@ -141,10 +141,10 @@
|
|||
<string>Antenna half power (-3dB) beamwidth (degrees)</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.100000000000000</double>
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>360.000000000000000</double>
|
||||
|
|
|
@ -23,14 +23,6 @@
|
|||
|
||||
#include "startrackersettings.h"
|
||||
|
||||
const QStringList StarTrackerSettings::m_pipeTypes = {
|
||||
QStringLiteral("RadioAstronomy")
|
||||
};
|
||||
|
||||
const QStringList StarTrackerSettings::m_pipeURIs = {
|
||||
QStringLiteral("sdrangel.channel.radioastronomy")
|
||||
};
|
||||
|
||||
StarTrackerSettings::StarTrackerSettings() :
|
||||
m_rollupState(nullptr)
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2015-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
||||
// Copyright (C) 2020-2021, 2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// Copyright (C) 2020-2024 Jon Beniston, M7RCE <jon@beniston.com> //
|
||||
// //
|
||||
// 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 //
|
||||
|
@ -30,29 +30,12 @@ class Serializable;
|
|||
|
||||
struct StarTrackerSettings
|
||||
{
|
||||
struct AvailableFeature
|
||||
{
|
||||
int m_featureSetIndex;
|
||||
int m_featureIndex;
|
||||
QString m_type;
|
||||
|
||||
AvailableFeature() = default;
|
||||
AvailableFeature(const AvailableFeature&) = default;
|
||||
AvailableFeature& operator=(const AvailableFeature&) = default;
|
||||
bool operator==(const AvailableFeature& a) const {
|
||||
return (m_featureSetIndex == a.m_featureSetIndex) && (m_featureIndex == a.m_featureIndex) && (m_type == a.m_type);
|
||||
}
|
||||
QString getName() const {
|
||||
return QString("F%1:%2 %3").arg(m_featureSetIndex).arg(m_featureIndex).arg(m_type);
|
||||
}
|
||||
};
|
||||
|
||||
QString m_ra;
|
||||
QString m_dec;
|
||||
double m_latitude;
|
||||
double m_longitude;
|
||||
QString m_target; // Sun, Moon, Custom
|
||||
QString m_dateTime; // Date/time for observation, or "" for now
|
||||
QString m_dateTime; // Local date/time for observation, or "" for now
|
||||
QString m_refraction; // Refraction correction. "None", "Saemundsson" or "Positional Astronomy Library"
|
||||
double m_pressure; // Air pressure in millibars
|
||||
double m_temperature; // Air temperature in C
|
||||
|
@ -102,9 +85,6 @@ struct StarTrackerSettings
|
|||
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
|
||||
void applySettings(const QStringList& settingsKeys, const StarTrackerSettings& settings);
|
||||
QString getDebugString(const QStringList& settingsKeys, bool force=false) const;
|
||||
|
||||
static const QStringList m_pipeTypes;
|
||||
static const QStringList m_pipeURIs;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_STARTRACKERSETTINGS_H_
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "SWGTargetAzimuthElevation.h"
|
||||
#include "SWGMapItem.h"
|
||||
#include "SWGStarTrackerTarget.h"
|
||||
#include "SWGSkyMapTarget.h"
|
||||
|
||||
#include "webapi/webapiadapterinterface.h"
|
||||
#include "webapi/webapiutils.h"
|
||||
|
@ -384,7 +385,7 @@ void StarTrackerWorker::updateRaDec(RADec rd, QDateTime dt, bool lbTarget)
|
|||
// Send to Stellarium
|
||||
writeStellariumTarget(rdJ2000.ra, rdJ2000.dec);
|
||||
// Send to GUI
|
||||
if (m_settings.m_target == "Sun" || m_settings.m_target == "Moon" || (m_settings.m_target == "Custom Az/El") || lbTarget || m_settings.m_target.contains("SatelliteTracker"))
|
||||
if (m_settings.m_target == "Sun" || m_settings.m_target == "Moon" || (m_settings.m_target == "Custom Az/El") || lbTarget || m_settings.m_target.contains("SatelliteTracker") || m_settings.m_target.contains("SkyMap"))
|
||||
{
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
|
@ -517,6 +518,26 @@ void StarTrackerWorker::update()
|
|||
else
|
||||
qDebug() << "StarTrackerWorker::update - Failed to parse feature name " << m_settings.m_target;
|
||||
}
|
||||
if (m_settings.m_target.contains("SkyMap"))
|
||||
{
|
||||
// Get RA/Dec from Sky Map
|
||||
double ra, dec;
|
||||
unsigned int featureSetIndex,featureIndex;
|
||||
|
||||
if (MainCore::getFeatureIndexFromId(m_settings.m_target, featureSetIndex, featureIndex))
|
||||
{
|
||||
if (ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "ra", ra)
|
||||
&& ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "dec", dec))
|
||||
{
|
||||
m_settings.m_ra = QString::number(ra, 'f', 10);
|
||||
m_settings.m_dec = QString::number(dec, 'f', 10);
|
||||
}
|
||||
else
|
||||
qDebug() << "StarTrackerWorker::update - Failed to target from feature " << m_settings.m_target;
|
||||
}
|
||||
else
|
||||
qDebug() << "StarTrackerWorker::update - Failed to parse feature name " << m_settings.m_target;
|
||||
}
|
||||
|
||||
if (m_settings.m_target == "Sun")
|
||||
{
|
||||
|
@ -654,6 +675,33 @@ void StarTrackerWorker::update()
|
|||
}
|
||||
}
|
||||
|
||||
// Send RA/Dec, position, beamwidth and date to Sky Map
|
||||
QList<ObjectPipe*> skyMapPipes;
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(m_starTracker, "skymap.target", skyMapPipes);
|
||||
for (const auto& pipe : skyMapPipes)
|
||||
{
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
SWGSDRangel::SWGSkyMapTarget *swgTarget = new SWGSDRangel::SWGSkyMapTarget();
|
||||
if (m_settings.m_jnow)
|
||||
{
|
||||
double jd = Astronomy::julianDate(dt);
|
||||
RADec rdJ2000 = Astronomy::precess(rd, jd, Astronomy::jd_j2000());
|
||||
swgTarget->setRa(rdJ2000.ra);
|
||||
swgTarget->setDec(rdJ2000.dec);
|
||||
}
|
||||
else
|
||||
{
|
||||
swgTarget->setRa(rd.ra);
|
||||
swgTarget->setDec(rd.dec);
|
||||
}
|
||||
swgTarget->setLatitude(m_settings.m_latitude);
|
||||
swgTarget->setLongitude(m_settings.m_longitude);
|
||||
swgTarget->setAltitude(m_settings.m_heightAboveSeaLevel);
|
||||
swgTarget->setDateTime(new QString(dt.toString(Qt::ISODateWithMs)));
|
||||
swgTarget->setHpbw(m_settings.m_beamwidth);
|
||||
messageQueue->push(MainCore::MsgSkyMapTarget::create(m_starTracker, swgTarget));
|
||||
}
|
||||
|
||||
// Send to Map
|
||||
if (m_settings.m_drawSunOnMap || m_settings.m_drawMoonOnMap || m_settings.m_drawStarOnMap)
|
||||
{
|
||||
|
|
|
@ -286,6 +286,8 @@ set(sdrbase_SOURCES
|
|||
|
||||
websockets/wsspectrum.cpp
|
||||
|
||||
availablechannelorfeature.cpp
|
||||
availablechannelorfeaturehandler.cpp
|
||||
mainparser.cpp
|
||||
maincore.cpp
|
||||
remotetcpsinkstarter.cpp
|
||||
|
@ -525,6 +527,8 @@ set(sdrbase_HEADERS
|
|||
|
||||
websockets/wsspectrum.h
|
||||
|
||||
availablechannelorfeature.h
|
||||
availablechannelorfeaturehandler.h
|
||||
mainparser.h
|
||||
maincore.h
|
||||
remotetcpsinkstarter.h
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston <jon@beniston.com> //
|
||||
// //
|
||||
// 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 "availablechannelorfeature.h"
|
||||
#include "feature/feature.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "maincore.h"
|
||||
|
||||
int AvailableChannelOrFeatureList::indexOfObject(const QObject *object, int from) const
|
||||
{
|
||||
for (int index = from; index < size(); index++)
|
||||
{
|
||||
if (at(index).m_object == object) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int AvailableChannelOrFeatureList::indexOfId(const QString& id, int from) const
|
||||
{
|
||||
for (int index = from; index < size(); index++)
|
||||
{
|
||||
if (at(index).getId() == id) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int AvailableChannelOrFeatureList::indexOfLongId(const QString& longId, int from) const
|
||||
{
|
||||
for (int index = from; index < size(); index++)
|
||||
{
|
||||
if (at(index).getLongId() == longId) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston <jon@beniston.com> //
|
||||
// //
|
||||
// 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 SDRBASE_AVAILABLECHANNELORFEATURE_H_
|
||||
#define SDRBASE_AVAILABLECHANNELORFEATURE_H_
|
||||
|
||||
#include "pipes/messagepipes.h"
|
||||
#include "export.h"
|
||||
|
||||
struct AvailableChannelOrFeature
|
||||
{
|
||||
QChar m_kind; //!< 'R' or 'T' for channel, 'M' for MIMO channel, 'F' for feature as from MainCore::getDeviceSetTypeId
|
||||
int m_superIndex; //!< Device Set index or Feature Set index
|
||||
int m_index; //!< Channel or Feature index
|
||||
int m_streamIndex; //!< For MIMO channels only
|
||||
QString m_type; //!< Plugin type (E.g. NFMDemod)
|
||||
QObject *m_object; //!< Pointer to the object (ChannelAPI or Feature object)
|
||||
|
||||
AvailableChannelOrFeature() = default;
|
||||
AvailableChannelOrFeature(const AvailableChannelOrFeature&) = default;
|
||||
AvailableChannelOrFeature& operator=(const AvailableChannelOrFeature&) = default;
|
||||
|
||||
bool operator==(const AvailableChannelOrFeature& a) const {
|
||||
return (m_kind == a.m_kind) && (m_superIndex == a.m_superIndex) && (m_index == a.m_index) && (m_type == a.m_type) && ((m_kind == 'M') && (m_streamIndex == a.m_streamIndex));
|
||||
}
|
||||
|
||||
QString getId() const { // Eg: "R3:4"
|
||||
QString id = QString("%1%2:%3").arg(m_kind).arg(m_superIndex).arg(m_index);
|
||||
if (m_kind == 'M') {
|
||||
id.append(QString(".%1").arg(m_streamIndex));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
QString getLongId() const { // Eg: "F0:1 StarTracker"
|
||||
return QString("%1 %2").arg(getId()).arg(m_type);
|
||||
}
|
||||
};
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
inline uint qHash(const AvailableChannelOrFeature &c, uint seed = 0) noexcept
|
||||
{
|
||||
return qHash(c.getLongId(), seed);
|
||||
}
|
||||
#else
|
||||
inline size_t qHash(const AvailableChannelOrFeature &c, size_t seed = 0) noexcept
|
||||
{
|
||||
return qHash(c.getLongId(), seed);
|
||||
}
|
||||
#endif
|
||||
|
||||
class SDRBASE_API AvailableChannelOrFeatureList : public QList<AvailableChannelOrFeature>
|
||||
{
|
||||
public:
|
||||
AvailableChannelOrFeatureList() {}
|
||||
inline explicit AvailableChannelOrFeatureList(const AvailableChannelOrFeature &i) { append(i); }
|
||||
|
||||
int indexOfObject(const QObject *object, int from=0) const; //!< // Find index of entry containing specified object. -1 if not found.
|
||||
int indexOfId(const QString& longId, int from=0) const; //!< // Find index of entry with specified Id. -1 if not found.
|
||||
int indexOfLongId(const QString& longId, int from=0) const; //!< // Find index of entry with specified long Id. -1 if not found.
|
||||
};
|
||||
|
||||
#endif // SDRBASE_AVAILABLECHANNELORFEATURE_H_
|
|
@ -0,0 +1,175 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston <jon@beniston.com> //
|
||||
// //
|
||||
// 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 "availablechannelorfeaturehandler.h"
|
||||
#include "feature/feature.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "maincore.h"
|
||||
|
||||
void AvailableChannelOrFeatureHandler::init()
|
||||
{
|
||||
QObject::connect(MainCore::instance(), &MainCore::channelAdded, this, &AvailableChannelOrFeatureHandler::handleChannelAdded);
|
||||
QObject::connect(MainCore::instance(), &MainCore::channelRemoved, this, &AvailableChannelOrFeatureHandler::handleChannelRemoved);
|
||||
QObject::connect(MainCore::instance(), &MainCore::featureAdded, this, &AvailableChannelOrFeatureHandler::handleFeatureAdded);
|
||||
QObject::connect(MainCore::instance(), &MainCore::featureRemoved, this, &AvailableChannelOrFeatureHandler::handleFeatureRemoved);
|
||||
// Don't call scanAvailableChannelsAndFeatures() here, as channelsOrFeaturesChanged slot will not yet be connected
|
||||
// Owner should call scanAvailableChannelsAndFeatures after connection
|
||||
}
|
||||
|
||||
void AvailableChannelOrFeatureHandler::scanAvailableChannelsAndFeatures()
|
||||
{
|
||||
// Get current list of channels and features with specified URIs
|
||||
AvailableChannelOrFeatureList availableChannelOrFeatureList = MainCore::instance()->getAvailableChannelsAndFeatures(m_uris, m_kinds);
|
||||
|
||||
// Look for new channels or features
|
||||
for (const auto& channelOrFeature : availableChannelOrFeatureList)
|
||||
{
|
||||
if (!m_availableChannelOrFeatureList.contains(channelOrFeature))
|
||||
{
|
||||
// For MIMO channels, get notified when stream index changes
|
||||
if (channelOrFeature.m_kind == 'M')
|
||||
{
|
||||
ChannelAPI *channel = qobject_cast<ChannelAPI *>(channelOrFeature.m_object);
|
||||
if (channel) {
|
||||
QObject::connect(channel, &ChannelAPI::streamIndexChanged, this, &AvailableChannelOrFeatureHandler::handleStreamIndexChanged);
|
||||
}
|
||||
}
|
||||
// Register pipes for any new channels or features
|
||||
for (const auto& pipeName: m_pipeNames) {
|
||||
registerPipe(pipeName, channelOrFeature.m_object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if list has changed
|
||||
bool changes = m_availableChannelOrFeatureList != availableChannelOrFeatureList;
|
||||
|
||||
// Check to see if anything has been renamed, due to indexes changing after device/channel/feature removal
|
||||
// or if stream index was changed
|
||||
QStringList renameFrom;
|
||||
QStringList renameTo;
|
||||
for (const auto& channelOrFeature : availableChannelOrFeatureList)
|
||||
{
|
||||
int index = m_availableChannelOrFeatureList.indexOfObject(channelOrFeature.m_object);
|
||||
if (index >= 0)
|
||||
{
|
||||
const AvailableChannelOrFeature& oldEntry = m_availableChannelOrFeatureList.at(index);
|
||||
if ((oldEntry.m_superIndex != channelOrFeature.m_superIndex)
|
||||
|| (oldEntry.m_index != channelOrFeature.m_index)
|
||||
|| ((channelOrFeature.m_kind == 'M') && (oldEntry.m_streamIndex != channelOrFeature.m_streamIndex))
|
||||
)
|
||||
{
|
||||
renameFrom.append(oldEntry.getId());
|
||||
renameTo.append(channelOrFeature.getId());
|
||||
renameFrom.append(oldEntry.getLongId());
|
||||
renameTo.append(channelOrFeature.getLongId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_availableChannelOrFeatureList = availableChannelOrFeatureList;
|
||||
|
||||
// Signal if list has changed
|
||||
if (changes) {
|
||||
emit channelsOrFeaturesChanged(renameFrom, renameTo);
|
||||
}
|
||||
}
|
||||
|
||||
QObject* AvailableChannelOrFeatureHandler::registerPipes(const QString& longIdFrom, const QStringList& pipeNames)
|
||||
{
|
||||
int index = m_availableChannelOrFeatureList.indexOfLongId(longIdFrom);
|
||||
if (index >= 0)
|
||||
{
|
||||
QObject *object = m_availableChannelOrFeatureList[index].m_object;
|
||||
for (const auto& pipeName : pipeNames) {
|
||||
registerPipe(pipeName, object);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AvailableChannelOrFeatureHandler::deregisterPipes(QObject* from, const QStringList& pipeNames)
|
||||
{
|
||||
// Don't dereference 'from' here, as it may have been deleted
|
||||
if (from)
|
||||
{
|
||||
qDebug("AvailableChannelOrFeatureHandler::deregisterPipes: unregister (%p)", from);
|
||||
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
for (const auto& pipeName : pipeNames) {
|
||||
messagePipes.unregisterProducerToConsumer(from, this, pipeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvailableChannelOrFeatureHandler::registerPipe(const QString& pipeName, QObject *channelOrFeature)
|
||||
{
|
||||
qDebug("MessagePipeHandler::registerPipe: register %s (%p)", qPrintable(channelOrFeature->objectName()), channelOrFeature);
|
||||
MessagePipes& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
|
||||
ObjectPipe *pipe = messagePipes.registerProducerToConsumer(channelOrFeature, this, pipeName);
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
QObject::connect(
|
||||
messageQueue,
|
||||
&MessageQueue::messageEnqueued,
|
||||
this,
|
||||
[=](){ emit messageEnqueued(messageQueue); },
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
}
|
||||
|
||||
void AvailableChannelOrFeatureHandler::handleChannelAdded(int deviceSetIndex, ChannelAPI *channel)
|
||||
{
|
||||
(void) deviceSetIndex;
|
||||
(void) channel;
|
||||
|
||||
scanAvailableChannelsAndFeatures();
|
||||
}
|
||||
|
||||
void AvailableChannelOrFeatureHandler::handleChannelRemoved(int deviceSetIndex, ChannelAPI *channel)
|
||||
{
|
||||
(void) deviceSetIndex;
|
||||
(void) channel;
|
||||
|
||||
scanAvailableChannelsAndFeatures();
|
||||
}
|
||||
|
||||
void AvailableChannelOrFeatureHandler::handleStreamIndexChanged(int streamIndex)
|
||||
{
|
||||
(void) streamIndex;
|
||||
|
||||
scanAvailableChannelsAndFeatures();
|
||||
}
|
||||
|
||||
void AvailableChannelOrFeatureHandler::handleFeatureAdded(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
(void) featureSetIndex;
|
||||
(void) feature;
|
||||
|
||||
scanAvailableChannelsAndFeatures();
|
||||
}
|
||||
|
||||
void AvailableChannelOrFeatureHandler::handleFeatureRemoved(int featureSetIndex, Feature *feature)
|
||||
{
|
||||
(void) featureSetIndex;
|
||||
(void) feature;
|
||||
|
||||
scanAvailableChannelsAndFeatures();
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2024 Jon Beniston <jon@beniston.com> //
|
||||
// //
|
||||
// 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 SDRBASE_AVAILABLECHANNELORFEATUREHANDLER_H_
|
||||
#define SDRBASE_AVAILABLECHANNELORFEATUREHANDLER_H_
|
||||
|
||||
#include "pipes/messagepipes.h"
|
||||
#include "availablechannelorfeature.h"
|
||||
#include "export.h"
|
||||
|
||||
class ChannelAPI;
|
||||
class Feature;
|
||||
|
||||
// Utility class to help keeping track of list of available channels or features and optionally register pipes to them
|
||||
class SDRBASE_API AvailableChannelOrFeatureHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
// Use this constructor to just keep track of available channels and features with specified URIs and kinds
|
||||
AvailableChannelOrFeatureHandler(QStringList uris, const QString& kinds = "RTMF") :
|
||||
m_uris(uris),
|
||||
m_kinds(kinds)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
// Use this constructor to keep track of available channels and features with specified URIs and kinds and register pipes with the given names to them
|
||||
AvailableChannelOrFeatureHandler(QStringList uris, QStringList pipeNames, const QString& kinds = "RTMF") :
|
||||
m_uris(uris),
|
||||
m_pipeNames(pipeNames),
|
||||
m_kinds(kinds)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void scanAvailableChannelsAndFeatures();
|
||||
|
||||
const AvailableChannelOrFeatureList& getAvailableChannelOrFeatureList() const {
|
||||
return m_availableChannelOrFeatureList;
|
||||
}
|
||||
|
||||
QObject* registerPipes(const QString& longIdFrom, const QStringList& pipeNames);
|
||||
void deregisterPipes(QObject* from, const QStringList& pipeNames);
|
||||
|
||||
private:
|
||||
|
||||
AvailableChannelOrFeatureList m_availableChannelOrFeatureList;
|
||||
|
||||
QStringList m_uris; //!< URIs of channels/features we want to create a list for
|
||||
QStringList m_pipeNames; //!< List of pipe names to register
|
||||
QString m_kinds;
|
||||
|
||||
void init();
|
||||
void registerPipe(const QString& pipeName, QObject *channelOrFeature);
|
||||
|
||||
private slots:
|
||||
|
||||
void handleChannelAdded(int deviceSetIndex, ChannelAPI *channel);
|
||||
void handleChannelRemoved(int deviceSetIndex, ChannelAPI *channel);
|
||||
void handleStreamIndexChanged(int streamIndex);
|
||||
void handleFeatureAdded(int featureSetIndex, Feature *feature);
|
||||
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 messageEnqueued(MessageQueue *messageQueue); //!< Emitted when message enqueued to a pipe
|
||||
|
||||
};
|
||||
|
||||
#endif // SDRBASE_AVAILABLECHANNELORFEATUREHANDLER_H_
|
|
@ -71,6 +71,7 @@ MESSAGE_CLASS_DEFINITION(MainCore::MsgTargetAzimuthElevation, Message)
|
|||
MESSAGE_CLASS_DEFINITION(MainCore::MsgStarTrackerTarget, Message)
|
||||
MESSAGE_CLASS_DEFINITION(MainCore::MsgStarTrackerDisplaySettings, Message)
|
||||
MESSAGE_CLASS_DEFINITION(MainCore::MsgStarTrackerDisplayLoSSettings, Message)
|
||||
MESSAGE_CLASS_DEFINITION(MainCore::MsgSkyMapTarget, Message)
|
||||
|
||||
MainCore::MainCore()
|
||||
{
|
||||
|
@ -435,6 +436,80 @@ void MainCore::updateWakeLock()
|
|||
}
|
||||
#endif
|
||||
|
||||
AvailableChannelOrFeatureList MainCore::getAvailableChannels(const QStringList& uris)
|
||||
{
|
||||
AvailableChannelOrFeatureList list;
|
||||
|
||||
for (const auto deviceSet : m_deviceSets)
|
||||
{
|
||||
for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++)
|
||||
{
|
||||
ChannelAPI* channel = deviceSet->getChannelAt(chi);
|
||||
|
||||
if ((uris.size() == 0) || uris.contains(channel->getURI()))
|
||||
{
|
||||
QChar type = getDeviceSetTypeId(deviceSet);
|
||||
int streamIdx = type == 'M' ? channel->getStreamIndex() : -1;
|
||||
|
||||
AvailableChannelOrFeature item {
|
||||
type,
|
||||
deviceSet->getIndex(),
|
||||
chi,
|
||||
streamIdx,
|
||||
channel->getIdentifier(),
|
||||
channel
|
||||
};
|
||||
list.append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
AvailableChannelOrFeatureList MainCore::getAvailableFeatures(const QStringList& uris)
|
||||
{
|
||||
AvailableChannelOrFeatureList list;
|
||||
std::vector<FeatureSet*>& featureSets = MainCore::instance()->getFeatureeSets();
|
||||
|
||||
for (const auto& featureSet : featureSets)
|
||||
{
|
||||
for (int fei = 0; fei < featureSet->getNumberOfFeatures(); fei++)
|
||||
{
|
||||
Feature *feature = featureSet->getFeatureAt(fei);
|
||||
|
||||
if ((uris.size() == 0) || uris.contains(feature->getURI()))
|
||||
{
|
||||
AvailableChannelOrFeature item {
|
||||
'F',
|
||||
featureSet->getIndex(),
|
||||
fei,
|
||||
-1,
|
||||
feature->getIdentifier(),
|
||||
feature
|
||||
};
|
||||
list.append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
AvailableChannelOrFeatureList MainCore::getAvailableChannelsAndFeatures(const QStringList& uris, const QString& kinds)
|
||||
{
|
||||
AvailableChannelOrFeatureList list;
|
||||
|
||||
if (kinds != "F") {
|
||||
list.append(getAvailableChannels(uris));
|
||||
}
|
||||
if (kinds.contains("F")) {
|
||||
list.append(getAvailableFeatures(uris));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QChar MainCore::getDeviceSetTypeId(const DeviceSet* deviceSet)
|
||||
{
|
||||
if (deviceSet->m_deviceMIMOEngine) {
|
||||
|
@ -461,9 +536,11 @@ QString MainCore::getChannelId(const ChannelAPI* channel)
|
|||
DeviceSet* deviceSet = deviceSets[channel->getDeviceSetIndex()];
|
||||
QString deviceSetId = getDeviceSetId(deviceSet);
|
||||
int index = channel->getIndexInDeviceSet();
|
||||
// FIXME: if (deviceSet->m_deviceMIMOEngine) {
|
||||
// we should append stream index. E.g. "M0:0.0" However, only ChannelGUI seems to know what it is
|
||||
return QString("%1:%2").arg(deviceSetId).arg(index);
|
||||
if (deviceSet->m_deviceMIMOEngine) {
|
||||
return QString("%1:%2.%3").arg(deviceSetId).arg(index).arg(channel->getStreamIndex());
|
||||
} else {
|
||||
return QString("%1:%2").arg(deviceSetId).arg(index);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList MainCore::getDeviceSetIds(bool rx, bool tx, bool mimo)
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "pipes/messagepipes.h"
|
||||
#include "pipes/datapipes.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "availablechannelorfeature.h"
|
||||
|
||||
class DeviceSet;
|
||||
class FeatureSet;
|
||||
|
@ -59,6 +60,7 @@ namespace SWGSDRangel
|
|||
class SWGStarTrackerTarget;
|
||||
class SWGStarTrackerDisplaySettings;
|
||||
class SWGStarTrackerDisplayLoSSettings;
|
||||
class SWGSkyMapTarget;
|
||||
}
|
||||
|
||||
class SDRBASE_API MainCore : public QObject
|
||||
|
@ -842,7 +844,28 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
class SDRBASE_API MsgSkyMapTarget : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const QObject *getPipeSource() const { return m_pipeSource; }
|
||||
SWGSDRangel::SWGSkyMapTarget *getSWGSkyMapTarget() const { return m_swgSkyMapTarget; }
|
||||
|
||||
static MsgSkyMapTarget* create(const QObject *pipeSource, SWGSDRangel::SWGSkyMapTarget *swgSkyMapTarget)
|
||||
{
|
||||
return new MsgSkyMapTarget(pipeSource, swgSkyMapTarget);
|
||||
}
|
||||
|
||||
private:
|
||||
const QObject *m_pipeSource;
|
||||
SWGSDRangel::SWGSkyMapTarget *m_swgSkyMapTarget;
|
||||
|
||||
MsgSkyMapTarget(const QObject *pipeSource, SWGSDRangel::SWGSkyMapTarget *swgSkyMapTarget) :
|
||||
Message(),
|
||||
m_pipeSource(pipeSource),
|
||||
m_swgSkyMapTarget(swgSkyMapTarget)
|
||||
{ }
|
||||
};
|
||||
|
||||
MainCore();
|
||||
~MainCore();
|
||||
|
@ -886,6 +909,11 @@ public:
|
|||
// Position
|
||||
const QGeoPositionInfo& getPosition() const;
|
||||
|
||||
// Lists of available channels and features. List should be ordered by indexes. Plugins should use AvailableChannelOrFeatureHandler to maintain this list
|
||||
AvailableChannelOrFeatureList getAvailableChannels(const QStringList& uris); // Get list of available channels with given URIs or all if empty list.
|
||||
AvailableChannelOrFeatureList getAvailableFeatures(const QStringList& uris); // Get list of available features with given URIs or all if empty list.
|
||||
AvailableChannelOrFeatureList getAvailableChannelsAndFeatures(const QStringList& uris, const QString& kinds); // Get list of available channels and features with given URIs or all if empty list.
|
||||
|
||||
// Ids
|
||||
QChar getDeviceSetTypeId(const DeviceSet* deviceSet); //!< Get Type Id (E.g. 'R', 'T' or 'M') for the given device set
|
||||
QString getDeviceSetId(const DeviceSet* deviceSet); //!< Get Id (E.g. "R2") for the given device set
|
||||
|
|
|
@ -6249,6 +6249,9 @@ margin-bottom: 20px;
|
|||
"SimplePTTActions" : {
|
||||
"$ref" : "#/definitions/SimplePTTActions"
|
||||
},
|
||||
"SkyMapActions" : {
|
||||
"$ref" : "#/definitions/SkyMapActions"
|
||||
},
|
||||
"StarTrackerActions" : {
|
||||
"$ref" : "#/definitions/StarTrackerActions"
|
||||
},
|
||||
|
@ -6394,6 +6397,9 @@ margin-bottom: 20px;
|
|||
"SimplePTTReport" : {
|
||||
"$ref" : "#/definitions/SimplePTTReport"
|
||||
},
|
||||
"SkyMapReport" : {
|
||||
"$ref" : "#/definitions/SkyMapReport"
|
||||
},
|
||||
"StarTrackerReport" : {
|
||||
"$ref" : "#/definitions/StarTrackerReport"
|
||||
},
|
||||
|
@ -6495,12 +6501,15 @@ margin-bottom: 20px;
|
|||
"SatelliteTrackerSettings" : {
|
||||
"$ref" : "#/definitions/SatelliteTrackerSettings"
|
||||
},
|
||||
"StarTrackerSettings" : {
|
||||
"$ref" : "#/definitions/StarTrackerSettings"
|
||||
},
|
||||
"SimplePTTSettings" : {
|
||||
"$ref" : "#/definitions/SimplePTTSettings"
|
||||
},
|
||||
"SkyMapSettings" : {
|
||||
"$ref" : "#/definitions/SkyMapSettings"
|
||||
},
|
||||
"StarTrackerSettings" : {
|
||||
"$ref" : "#/definitions/StarTrackerSettings"
|
||||
},
|
||||
"VORLocalizerSettings" : {
|
||||
"$ref" : "#/definitions/VORLocalizerSettings"
|
||||
}
|
||||
|
@ -14618,6 +14627,236 @@ margin-bottom: 20px;
|
|||
}
|
||||
},
|
||||
"description" : "Simple PTT settings"
|
||||
};
|
||||
defs.SkyMapActions = {
|
||||
"properties" : {
|
||||
"find" : {
|
||||
"type" : "string",
|
||||
"description" : "The name of the object or the coordinates to centre the sky map on"
|
||||
}
|
||||
},
|
||||
"description" : "Sky Map"
|
||||
};
|
||||
defs.SkyMapReport = {
|
||||
"properties" : {
|
||||
"dateTime" : {
|
||||
"type" : "string",
|
||||
"description" : "Current date and time being used for sky map"
|
||||
},
|
||||
"ra" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Right ascension of target (In decimal hours, J2000)"
|
||||
},
|
||||
"dec" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Declination of target (In decimal degrees, J2000)"
|
||||
},
|
||||
"latitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Latitude in decimal degrees (North positive) of observation/antenna location"
|
||||
},
|
||||
"longitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Longitude in decimal degrees (East positive) of observation/antenna location"
|
||||
},
|
||||
"altitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Altitude in metres of observation/antenna location"
|
||||
},
|
||||
"azimuth" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "The azimuth angle in degrees to the target"
|
||||
},
|
||||
"elevation" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "The elevation angle in degrees to the target"
|
||||
},
|
||||
"fov" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Current field-of-view in degrees"
|
||||
}
|
||||
},
|
||||
"description" : "Sky Map"
|
||||
};
|
||||
defs.SkyMapSettings = {
|
||||
"properties" : {
|
||||
"displayNames" : {
|
||||
"type" : "integer",
|
||||
"description" : "Display names on the sky map (1 for yes, 0 for no)"
|
||||
},
|
||||
"displayConstellations" : {
|
||||
"type" : "integer",
|
||||
"description" : "Display constellations on the sky map (1 for yes, 0 for no)"
|
||||
},
|
||||
"displayReticle" : {
|
||||
"type" : "integer",
|
||||
"description" : "Display reticle on the sky map (1 for yes, 0 for no)"
|
||||
},
|
||||
"displayGrid" : {
|
||||
"type" : "integer",
|
||||
"description" : "Display grid on the sky map (1 for yes, 0 for no)"
|
||||
},
|
||||
"displayAntennaFoV" : {
|
||||
"type" : "integer",
|
||||
"description" : "Display antenna field-of-view on the sky map (1 for yes, 0 for no)"
|
||||
},
|
||||
"map" : {
|
||||
"type" : "string",
|
||||
"description" : "WWT, ESASky or Aladin"
|
||||
},
|
||||
"background" : {
|
||||
"type" : "string",
|
||||
"description" : "Name of background image set"
|
||||
},
|
||||
"projection" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"source" : {
|
||||
"type" : "string",
|
||||
"description" : "Channel or Feature plugin to get target from"
|
||||
},
|
||||
"track" : {
|
||||
"type" : "integer",
|
||||
"description" : "Whether to track the specified source plugin"
|
||||
},
|
||||
"latitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Latitude in decimal degrees (North positive) of observation/antenna location"
|
||||
},
|
||||
"longitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Longitude in decimal degrees (East positive) of observation/antenna location"
|
||||
},
|
||||
"altitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Altitude in metres of observation/antenna location"
|
||||
},
|
||||
"hpbw" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Antenna half-power beam width in degrees"
|
||||
},
|
||||
"useMyPosition" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Use My Position instead of latitude/longitude settings"
|
||||
},
|
||||
"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" : "Sky Map"
|
||||
};
|
||||
defs.SkyMapTarget = {
|
||||
"properties" : {
|
||||
"ra" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Right ascension of target (In decimal hours, J2000)"
|
||||
},
|
||||
"dec" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Declination of target (In decimal degrees, J2000)"
|
||||
},
|
||||
"latitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Latitude in decimal degrees (North positive) of observation/antenna location"
|
||||
},
|
||||
"longitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Longitude in decimal degrees (East positive) of observation/antenna location"
|
||||
},
|
||||
"altitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Altitude in metres of observation/antenna location"
|
||||
},
|
||||
"hpbw" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Antenna half-power beam width in degrees"
|
||||
},
|
||||
"dateTime" : {
|
||||
"type" : "string",
|
||||
"description" : "Date and time of observation. ISO 8601 extended format: yyyy-MM-ddTHH:mm:ss with Z suffix for UTC. Empty string for current time."
|
||||
}
|
||||
},
|
||||
"description" : "Sky Map target. Sent by other plugins to skymap.target message queue."
|
||||
};
|
||||
defs.SkyMapTarget_2 = {
|
||||
"properties" : {
|
||||
"ra" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Right ascension of target (In decimal hours, J2000)"
|
||||
},
|
||||
"dec" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Declination of target (In decimal degrees, J2000)"
|
||||
},
|
||||
"latitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Latitude in decimal degrees (North positive) of observation/antenna location"
|
||||
},
|
||||
"longitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Longitude in decimal degrees (East positive) of observation/antenna location"
|
||||
},
|
||||
"altitude" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Altitude in metres of observation/antenna location"
|
||||
},
|
||||
"hpbw" : {
|
||||
"type" : "number",
|
||||
"format" : "float",
|
||||
"description" : "Antenna half-power beam width in degrees"
|
||||
},
|
||||
"dateTime" : {
|
||||
"type" : "string",
|
||||
"description" : "Date and time of observation. ISO 8601 extended format: yyyy-MM-ddTHH:mm:ss with Z suffix for UTC. Empty string for current time."
|
||||
}
|
||||
},
|
||||
"description" : "Sky Map target. Sent by other plugins to skymap.target message queue."
|
||||
};
|
||||
defs.SoapySDRFrequencySetting = {
|
||||
"properties" : {
|
||||
|
@ -58429,7 +58668,7 @@ except ApiException as e:
|
|||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2024-01-21T13:22:02.160+01:00
|
||||
Generated 2024-02-12T10:33:45.606+01:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -31,6 +31,8 @@ FeatureActions:
|
|||
$ref: "/doc/swagger/include/SatelliteTracker.yaml#/SatelliteTrackerActions"
|
||||
SimplePTTActions:
|
||||
$ref: "/doc/swagger/include/SimplePTT.yaml#/SimplePTTActions"
|
||||
SkyMapActions:
|
||||
$ref: "/doc/swagger/include/SkyMap.yaml#/SkyMapActions"
|
||||
StarTrackerActions:
|
||||
$ref: "/doc/swagger/include/StarTracker.yaml#/StarTrackerActions"
|
||||
VORLocalizerActions:
|
||||
|
|
|
@ -25,6 +25,8 @@ FeatureReport:
|
|||
$ref: "/doc/swagger/include/SatelliteTracker.yaml#/SatelliteTrackerReport"
|
||||
SimplePTTReport:
|
||||
$ref: "/doc/swagger/include/SimplePTT.yaml#/SimplePTTReport"
|
||||
SkyMapReport:
|
||||
$ref: "/doc/swagger/include/SkyMap.yaml#/SkyMapReport"
|
||||
StarTrackerReport:
|
||||
$ref: "/doc/swagger/include/StarTracker.yaml#/StarTrackerReport"
|
||||
VORLocalizerReport:
|
||||
|
|
|
@ -41,9 +41,11 @@ FeatureSettings:
|
|||
$ref: "/doc/swagger/include/RigCtlServer.yaml#/RigCtlServerSettings"
|
||||
SatelliteTrackerSettings:
|
||||
$ref: "/doc/swagger/include/SatelliteTracker.yaml#/SatelliteTrackerSettings"
|
||||
StarTrackerSettings:
|
||||
$ref: "/doc/swagger/include/StarTracker.yaml#/StarTrackerSettings"
|
||||
SimplePTTSettings:
|
||||
$ref: "/doc/swagger/include/SimplePTT.yaml#/SimplePTTSettings"
|
||||
SkyMapSettings:
|
||||
$ref: "/doc/swagger/include/SkyMap.yaml#/SkyMapSettings"
|
||||
StarTrackerSettings:
|
||||
$ref: "/doc/swagger/include/StarTracker.yaml#/StarTrackerSettings"
|
||||
VORLocalizerSettings:
|
||||
$ref: "/doc/swagger/include/VORLocalizer.yaml#/VORLocalizerSettings"
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
SkyMapSettings:
|
||||
description: Sky Map
|
||||
properties:
|
||||
displayNames:
|
||||
description: "Display names on the sky map (1 for yes, 0 for no)"
|
||||
type: integer
|
||||
displayConstellations:
|
||||
description: "Display constellations on the sky map (1 for yes, 0 for no)"
|
||||
type: integer
|
||||
displayReticle:
|
||||
description: "Display reticle on the sky map (1 for yes, 0 for no)"
|
||||
type: integer
|
||||
displayGrid:
|
||||
description: "Display grid on the sky map (1 for yes, 0 for no)"
|
||||
type: integer
|
||||
displayAntennaFoV:
|
||||
description: "Display antenna field-of-view on the sky map (1 for yes, 0 for no)"
|
||||
type: integer
|
||||
map:
|
||||
description: "WWT, ESASky or Aladin"
|
||||
type: string
|
||||
background:
|
||||
description: "Name of background image set"
|
||||
type: string
|
||||
projection:
|
||||
type: string
|
||||
source:
|
||||
description: "Channel or Feature plugin to get target from"
|
||||
type: string
|
||||
track:
|
||||
description: "Whether to track the specified source plugin"
|
||||
type: integer
|
||||
latitude:
|
||||
description: "Latitude in decimal degrees (North positive) of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
longitude:
|
||||
description: "Longitude in decimal degrees (East positive) of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
altitude:
|
||||
description: "Altitude in metres of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
hpbw:
|
||||
description: "Antenna half-power beam width in degrees"
|
||||
type: number
|
||||
format: float
|
||||
useMyPosition:
|
||||
description: "Use My Position instead of latitude/longitude settings"
|
||||
type: number
|
||||
format: float
|
||||
title:
|
||||
type: string
|
||||
rgbColor:
|
||||
type: integer
|
||||
useReverseAPI:
|
||||
description: Synchronize with reverse API (1 for yes, 0 for no)
|
||||
type: integer
|
||||
reverseAPIAddress:
|
||||
type: string
|
||||
reverseAPIPort:
|
||||
type: integer
|
||||
reverseAPIFeatureSetIndex:
|
||||
type: integer
|
||||
reverseAPIFeatureIndex:
|
||||
type: integer
|
||||
rollupState:
|
||||
$ref: "/doc/swagger/include/RollupState.yaml#/RollupState"
|
||||
|
||||
SkyMapReport:
|
||||
description: Sky Map
|
||||
properties:
|
||||
dateTime:
|
||||
description: "Current date and time being used for sky map"
|
||||
type: string
|
||||
ra:
|
||||
description: "Right ascension of target (In decimal hours, J2000)"
|
||||
type: number
|
||||
format: float
|
||||
dec:
|
||||
description: "Declination of target (In decimal degrees, J2000)"
|
||||
type: number
|
||||
format: float
|
||||
latitude:
|
||||
description: "Latitude in decimal degrees (North positive) of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
longitude:
|
||||
description: "Longitude in decimal degrees (East positive) of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
altitude:
|
||||
description: "Altitude in metres of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
azimuth:
|
||||
description: "The azimuth angle in degrees to the target"
|
||||
type: number
|
||||
format: float
|
||||
elevation:
|
||||
description: "The elevation angle in degrees to the target"
|
||||
type: number
|
||||
format: float
|
||||
fov:
|
||||
description: "Current field-of-view in degrees"
|
||||
type: number
|
||||
format: float
|
||||
|
||||
SkyMapActions:
|
||||
description: Sky Map
|
||||
properties:
|
||||
find:
|
||||
description: "The name of the object or the coordinates to centre the sky map on"
|
||||
type: string
|
||||
|
||||
SkyMapTarget:
|
||||
description: "Sky Map target. Sent by other plugins to skymap.target message queue."
|
||||
properties:
|
||||
ra:
|
||||
description: "Right ascension of target (In decimal hours, J2000)"
|
||||
type: number
|
||||
format: float
|
||||
dec:
|
||||
description: "Declination of target (In decimal degrees, J2000)"
|
||||
type: number
|
||||
format: float
|
||||
latitude:
|
||||
description: "Latitude in decimal degrees (North positive) of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
longitude:
|
||||
description: "Longitude in decimal degrees (East positive) of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
altitude:
|
||||
description: "Altitude in metres of observation/antenna location"
|
||||
type: number
|
||||
format: float
|
||||
hpbw:
|
||||
description: "Antenna half-power beam width in degrees"
|
||||
type: number
|
||||
format: float
|
||||
dateTime:
|
||||
description: "Date and time of observation. ISO 8601 extended format: yyyy-MM-ddTHH:mm:ss with Z suffix for UTC. Empty string for current time."
|
||||
type: string
|
|
@ -3263,6 +3263,10 @@ definitions:
|
|||
StarTrackerDisplayLoSSettings:
|
||||
$ref: "/doc/swagger/include/StarTracker.yaml#/StarTrackerDisplayLoSSettings"
|
||||
|
||||
# Sky Map target passed from Star Tracker plugin
|
||||
SkyMapTarget:
|
||||
$ref: "/doc/swagger/include/SkyMap.yaml#/SkyMapTarget"
|
||||
|
||||
# This isn't in GS232Controller, as it may eventually be used by other controllers or features
|
||||
TargetAzimuthElevation:
|
||||
description: "A target azimuth and elevation"
|
||||
|
|
|
@ -339,6 +339,43 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
// Try to convert a string to Right Ascension (RA) and Declination. Returns false if not recognised format.
|
||||
// This supports HMS/DMS or decimal.
|
||||
// E.g.:
|
||||
// 12 05 12.23 +17 06 21.0
|
||||
// 12:05:12.23 -17:06:21.0
|
||||
// 12h05m12.23s +17d06m21.0s
|
||||
// 107.1324 -34.233
|
||||
static bool stringToRADec(const QString& string, float& ra, float& dec)
|
||||
{
|
||||
QRegExp dms("([0-9]+)[ :h]([0-9]+)[ :m]([0-9]+(\\.[0-9]+)?)s? *,? *([+-]?[0-9]+)[ :d]([0-9]+)[ :m]([0-9]+(\\.[0-9]+)?)s?");
|
||||
if (dms.exactMatch(string))
|
||||
{
|
||||
int raHours = dms.capturedTexts()[1].toInt();
|
||||
int raMins = dms.capturedTexts()[2].toInt();
|
||||
float raSecs = dms.capturedTexts()[3].toFloat();
|
||||
ra = raHours + raMins / 60.0f + raSecs / (60.0f * 60.0f);
|
||||
qDebug() << ra << raHours << raMins << raSecs;
|
||||
int decDegs = dms.capturedTexts()[5].toInt();
|
||||
int decMins = dms.capturedTexts()[6].toInt();
|
||||
float decSecs = dms.capturedTexts()[7].toFloat();
|
||||
bool neg = decDegs < 0;
|
||||
dec = abs(decDegs) + decMins / 60.0f + decSecs / (60.0f * 60.0f);
|
||||
if (neg) {
|
||||
dec = -dec;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
QRegExp decimal("([0-9]+(\\.[0-9]+)?) *,? *([+-]?[0-9]+(\\.[0-9]+)?)");
|
||||
if (decimal.exactMatch(string))
|
||||
{
|
||||
ra = decimal.capturedTexts()[1].toFloat();
|
||||
dec = decimal.capturedTexts()[3].toFloat();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static float solarFluxUnitsToJansky(float sfu)
|
||||
{
|
||||
return sfu * 10000.0f;
|
||||
|
|
|
@ -5273,6 +5273,11 @@ bool WebAPIRequestMapper::getFeatureSettings(
|
|||
featureSettings->setSimplePttSettings(new SWGSDRangel::SWGSimplePTTSettings());
|
||||
featureSettings->getSimplePttSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (featureSettingsKey == "SkyMapSettings")
|
||||
{
|
||||
featureSettings->setSkyMapSettings(new SWGSDRangel::SWGSkyMapSettings());
|
||||
featureSettings->getSkyMapSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (featureSettingsKey == "StarTrackerSettings")
|
||||
{
|
||||
featureSettings->setStarTrackerSettings(new SWGSDRangel::SWGStarTrackerSettings());
|
||||
|
@ -5366,6 +5371,11 @@ bool WebAPIRequestMapper::getFeatureActions(
|
|||
featureActions->setSimplePttActions(new SWGSDRangel::SWGSimplePTTActions());
|
||||
featureActions->getSimplePttActions()->fromJsonObject(actionsJsonObject);
|
||||
}
|
||||
else if (featureActionsKey == "SkyMapActions")
|
||||
{
|
||||
featureActions->setSkyMapActions(new SWGSDRangel::SWGSkyMapActions());
|
||||
featureActions->getSkyMapActions()->fromJsonObject(actionsJsonObject);
|
||||
}
|
||||
else if (featureActionsKey == "StarTrackerActions")
|
||||
{
|
||||
featureActions->setStarTrackerActions(new SWGSDRangel::SWGStarTrackerActions());
|
||||
|
@ -5376,7 +5386,7 @@ bool WebAPIRequestMapper::getFeatureActions(
|
|||
featureActions->setVorLocalizerActions(new SWGSDRangel::SWGVORLocalizerActions());
|
||||
featureActions->getVorLocalizerActions()->fromJsonObject(actionsJsonObject);
|
||||
}
|
||||
else if (featureActionsKey == "DemodAnalyzerActions")
|
||||
else if (featureActionsKey == "DemodAnalyzerActions")
|
||||
{
|
||||
featureActions->setDemodAnalyzerActions(new SWGSDRangel::SWGDemodAnalyzerActions());
|
||||
featureActions->getDemodAnalyzerActions()->fromJsonObject(actionsJsonObject);
|
||||
|
@ -5623,6 +5633,7 @@ void WebAPIRequestMapper::resetFeatureSettings(SWGSDRangel::SWGFeatureSettings&
|
|||
featureSettings.setPerTesterSettings(nullptr);
|
||||
featureSettings.setSatelliteTrackerSettings(nullptr);
|
||||
featureSettings.setSimplePttSettings(nullptr);
|
||||
featureSettings.setSkyMapSettings(nullptr);
|
||||
featureSettings.setStarTrackerSettings(nullptr);
|
||||
featureSettings.setRadiosondeSettings(nullptr);
|
||||
featureSettings.setRigCtlServerSettings(nullptr);
|
||||
|
@ -5639,6 +5650,7 @@ void WebAPIRequestMapper::resetFeatureReport(SWGSDRangel::SWGFeatureReport& feat
|
|||
featureReport.setMapReport(nullptr);
|
||||
featureReport.setSatelliteTrackerReport(nullptr);
|
||||
featureReport.setSimplePttReport(nullptr);
|
||||
featureReport.setSkyMapReport(nullptr);
|
||||
featureReport.setStarTrackerReport(nullptr);
|
||||
featureReport.setVorLocalizerReport(nullptr);
|
||||
}
|
||||
|
@ -5654,6 +5666,7 @@ void WebAPIRequestMapper::resetFeatureActions(SWGSDRangel::SWGFeatureActions& fe
|
|||
featureActions.setRigCtlServerActions(nullptr);
|
||||
featureActions.setSatelliteTrackerActions(nullptr);
|
||||
featureActions.setSimplePttActions(nullptr);
|
||||
featureActions.setSkyMapActions(nullptr);
|
||||
featureActions.setStarTrackerActions(nullptr);
|
||||
featureActions.setVorLocalizerActions(nullptr);
|
||||
}
|
||||
|
|
|
@ -320,6 +320,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureTypeToSettingsKey = {
|
|||
{"RigCtlServer", "RigCtlServerSettings"},
|
||||
{"SatelliteTracker", "SatelliteTrackerSettings"},
|
||||
{"SimplePTT", "SimplePTTSettings"},
|
||||
{"SkyMap", "SkyMapSettings"},
|
||||
{"StarTracker", "StarTrackerSettings"},
|
||||
{"VORLocalizer", "VORLocalizerSettings"}
|
||||
};
|
||||
|
@ -334,6 +335,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureTypeToActionsKey = {
|
|||
{"RigCtlServer", "RigCtlServerActions"},
|
||||
{"SatelliteTracker", "SatelliteTrackerActions"},
|
||||
{"SimplePTT", "SimplePTTActions"},
|
||||
{"SkyMap", "SkyMapActions"},
|
||||
{"StarTracker", "StarTrackerActions"},
|
||||
{"VORLocalizer", "VORLocalizerActions"},
|
||||
{"DemodAnalyzer", "DemodAnalyzerActions"}
|
||||
|
@ -355,6 +357,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureURIToSettingsKey = {
|
|||
{"sdrangel.feature.rigctlserver", "RigCtlServerSettings"},
|
||||
{"sdrangel.feature.satellitetracker", "SatelliteTrackerSettings"},
|
||||
{"sdrangel.feature.simpleptt", "SimplePTTSettings"},
|
||||
{"sdrangel.feature.skymap", "SkyMapSettings"},
|
||||
{"sdrangel.feature.startracker", "StarTrackerSettings"},
|
||||
{"sdrangel.feature.vorlocalizer", "VORLocalizerSettings"}
|
||||
};
|
||||
|
|
|
@ -417,3 +417,8 @@ void FeatureGUI::setDisplayedame(const QString& name)
|
|||
m_displayedName = name;
|
||||
m_indexLabel->setToolTip(tr("%1").arg(m_displayedName));
|
||||
}
|
||||
|
||||
void FeatureGUI::setStatusText(const QString& text)
|
||||
{
|
||||
m_statusLabel->setText(text);
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ public:
|
|||
void setIndex(int index);
|
||||
int getIndex() const { return m_featureIndex; }
|
||||
void setDisplayedame(const QString& name);
|
||||
void setStatusText(const QString& text);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
QValidator::State Int64Validator::validate(QString& input, int &pos) const
|
||||
{
|
||||
(void) pos;
|
||||
|
||||
if (input == "") {
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ FeatureActions:
|
|||
$ref: "http://swgserver:8081/api/swagger/include/SatelliteTracker.yaml#/SatelliteTrackerActions"
|
||||
SimplePTTActions:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/SimplePTT.yaml#/SimplePTTActions"
|
||||
SkyMapActions:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/SkyMap.yaml#/SkyMapActions"
|
||||
StarTrackerActions:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/StarTracker.yaml#/StarTrackerActions"
|
||||
VORLocalizerActions:
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue