mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-10-08 08:10:35 -04:00
QMessageBox::information() and friends build the box on the stack, parent it to the calling widget and run a nested event loop in exec(). If the parent is closed while the box is open, which the Web API can do at any time, the parent deletes the box and its children and the heap is corrupted. MessageDialog offers the same calls, but the box is heap allocated, given Qt::WA_DeleteOnClose and shown rather than exec'd, so nothing runs a nested event loop and the box owns its own lifetime. A box with the same text as one already up is raised instead of a second being created. The trade off is that information(), warning() and critical() return immediately and cannot report which button was pressed. question() takes a callback for the cases that need the answer. Converts the call sites in the device, channel and feature GUIs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1725 lines
61 KiB
C++
1725 lines
61 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
|
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
|
// Copyright (C) 2021 DreamNik <dreamnik@mail.ru> //
|
|
// //
|
|
// 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 <cmath>
|
|
#include <QMessageBox>
|
|
#include <QLineEdit>
|
|
|
|
#include <QtCharts/QChartView>
|
|
#include <QtCharts/QLineSeries>
|
|
#include <QtCharts/QDateTimeAxis>
|
|
#include <QtCharts/QValueAxis>
|
|
|
|
#include "channel/channelwebapiutils.h"
|
|
#include "feature/featureset.h"
|
|
#include "feature/featureuiset.h"
|
|
#include "feature/featureutils.h"
|
|
#include "feature/featurewebapiutils.h"
|
|
#include "gui/messagedialog.h"
|
|
#include "gui/basicfeaturesettingsdialog.h"
|
|
#include "gui/dialogpositioner.h"
|
|
#include "util/units.h"
|
|
#include "device/deviceapi.h"
|
|
#include "device/deviceset.h"
|
|
#include "maincore.h"
|
|
|
|
#include "ui_satellitetrackergui.h"
|
|
#include "satellitetracker.h"
|
|
#include "satellitetrackergui.h"
|
|
#include "satellitetrackerreport.h"
|
|
#include "satellitetrackersettingsdialog.h"
|
|
#include "satelliteselectiondialog.h"
|
|
#include "satelliteradiocontroldialog.h"
|
|
#include "satellitetrackersgp4.h"
|
|
|
|
SatelliteTrackerGUI* SatelliteTrackerGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
|
|
{
|
|
SatelliteTrackerGUI* gui = new SatelliteTrackerGUI(pluginAPI, featureUISet, feature);
|
|
return gui;
|
|
}
|
|
|
|
void SatelliteTrackerGUI::destroy()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
void SatelliteTrackerGUI::resetToDefaults()
|
|
{
|
|
m_settings.resetToDefaults();
|
|
displaySettings();
|
|
applySettings(true);
|
|
}
|
|
|
|
QByteArray SatelliteTrackerGUI::serialize() const
|
|
{
|
|
return m_settings.serialize();
|
|
}
|
|
|
|
bool SatelliteTrackerGUI::deserialize(const QByteArray& data)
|
|
{
|
|
if (m_settings.deserialize(data))
|
|
{
|
|
m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
|
|
updateSelectedSats();
|
|
displaySettings();
|
|
applySettings(true);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
resetToDefaults();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
QString SatelliteTrackerGUI::convertDegreesToText(double degrees)
|
|
{
|
|
if (m_settings.m_azElUnits == SatelliteTrackerSettings::DMS)
|
|
return Units::decimalDegreesToDegreeMinutesAndSeconds(degrees);
|
|
else if (m_settings.m_azElUnits == SatelliteTrackerSettings::DM)
|
|
return Units::decimalDegreesToDegreesAndMinutes(degrees);
|
|
else if (m_settings.m_azElUnits == SatelliteTrackerSettings::D)
|
|
return Units::decimalDegreesToDegrees(degrees);
|
|
else
|
|
return QString("%1").arg(degrees, 0, 'f', 2);
|
|
}
|
|
|
|
bool SatelliteTrackerGUI::handleMessage(const Message& message)
|
|
{
|
|
if (SatelliteTracker::MsgConfigureSatelliteTracker::match(message))
|
|
{
|
|
qDebug("SatelliteTrackerGUI::handleMessage: SatelliteTracker::MsgConfigureSatelliteTracker");
|
|
const SatelliteTracker::MsgConfigureSatelliteTracker& cfg = (SatelliteTracker::MsgConfigureSatelliteTracker&) message;
|
|
|
|
if (cfg.getForce()) {
|
|
m_settings = cfg.getSettings();
|
|
} else {
|
|
m_settings.applySettings(cfg.getSettingsKeys(), cfg.getSettings());
|
|
}
|
|
|
|
blockApplySettings(true);
|
|
displaySettings();
|
|
blockApplySettings(false);
|
|
|
|
return true;
|
|
}
|
|
else if (SatelliteTrackerReport::MsgReportSat::match(message))
|
|
{
|
|
SatelliteTrackerReport::MsgReportSat& satReport = (SatelliteTrackerReport::MsgReportSat&) message;
|
|
SatelliteState *satState = satReport.getSatelliteState();
|
|
|
|
if (satState->m_name == m_settings.m_target)
|
|
{
|
|
delete m_targetSatState;
|
|
m_targetSatState = satState;
|
|
|
|
ui->azimuth->setText(convertDegreesToText(satState->m_azimuth));
|
|
ui->elevation->setText(convertDegreesToText(satState->m_elevation));
|
|
plotChart();
|
|
|
|
if (satState->m_passes.size() > 0)
|
|
{
|
|
const SatellitePass &pass = satState->m_passes[0];
|
|
bool geostationary = !pass.m_aos.isValid() && !pass.m_los.isValid();
|
|
|
|
if ((m_nextTargetAOS != pass.m_aos) || (m_nextTargetLOS != pass.m_los) || (geostationary != m_geostationarySatVisible))
|
|
{
|
|
m_nextTargetAOS = pass.m_aos;
|
|
m_nextTargetLOS = pass.m_los;
|
|
m_geostationarySatVisible = geostationary;
|
|
updateTimeToAOS();
|
|
}
|
|
}
|
|
}
|
|
|
|
updateTable(satState);
|
|
|
|
if (satState->m_name != m_settings.m_target) {
|
|
delete satState;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (SatelliteTrackerReport::MsgReportAOS::match(message))
|
|
{
|
|
SatelliteTrackerReport::MsgReportAOS& aosReport = (SatelliteTrackerReport::MsgReportAOS&) message;
|
|
aos(aosReport.getSpeech());
|
|
return true;
|
|
}
|
|
else if (SatelliteTrackerReport::MsgReportTarget::match(message))
|
|
{
|
|
SatelliteTrackerReport::MsgReportTarget& targetReport = (SatelliteTrackerReport::MsgReportTarget&) message;
|
|
setTarget(targetReport.getName());
|
|
return true;
|
|
}
|
|
else if (SatelliteTrackerReport::MsgReportLOS::match(message))
|
|
{
|
|
SatelliteTrackerReport::MsgReportLOS& losReport = (SatelliteTrackerReport::MsgReportLOS&) message;
|
|
los(losReport.getSpeech());
|
|
return true;
|
|
}
|
|
else if (SatelliteTracker::MsgSatData::match(message))
|
|
{
|
|
SatelliteTracker::MsgSatData& satData = (SatelliteTracker::MsgSatData&) message;
|
|
m_satellites = satData.getSatellites();
|
|
// Remove satellites that no longer exist
|
|
QMutableListIterator<QString> itr(m_settings.m_satellites);
|
|
|
|
while (itr.hasNext())
|
|
{
|
|
QString satellite = itr.next();
|
|
if (!m_satellites.contains(satellite))
|
|
itr.remove();
|
|
}
|
|
|
|
if (!m_satellites.contains(m_settings.m_target)) {
|
|
setTarget("");
|
|
}
|
|
|
|
// Update GUI
|
|
updateSelectedSats();
|
|
|
|
return true;
|
|
}
|
|
else if (SatelliteTracker::MsgError::match(message))
|
|
{
|
|
SatelliteTracker::MsgError& errorMsg = (SatelliteTracker::MsgError&) message;
|
|
QString error = errorMsg.getError();
|
|
MessageDialog::critical(this, "Satellite Tracker", error);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Call when m_settings.m_satellites changes
|
|
void SatelliteTrackerGUI::updateSelectedSats()
|
|
{
|
|
// Remove unselects sats from target combo and table
|
|
for (int i = 0; i < ui->target->count(); )
|
|
{
|
|
QString name = ui->target->itemText(i);
|
|
int idx = m_settings.m_satellites.indexOf(name);
|
|
|
|
if (idx == -1)
|
|
{
|
|
ui->target->removeItem(i);
|
|
QList<QTableWidgetItem *> matches = ui->satTable->findItems(name, Qt::MatchExactly);
|
|
|
|
for (int j = 0; j < matches.length(); j++) {
|
|
ui->satTable->removeRow(ui->satTable->row(matches[j]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// Add new satellites to target combo
|
|
for (int i = 0; i < m_settings.m_satellites.size(); i++)
|
|
{
|
|
if (ui->target->findText(m_settings.m_satellites[i], Qt::MatchExactly) == -1) {
|
|
ui->target->addItem(m_settings.m_satellites[i]);
|
|
}
|
|
}
|
|
// Select current target, if it still exists
|
|
int idx = ui->target->findText(m_settings.m_target);
|
|
|
|
if (idx != -1) {
|
|
ui->target->setCurrentIndex(idx);
|
|
} else {
|
|
setTarget("");
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::handleInputMessages()
|
|
{
|
|
Message* message;
|
|
|
|
while ((message = getInputMessageQueue()->pop()))
|
|
{
|
|
if (handleMessage(*message)) {
|
|
delete message;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
|
{
|
|
(void) widget;
|
|
(void) rollDown;
|
|
|
|
RollupContents *rollupContents = getRollupContents();
|
|
rollupContents->saveState(m_rollupState);
|
|
}
|
|
|
|
SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
|
|
FeatureGUI(parent),
|
|
ui(new Ui::SatelliteTrackerGUI),
|
|
m_pluginAPI(pluginAPI),
|
|
m_featureUISet(featureUISet),
|
|
m_doApplySettings(true),
|
|
m_lastFeatureState(0),
|
|
m_lastUpdatingSatData(false),
|
|
m_targetSatState(nullptr),
|
|
m_plotPass(0),
|
|
m_lineChart(nullptr),
|
|
m_polarChart(nullptr),
|
|
m_geostationarySatVisible(false)
|
|
{
|
|
m_feature = feature;
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
m_helpURL = "plugins/feature/satellitetracker/readme.md";
|
|
RollupContents *rollupContents = getRollupContents();
|
|
ui->setupUi(rollupContents);
|
|
rollupContents->arrangeRollups();
|
|
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
|
|
|
m_satelliteTracker = reinterpret_cast<SatelliteTracker*>(feature);
|
|
m_satelliteTracker->setMessageQueueToGUI(&m_inputMessageQueue);
|
|
|
|
m_settings.setRollupState(&m_rollupState);
|
|
|
|
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
|
|
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
|
|
|
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
|
m_statusTimer.start(1000);
|
|
|
|
connect(&m_redrawTimer, &QTimer::timeout, this, &SatelliteTrackerGUI::plotChart);
|
|
|
|
// Initialise charts
|
|
m_emptyChart.layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_emptyChart.setMargins(QMargins(1, 1, 1, 1));
|
|
ui->passChart->setChart(&m_emptyChart);
|
|
ui->passChart->setRenderHint(QPainter::Antialiasing);
|
|
|
|
ui->dateTime->setDateTime(m_satelliteTracker->currentDateTime());
|
|
ui->deviceFeatureSelect->setVisible(false);
|
|
|
|
resizeTable();
|
|
// Allow user to reorder columns
|
|
ui->satTable->horizontalHeader()->setSectionsMovable(true);
|
|
// Allow user to sort table by clicking on headers
|
|
ui->satTable->setSortingEnabled(true);
|
|
// Add context menu to allow hiding/showing of columns
|
|
menu = new QMenu(ui->satTable);
|
|
|
|
for (int i = 0; i < ui->satTable->horizontalHeader()->count(); i++)
|
|
{
|
|
QString text = ui->satTable->horizontalHeaderItem(i)->text();
|
|
menu->addAction(createCheckableItem(text, i, true));
|
|
}
|
|
|
|
ui->satTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->satTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(columnSelectMenu(QPoint)));
|
|
// Get signals when columns change
|
|
connect(ui->satTable->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(satTable_sectionMoved(int, int, int)));
|
|
connect(ui->satTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(satTable_sectionResized(int, int, int)));
|
|
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
|
m_speech = new QTextToSpeech(this);
|
|
#endif
|
|
|
|
displaySettings();
|
|
applySettings(true);
|
|
makeUIConnections();
|
|
m_resizer.enableChildMouseTracking();
|
|
|
|
// Get initial list of satellites
|
|
on_updateSatData_clicked();
|
|
|
|
// Use My Position from preferences, if none set
|
|
if ((m_settings.m_latitude == 0.0) && (m_settings.m_longitude == 0.0)) {
|
|
on_useMyPosition_clicked();
|
|
}
|
|
}
|
|
|
|
SatelliteTrackerGUI::~SatelliteTrackerGUI()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void SatelliteTrackerGUI::setWorkspaceIndex(int index)
|
|
{
|
|
m_settings.m_workspaceIndex = index;
|
|
m_settingsKeys.append("workspaceIndex");
|
|
m_feature->setWorkspaceIndex(index);
|
|
}
|
|
|
|
void SatelliteTrackerGUI::blockApplySettings(bool block)
|
|
{
|
|
m_doApplySettings = !block;
|
|
}
|
|
|
|
void SatelliteTrackerGUI::displaySettings()
|
|
{
|
|
setTitleColor(m_settings.m_rgbColor);
|
|
setWindowTitle(m_settings.m_title);
|
|
setTitle(m_settings.m_title);
|
|
blockApplySettings(true);
|
|
ui->latitude->setValue(m_settings.m_latitude);
|
|
ui->longitude->setValue(m_settings.m_longitude);
|
|
|
|
ui->target->blockSignals(true);
|
|
ui->target->clear();
|
|
for (const QString& s : m_settings.m_satellites) {
|
|
ui->target->addItem(s);
|
|
}
|
|
ui->target->blockSignals(false);
|
|
ui->target->setCurrentIndex(ui->target->findText(m_settings.m_target));
|
|
|
|
ui->dateTime->setDateTime(QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs));
|
|
ui->dateTimeSelect->setCurrentIndex((int)m_settings.m_dateTimeSelect);
|
|
ui->dateTime->setVisible(m_settings.m_dateTimeSelect == SatelliteTrackerSettings::CUSTOM);
|
|
ui->autoTarget->setChecked(m_settings.m_autoTarget);
|
|
ui->darkTheme->setChecked(m_settings.m_chartsDarkTheme);
|
|
ui->satTable->horizontalHeader()->setSortIndicator(m_settings.m_columnSort, m_settings.m_columnSortOrder);
|
|
getRollupContents()->restoreState(m_rollupState);
|
|
plotChart();
|
|
blockApplySettings(false);
|
|
}
|
|
|
|
void SatelliteTrackerGUI::onMenuDialogCalled(const QPoint &p)
|
|
{
|
|
if (m_contextMenuType == ContextMenuType::ContextMenuChannelSettings)
|
|
{
|
|
BasicFeatureSettingsDialog dialog(this);
|
|
dialog.setTitle(m_settings.m_title);
|
|
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
|
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
|
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
|
dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex);
|
|
dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex);
|
|
dialog.setDefaultTitle(m_displayedName);
|
|
|
|
dialog.move(p);
|
|
new DialogPositioner(&dialog, false);
|
|
dialog.exec();
|
|
|
|
m_settings.m_title = dialog.getTitle();
|
|
m_settings.m_useReverseAPI = dialog.useReverseAPI();
|
|
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
|
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
|
m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex();
|
|
m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex();
|
|
|
|
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");
|
|
|
|
applySettings();
|
|
}
|
|
|
|
resetContextMenuType();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::aos(const QString &speech)
|
|
{
|
|
// Call plotChart() to start the periodic updates with sat position in polar chart
|
|
plotChart();
|
|
// Give speech notification of pass
|
|
if (!speech.isEmpty())
|
|
{
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
|
m_speech->say(speech);
|
|
#else
|
|
qWarning() << "SatelliteTrackerGUI::aos: No TextToSpeech: " << speech;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::los(const QString &speech)
|
|
{
|
|
// Give speech notification of end of pass
|
|
if (!speech.isEmpty())
|
|
{
|
|
#ifdef QT_TEXTTOSPEECH_FOUND
|
|
m_speech->say(speech);
|
|
#else
|
|
qWarning() << "SatelliteTrackerGUI::los: No TextToSpeech: " << speech;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_startStop_toggled(bool checked)
|
|
{
|
|
if (m_doApplySettings)
|
|
{
|
|
SatelliteTracker::MsgStartStop *message = SatelliteTracker::MsgStartStop::create(checked);
|
|
m_satelliteTracker->getInputMessageQueue()->push(message);
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_latitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_latitude = value;
|
|
m_settingsKeys.append("latitude");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_longitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_longitude = value;
|
|
m_settingsKeys.append("longitude");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::setTarget(const QString& target)
|
|
{
|
|
if (target != m_settings.m_target)
|
|
{
|
|
m_settings.m_target = target;
|
|
m_settingsKeys.append("target");
|
|
ui->azimuth->setText("");
|
|
ui->elevation->setText("");
|
|
ui->aos->setText("");
|
|
ui->target->setCurrentIndex(ui->target->findText(m_settings.m_target));
|
|
m_nextTargetAOS = QDateTime();
|
|
m_nextTargetLOS = QDateTime();
|
|
m_geostationarySatVisible = false;
|
|
applySettings();
|
|
delete m_targetSatState;
|
|
m_targetSatState = nullptr;
|
|
m_plotPass = 0;
|
|
ui->passLabel->setText(QString("%1").arg(m_plotPass));
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_target_currentTextChanged(const QString &text)
|
|
{
|
|
setTarget(text);
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_useMyPosition_clicked(bool checked)
|
|
{
|
|
(void) checked;
|
|
double stationLatitude = MainCore::instance()->getSettings().getLatitude();
|
|
double stationLongitude = MainCore::instance()->getSettings().getLongitude();
|
|
double stationAltitude = MainCore::instance()->getSettings().getAltitude();
|
|
|
|
ui->latitude->setValue(stationLatitude);
|
|
ui->longitude->setValue(stationLongitude);
|
|
m_settings.m_heightAboveSeaLevel = stationAltitude;
|
|
m_settingsKeys.append("heightAboveSeaLevel");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
// Show settings dialog
|
|
void SatelliteTrackerGUI::on_displaySettings_clicked()
|
|
{
|
|
SatelliteTrackerSettingsDialog dialog(&m_settings);
|
|
new DialogPositioner(&dialog, true);
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
m_settingsKeys.append("heightAboveSeaLevel");
|
|
m_settingsKeys.append("predictionPeriod");
|
|
m_settingsKeys.append("passStartTime");
|
|
m_settingsKeys.append("passFinishTime");
|
|
m_settingsKeys.append("minAOSElevation");
|
|
m_settingsKeys.append("minPassElevation");
|
|
m_settingsKeys.append("rotatorMaxAzimuth");
|
|
m_settingsKeys.append("rotatorMaxElevation");
|
|
m_settingsKeys.append("aosSpeech");
|
|
m_settingsKeys.append("losSpeech");
|
|
m_settingsKeys.append("aosCommand");
|
|
m_settingsKeys.append("losCommand");
|
|
m_settingsKeys.append("updatePeriod");
|
|
m_settingsKeys.append("dopplerPeriod");
|
|
m_settingsKeys.append("defaultFrequency");
|
|
m_settingsKeys.append("azElUnits");
|
|
m_settingsKeys.append("groundTrackPoints");
|
|
m_settingsKeys.append("drawRotators");
|
|
m_settingsKeys.append("dateFormat");
|
|
m_settingsKeys.append("utc");
|
|
m_settingsKeys.append("tles");
|
|
m_settingsKeys.append("replayEnabled");
|
|
m_settingsKeys.append("replayStartDateTime");
|
|
m_settingsKeys.append("sendTimeToMap");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_dateTimeSelect_currentIndexChanged(int index)
|
|
{
|
|
m_settings.m_dateTimeSelect = (SatelliteTrackerSettings::DateTimeSelect)index;
|
|
|
|
if (m_settings.m_dateTimeSelect != SatelliteTrackerSettings::CUSTOM)
|
|
{
|
|
m_settings.m_dateTime = "";
|
|
ui->dateTime->setVisible(false);
|
|
}
|
|
else
|
|
{
|
|
m_settings.m_dateTime = ui->dateTime->dateTime().toString(Qt::ISODateWithMs);
|
|
ui->dateTime->setVisible(true);
|
|
}
|
|
|
|
ui->deviceFeatureSelect->setVisible(m_settings.m_dateTimeSelect >= SatelliteTrackerSettings::FROM_MAP);
|
|
updateDeviceFeatureCombo();
|
|
|
|
m_settingsKeys.append("dateTimeSelect");
|
|
m_settingsKeys.append("dateTime");
|
|
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_dateTime_dateTimeChanged(const QDateTime &datetime)
|
|
{
|
|
(void) datetime;
|
|
|
|
if (ui->dateTimeSelect->currentIndex() == 1)
|
|
{
|
|
m_settings.m_dateTime = ui->dateTime->dateTime().toString(Qt::ISODateWithMs);
|
|
m_settingsKeys.append("dateTime");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
// Find target on the Map
|
|
void SatelliteTrackerGUI::on_viewOnMap_clicked()
|
|
{
|
|
if (!m_settings.m_target.isEmpty()) {
|
|
FeatureWebAPIUtils::mapFind(m_settings.m_target);
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_updateSatData_clicked()
|
|
{
|
|
m_satelliteTracker->getInputMessageQueue()->push(SatelliteTracker::MsgUpdateSatData::create());
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_selectSats_clicked()
|
|
{
|
|
SatelliteSelectionDialog dialog(&m_settings, m_satellites);
|
|
new DialogPositioner(&dialog, true);
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
updateSelectedSats();
|
|
m_settingsKeys.append("satellites");
|
|
applySettings();
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_radioControl_clicked()
|
|
{
|
|
SatelliteRadioControlDialog dialog(&m_settings, m_satellites);
|
|
new DialogPositioner(&dialog, true);
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
m_settingsKeys.append("deviceSettings");
|
|
applySettings();
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_autoTarget_clicked(bool checked)
|
|
{
|
|
m_settings.m_autoTarget = checked;
|
|
m_settingsKeys.append("autoTarget");
|
|
applySettings();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::updateStatus()
|
|
{
|
|
int state = m_satelliteTracker->getState();
|
|
|
|
if (m_lastFeatureState != state)
|
|
{
|
|
// We set checked state of start/stop button, in case it was changed via API
|
|
bool oldState;
|
|
switch (state)
|
|
{
|
|
case Feature::StNotStarted:
|
|
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
|
break;
|
|
case Feature::StIdle:
|
|
oldState = ui->startStop->blockSignals(true);
|
|
ui->startStop->setChecked(false);
|
|
ui->startStop->blockSignals(oldState);
|
|
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
|
|
break;
|
|
case Feature::StRunning:
|
|
oldState = ui->startStop->blockSignals(true);
|
|
ui->startStop->setChecked(true);
|
|
ui->startStop->blockSignals(oldState);
|
|
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
|
|
break;
|
|
case Feature::StError:
|
|
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
|
|
QMessageBox::information(this, tr("Message"), m_satelliteTracker->getErrorMessage());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_lastFeatureState = state;
|
|
}
|
|
|
|
// Indicate if satellite data is being updated
|
|
bool updatingSatData = m_satelliteTracker->isUpdatingSatData();
|
|
|
|
if (updatingSatData != m_lastUpdatingSatData)
|
|
{
|
|
if (updatingSatData) {
|
|
ui->updateSatData->setStyleSheet("QToolButton { background-color : green; }");
|
|
} else {
|
|
ui->updateSatData->setStyleSheet("QToolButton { background: none; }");
|
|
}
|
|
|
|
m_lastUpdatingSatData = updatingSatData;
|
|
}
|
|
|
|
updateTimeToAOS();
|
|
updateDeviceFeatureCombo();
|
|
}
|
|
|
|
// Update time to AOS
|
|
void SatelliteTrackerGUI::updateTimeToAOS()
|
|
{
|
|
if (m_geostationarySatVisible)
|
|
{
|
|
ui->aos->setText("Now");
|
|
}
|
|
else if (m_nextTargetAOS.isValid())
|
|
{
|
|
QDateTime currentTime = m_satelliteTracker->currentDateTime(); // FIXME: UTC
|
|
int secondsToAOS = m_nextTargetAOS.toSecsSinceEpoch() - currentTime.toSecsSinceEpoch();
|
|
|
|
if (secondsToAOS > 0)
|
|
{
|
|
int seconds = secondsToAOS % 60;
|
|
int minutes = (secondsToAOS / 60) % 60;
|
|
int hours = (secondsToAOS / (60 * 60)) % 24;
|
|
int days = secondsToAOS / (60 * 60 * 24);
|
|
|
|
if (days == 1)
|
|
{
|
|
ui->aos->setText(QString("1 day"));
|
|
}
|
|
else if (days > 0)
|
|
{
|
|
ui->aos->setText(QString("%1 days").arg(days));
|
|
}
|
|
else
|
|
{
|
|
ui->aos->setText(QString("%1:%2:%3")
|
|
.arg(hours, 2, 10, QLatin1Char('0'))
|
|
.arg(minutes, 2, 10, QLatin1Char('0'))
|
|
.arg(seconds, 2, 10, QLatin1Char('0')));
|
|
}
|
|
}
|
|
else if (m_nextTargetLOS < currentTime)
|
|
{
|
|
ui->aos->setText("");
|
|
}
|
|
else
|
|
{
|
|
ui->aos->setText("Now");
|
|
}
|
|
}
|
|
else
|
|
ui->aos->setText("");
|
|
}
|
|
|
|
void SatelliteTrackerGUI::applySettings(bool force)
|
|
{
|
|
if (m_doApplySettings)
|
|
{
|
|
SatelliteTracker::MsgConfigureSatelliteTracker* message = SatelliteTracker::MsgConfigureSatelliteTracker::create(
|
|
m_settings, m_settingsKeys, force);
|
|
m_satelliteTracker->getInputMessageQueue()->push(message);
|
|
}
|
|
|
|
m_settingsKeys.clear();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_nextPass_clicked()
|
|
{
|
|
if (m_targetSatState != nullptr)
|
|
{
|
|
if (m_plotPass < m_targetSatState->m_passes.size() - 1)
|
|
{
|
|
m_plotPass++;
|
|
ui->passLabel->setText(QString("%1").arg(m_plotPass));
|
|
plotChart();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_prevPass_clicked()
|
|
{
|
|
if (m_plotPass > 0)
|
|
{
|
|
m_plotPass--;
|
|
ui->passLabel->setText(QString("%1").arg(m_plotPass));
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_darkTheme_clicked(bool checked)
|
|
{
|
|
m_settings.m_chartsDarkTheme = checked;
|
|
plotChart();
|
|
m_settingsKeys.append("chartsDarkTheme");
|
|
applySettings();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_chartSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
plotChart();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::plotChart()
|
|
{
|
|
if (ui->chartSelect->currentIndex() == 0) {
|
|
plotPolarChart();
|
|
} else {
|
|
plotAzElChart();
|
|
}
|
|
}
|
|
|
|
// Linear interpolation
|
|
static double interpolate(double x0, double y0, double x1, double y1, double x)
|
|
{
|
|
return (y0*(x1-x) + y1*(x-x0)) / (x1-x0);
|
|
}
|
|
|
|
// Reduce az/el range from 450,180 to 360,90
|
|
void SatelliteTrackerGUI::limitAzElRange(double& azimuth, double& elevation) const
|
|
{
|
|
if (elevation > 90.0)
|
|
{
|
|
elevation = 180.0 - elevation;
|
|
if (azimuth < 180.0) {
|
|
azimuth += 180.0;
|
|
} else {
|
|
azimuth -= 180.0;
|
|
}
|
|
}
|
|
if (azimuth > 360.0) {
|
|
azimuth -= 360.0f;
|
|
}
|
|
if (azimuth == 0) {
|
|
azimuth = 360.0;
|
|
}
|
|
}
|
|
|
|
// Plot pass in polar coords
|
|
void SatelliteTrackerGUI::plotPolarChart()
|
|
{
|
|
if ((m_targetSatState == nullptr) || !m_satellites.contains(m_settings.m_target) || (m_targetSatState->m_passes.size() == 0))
|
|
{
|
|
ui->passChart->setChart(&m_emptyChart);
|
|
return;
|
|
}
|
|
|
|
QChart *oldChart = m_polarChart;
|
|
|
|
if (m_plotPass >= m_targetSatState->m_passes.size() - 1)
|
|
{
|
|
m_plotPass = m_targetSatState->m_passes.size() - 1;
|
|
ui->passLabel->setText(QString("%1").arg(m_plotPass));
|
|
}
|
|
|
|
const SatellitePass &pass = m_targetSatState->m_passes[m_plotPass];
|
|
|
|
// Always create a new chart, otherwise sometimes they aren't drawn properly
|
|
m_polarChart = new QPolarChart();
|
|
m_polarChart->setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
|
QValueAxis *angularAxis = new QValueAxis();
|
|
QCategoryAxis *radialAxis = new QCategoryAxis();
|
|
|
|
angularAxis->setTickCount(9);
|
|
angularAxis->setMinorTickCount(1);
|
|
angularAxis->setLabelFormat("%d");
|
|
angularAxis->setRange(0, 360);
|
|
|
|
radialAxis->setMin(0);
|
|
radialAxis->setMax(90);
|
|
radialAxis->append("90", 0);
|
|
radialAxis->append("60", 30);
|
|
radialAxis->append("30", 60);
|
|
radialAxis->append("0", 90);
|
|
radialAxis->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
|
|
|
|
m_polarChart->addAxis(angularAxis, QPolarChart::PolarOrientationAngular);
|
|
m_polarChart->addAxis(radialAxis, QPolarChart::PolarOrientationRadial);
|
|
m_polarChart->legend()->hide();
|
|
m_polarChart->layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_polarChart->setMargins(QMargins(1, 1, 1, 1));
|
|
|
|
SatNogsSatellite *sat = m_satellites.value(m_settings.m_target);
|
|
|
|
if (pass.m_aos.isValid() && pass.m_los.isValid())
|
|
{
|
|
QString title;
|
|
|
|
if (m_settings.m_utc) {
|
|
title = pass.m_aos.date().toString(m_settings.m_dateFormat);
|
|
} else {
|
|
title = pass.m_aos.toLocalTime().date().toString(m_settings.m_dateFormat);
|
|
}
|
|
|
|
m_polarChart->setTitle(QString("%1").arg(title));
|
|
QLineSeries *polarSeries = new QLineSeries();
|
|
|
|
getPassAzEl(
|
|
nullptr,
|
|
nullptr,
|
|
polarSeries,
|
|
sat->m_tle->m_tle0,
|
|
sat->m_tle->m_tle1,
|
|
sat->m_tle->m_tle2,
|
|
m_settings.m_latitude,
|
|
m_settings.m_longitude,
|
|
m_settings.m_heightAboveSeaLevel/1000.0,
|
|
pass.m_aos, pass.m_los
|
|
);
|
|
|
|
// Polar charts can't handle points that are more than 180 degrees apart, so
|
|
// we need to split passes that cross from 359 -> 0 degrees (or the reverse)
|
|
QList<QLineSeries *> series;
|
|
series.append(new QLineSeries());
|
|
QLineSeries *s = series.first();
|
|
QPen pen(QColor(32, 159, 223), 2, Qt::SolidLine);
|
|
s->setPen(pen);
|
|
|
|
qreal prevAz = polarSeries->at(0).x();
|
|
qreal prevEl = polarSeries->at(0).y();
|
|
|
|
for (int i = 1; i < polarSeries->count(); i++)
|
|
{
|
|
qreal az = polarSeries->at(i).x();
|
|
qreal el = polarSeries->at(i).y();
|
|
|
|
if ((prevAz > 270.0) && (az <= 90.0))
|
|
{
|
|
double elMid = interpolate(prevAz, prevEl, az+360.0, el, 360.0);
|
|
s->append(360.0, elMid);
|
|
series.append(new QLineSeries());
|
|
s = series.last();
|
|
s->setPen(pen);
|
|
s->append(0.0, elMid);
|
|
s->append(az, el);
|
|
}
|
|
else if ((prevAz <= 90.0) && (az > 270.0))
|
|
{
|
|
double elMid = interpolate(prevAz, prevEl, az-360.0, el, 0.0);
|
|
s->append(0.0, elMid);
|
|
series.append(new QLineSeries());
|
|
s = series.last();
|
|
s->setPen(pen);
|
|
s->append(360.0, elMid);
|
|
s->append(az, el);
|
|
}
|
|
else
|
|
{
|
|
s->append(polarSeries->at(i));
|
|
}
|
|
|
|
prevAz = az;
|
|
prevEl = el;
|
|
}
|
|
|
|
for (int i = 0; i < series.length(); i++)
|
|
{
|
|
m_polarChart->addSeries(series[i]);
|
|
series[i]->attachAxis(angularAxis);
|
|
series[i]->attachAxis(radialAxis);
|
|
}
|
|
|
|
int redrawTime = 0;
|
|
|
|
if (m_settings.m_drawRotators != SatelliteTrackerSettings::NO_ROTATORS)
|
|
{
|
|
// Plot rotator position
|
|
QString ourSourceName = QString("F:%1 %2").arg(m_satelliteTracker->getIndexInFeatureSet()).arg(m_satelliteTracker->getIdentifier());
|
|
std::vector<FeatureSet*>& featureSets = MainCore::instance()->getFeatureeSets();
|
|
for (int featureSetIndex = 0; featureSetIndex < (int)featureSets.size(); featureSetIndex++)
|
|
{
|
|
FeatureSet *featureSet = featureSets[featureSetIndex];
|
|
for (int featureIndex = 0; featureIndex < featureSet->getNumberOfFeatures(); featureIndex++)
|
|
{
|
|
Feature *feature = featureSet->getFeatureAt(featureIndex);
|
|
if (FeatureUtils::compareFeatureURIs(feature->getURI(), "sdrangel.feature.gs232controller"))
|
|
{
|
|
QString source;
|
|
ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "source", source); // Will return false if source isn't set in Controller
|
|
int track = 0;
|
|
ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "track", track);
|
|
if ((m_settings.m_drawRotators == SatelliteTrackerSettings::ALL_ROTATORS) || ((source == ourSourceName) && track))
|
|
{
|
|
int onTarget = 0;
|
|
ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "onTarget", onTarget);
|
|
|
|
if (!onTarget)
|
|
{
|
|
// Target azimuth red dotted line
|
|
double targetAzimuth, targetElevation;
|
|
bool targetAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetAzimuth", targetAzimuth);
|
|
bool targetElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetElevation", targetElevation);
|
|
if (targetAzimuthOk && targetElevationOk)
|
|
{
|
|
limitAzElRange(targetAzimuth, targetElevation);
|
|
|
|
QScatterSeries *rotatorSeries = new QScatterSeries();
|
|
QColor color(255, 0, 0, 150);
|
|
QPen pen(color);
|
|
rotatorSeries->setPen(pen);
|
|
rotatorSeries->setColor(color.darker());
|
|
rotatorSeries->setMarkerSize(20);
|
|
rotatorSeries->append(targetAzimuth, 90-targetElevation);
|
|
m_polarChart->addSeries(rotatorSeries);
|
|
rotatorSeries->attachAxis(angularAxis);
|
|
rotatorSeries->attachAxis(radialAxis);
|
|
|
|
redrawTime = 333;
|
|
}
|
|
}
|
|
|
|
// Current azimuth line. Yellow while off target, green on target.
|
|
double currentAzimuth, currentElevation;
|
|
bool currentAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentAzimuth", currentAzimuth);
|
|
bool currentElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentElevation", currentElevation);
|
|
if (currentAzimuthOk && currentElevationOk)
|
|
{
|
|
limitAzElRange(currentAzimuth, currentElevation);
|
|
|
|
QScatterSeries *rotatorSeries = new QScatterSeries();
|
|
QColor color;
|
|
if (onTarget) {
|
|
color = QColor(0, 255, 0, 150);
|
|
} else {
|
|
color = QColor(255, 255, 0, 150);
|
|
}
|
|
rotatorSeries->setPen(QPen(color));
|
|
rotatorSeries->setColor(color.darker());
|
|
rotatorSeries->setMarkerSize(20);
|
|
rotatorSeries->append(currentAzimuth, 90-currentElevation);
|
|
m_polarChart->addSeries(rotatorSeries);
|
|
rotatorSeries->attachAxis(angularAxis);
|
|
rotatorSeries->attachAxis(radialAxis);
|
|
|
|
redrawTime = 333;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create series with single point, so we can plot time of AOS
|
|
QLineSeries *aosSeries = new QLineSeries();
|
|
aosSeries->append(polarSeries->at(0));
|
|
QTime time;
|
|
|
|
if (m_settings.m_utc) {
|
|
time = pass.m_aos.time();
|
|
} else {
|
|
time = pass.m_aos.toLocalTime().time();
|
|
}
|
|
|
|
if (m_settings.m_utc) {
|
|
aosSeries->setPointLabelsFormat(QString("AOS %1").arg(time.toString("hh:mm")));
|
|
} else {
|
|
aosSeries->setPointLabelsFormat(QString("AOS %1").arg(time.toString("hh:mm")));
|
|
}
|
|
|
|
aosSeries->setPointLabelsVisible(true);
|
|
aosSeries->setPointLabelsClipping(false);
|
|
m_polarChart->addSeries(aosSeries);
|
|
aosSeries->attachAxis(angularAxis);
|
|
aosSeries->attachAxis(radialAxis);
|
|
// Create series with single point, so we can plot time of LOS
|
|
QLineSeries *losSeries = new QLineSeries();
|
|
losSeries->append(polarSeries->at(polarSeries->count()-1));
|
|
|
|
if (m_settings.m_utc) {
|
|
time = pass.m_los.time();
|
|
} else {
|
|
time = pass.m_los.toLocalTime().time();
|
|
}
|
|
|
|
losSeries->setPointLabelsFormat(QString("LOS %1").arg(time.toString("hh:mm")));
|
|
losSeries->setPointLabelsVisible(true);
|
|
losSeries->setPointLabelsClipping(false);
|
|
m_polarChart->addSeries(losSeries);
|
|
losSeries->attachAxis(angularAxis);
|
|
losSeries->attachAxis(radialAxis);
|
|
QDateTime currentTime;
|
|
|
|
if (m_settings.m_dateTime == "") {
|
|
currentTime = m_satelliteTracker->currentDateTimeUtc();
|
|
} else if (m_settings.m_utc) {
|
|
currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
} else {
|
|
currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs).toUTC();
|
|
}
|
|
|
|
if ((currentTime >= pass.m_aos) && (currentTime <= pass.m_los))
|
|
{
|
|
// Create series with single point, so we can plot current time
|
|
QScatterSeries *nowSeries = new QScatterSeries();
|
|
nowSeries->setMarkerSize(3);
|
|
// Find closest point to current time
|
|
int idx = std::round(polarSeries->count() * (currentTime.toMSecsSinceEpoch() - pass.m_aos.toMSecsSinceEpoch())
|
|
/ (pass.m_los.toMSecsSinceEpoch() - pass.m_aos.toMSecsSinceEpoch()));
|
|
nowSeries->append(polarSeries->at(idx));
|
|
nowSeries->setPointLabelsFormat(m_settings.m_target);
|
|
nowSeries->setPointLabelsVisible(true);
|
|
nowSeries->setPointLabelsClipping(false);
|
|
m_polarChart->addSeries(nowSeries);
|
|
nowSeries->attachAxis(angularAxis);
|
|
nowSeries->attachAxis(radialAxis);
|
|
}
|
|
|
|
if (redrawTime > 0)
|
|
{
|
|
// Redraw to show updated rotator position
|
|
m_redrawTimer.setSingleShot(true);
|
|
m_redrawTimer.start(redrawTime);
|
|
}
|
|
|
|
delete polarSeries;
|
|
}
|
|
else
|
|
{
|
|
// Possibly geostationary, just plot current position
|
|
QDateTime currentTime;
|
|
|
|
if (m_settings.m_dateTime == "") {
|
|
currentTime = m_satelliteTracker->currentDateTimeUtc();
|
|
} else if (m_settings.m_utc) {
|
|
currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
} else {
|
|
currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs).toUTC();
|
|
}
|
|
|
|
QString title;
|
|
|
|
if (m_settings.m_utc) {
|
|
title = currentTime.date().toString(m_settings.m_dateFormat);
|
|
} else {
|
|
title = currentTime.toLocalTime().date().toString(m_settings.m_dateFormat);
|
|
}
|
|
|
|
m_polarChart->setTitle(QString("%1").arg(title));
|
|
QLineSeries *nowSeries = new QLineSeries();
|
|
QDateTime endTime = currentTime.addSecs(1);
|
|
|
|
getPassAzEl(
|
|
nullptr,
|
|
nullptr,
|
|
nowSeries,
|
|
sat->m_tle->m_tle0,
|
|
sat->m_tle->m_tle1,
|
|
sat->m_tle->m_tle2,
|
|
m_settings.m_latitude,
|
|
m_settings.m_longitude,
|
|
m_settings.m_heightAboveSeaLevel/1000.0,
|
|
currentTime, endTime
|
|
);
|
|
|
|
nowSeries->setPointLabelsFormat(m_settings.m_target);
|
|
nowSeries->setPointLabelsVisible(true);
|
|
nowSeries->setPointLabelsClipping(false);
|
|
m_polarChart->addSeries(nowSeries);
|
|
nowSeries->attachAxis(angularAxis);
|
|
nowSeries->attachAxis(radialAxis);
|
|
}
|
|
|
|
ui->passChart->setChart(m_polarChart);
|
|
delete oldChart;
|
|
}
|
|
|
|
// Plot target elevation/azimuth for the next pass
|
|
void SatelliteTrackerGUI::plotAzElChart()
|
|
{
|
|
if ((m_targetSatState == nullptr) || !m_satellites.contains(m_settings.m_target) || (m_targetSatState->m_passes.size() == 0))
|
|
{
|
|
ui->passChart->setChart(&m_emptyChart);
|
|
return;
|
|
}
|
|
|
|
QChart *oldChart = m_lineChart;
|
|
|
|
if (m_plotPass >= m_targetSatState->m_passes.size() - 1)
|
|
{
|
|
m_plotPass = m_targetSatState->m_passes.size() - 1;
|
|
ui->passLabel->setText(QString("%1").arg(m_plotPass));
|
|
}
|
|
|
|
const SatellitePass &pass = m_targetSatState->m_passes[m_plotPass];
|
|
// Always create a new chart, otherwise sometimes they aren't drawn properly
|
|
m_lineChart = new QChart();
|
|
m_lineChart->setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
|
QDateTimeAxis *xAxis = new QDateTimeAxis();
|
|
QValueAxis *yLeftAxis = new QValueAxis();
|
|
QValueAxis *yRightAxis = new QValueAxis();
|
|
QString title;
|
|
|
|
if (m_settings.m_utc) {
|
|
title = pass.m_aos.date().toString(m_settings.m_dateFormat);
|
|
} else {
|
|
title = pass.m_aos.toLocalTime().date().toString(m_settings.m_dateFormat);
|
|
}
|
|
|
|
m_lineChart->setTitle(QString("%1").arg(title));
|
|
m_lineChart->legend()->hide();
|
|
m_lineChart->addAxis(xAxis, Qt::AlignBottom);
|
|
m_lineChart->addAxis(yLeftAxis, Qt::AlignLeft);
|
|
m_lineChart->addAxis(yRightAxis, Qt::AlignRight);
|
|
m_lineChart->layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_lineChart->setMargins(QMargins(1, 1, 1, 1));
|
|
SatNogsSatellite *sat = m_satellites.value(m_settings.m_target);
|
|
QLineSeries *azSeries = new QLineSeries();
|
|
QLineSeries *elSeries = new QLineSeries();
|
|
|
|
getPassAzEl(
|
|
azSeries,
|
|
elSeries,
|
|
nullptr,
|
|
sat->m_tle->m_tle0,
|
|
sat->m_tle->m_tle1,
|
|
sat->m_tle->m_tle2,
|
|
m_settings.m_latitude,
|
|
m_settings.m_longitude,
|
|
m_settings.m_heightAboveSeaLevel/1000.0,
|
|
pass.m_aos,
|
|
pass.m_los
|
|
);
|
|
|
|
// Split crossing of 360/0 degrees in to multiple series in the same colour
|
|
QList<QLineSeries *> azSeriesList;
|
|
QPen pen(QColor(153, 202, 83), 2, Qt::SolidLine);
|
|
QLineSeries *s = new QLineSeries();
|
|
azSeriesList.append(s);
|
|
s->setPen(pen);
|
|
qreal prevAz = azSeries->at(0).y();
|
|
|
|
for (int i = 0; i < azSeries->count(); i++)
|
|
{
|
|
qreal az = azSeries->at(i).y();
|
|
|
|
if (((prevAz >= 270) && (az < 90)) || ((prevAz < 90) && (az >= 270)))
|
|
{
|
|
s = new QLineSeries();
|
|
azSeriesList.append(s);
|
|
s->setPen(pen);
|
|
}
|
|
|
|
s->append(azSeries->at(i).x(), az);
|
|
prevAz = az;
|
|
}
|
|
|
|
m_lineChart->addSeries(elSeries);
|
|
elSeries->attachAxis(xAxis);
|
|
elSeries->attachAxis(yLeftAxis);
|
|
|
|
for (int i = 0; i < azSeriesList.size(); i++)
|
|
{
|
|
m_lineChart->addSeries(azSeriesList[i]);
|
|
azSeriesList[i]->attachAxis(xAxis);
|
|
azSeriesList[i]->attachAxis(yRightAxis);
|
|
}
|
|
|
|
// Plot current target on elevation series
|
|
if (m_targetSatState && (m_targetSatState->m_elevation > 0.0))
|
|
{
|
|
QDateTime currentTime;
|
|
|
|
if (m_settings.m_dateTime == "") {
|
|
currentTime = m_satelliteTracker->currentDateTimeUtc();
|
|
} else if (m_settings.m_utc) {
|
|
currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
} else {
|
|
currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs).toUTC();
|
|
}
|
|
|
|
QScatterSeries *posSeries = new QScatterSeries();
|
|
posSeries->setMarkerSize(3);
|
|
posSeries->append(currentTime.toMSecsSinceEpoch(), m_targetSatState->m_elevation);
|
|
posSeries->setPointLabelsVisible(true);
|
|
posSeries->setPointLabelsFormat(m_settings.m_target);
|
|
posSeries->setPointLabelsClipping(false);
|
|
m_lineChart->addSeries(posSeries);
|
|
posSeries->attachAxis(xAxis);
|
|
posSeries->attachAxis(yLeftAxis);
|
|
}
|
|
|
|
xAxis->setRange(pass.m_aos, pass.m_los);
|
|
xAxis->setFormat("hh:mm");
|
|
yLeftAxis->setRange(0.0, 90.0);
|
|
yLeftAxis->setTickCount(7);
|
|
yLeftAxis->setLabelFormat("%d");
|
|
yLeftAxis->setTitleText(QString("Elevation (%1)").arg(QChar(0xb0)));
|
|
yRightAxis->setRange(0.0, 360.0);
|
|
yRightAxis->setTickCount(7);
|
|
yRightAxis->setLabelFormat("%d");
|
|
yRightAxis->setTitleText(QString("Azimuth (%1)").arg(QChar(0xb0)));
|
|
|
|
ui->passChart->setChart(m_lineChart);
|
|
|
|
delete azSeries;
|
|
delete oldChart;
|
|
}
|
|
|
|
void SatelliteTrackerGUI::resizeTable()
|
|
{
|
|
// Fill table with a row of dummy data that will size the columns nicely
|
|
int row = ui->satTable->rowCount();
|
|
ui->satTable->setRowCount(row + 1);
|
|
ui->satTable->setItem(row, SAT_COL_NAME, new QTableWidgetItem("Satellite123"));
|
|
ui->satTable->setItem(row, SAT_COL_AZ, new QTableWidgetItem("360"));
|
|
ui->satTable->setItem(row, SAT_COL_EL, new QTableWidgetItem("-90"));
|
|
ui->satTable->setItem(row, SAT_COL_TNE, new QTableWidgetItem("99:99:99 AOS"));
|
|
ui->satTable->setItem(row, SAT_COL_DUR, new QTableWidgetItem("9:99:99"));
|
|
ui->satTable->setItem(row, SAT_COL_AOS, new QTableWidgetItem("+1 10:17"));
|
|
ui->satTable->setItem(row, SAT_COL_LOS, new QTableWidgetItem("+1 10:17"));
|
|
ui->satTable->setItem(row, SAT_COL_MAX_EL, new QTableWidgetItem("90"));
|
|
ui->satTable->setItem(row, SAT_COL_DIR, new QTableWidgetItem("^"));
|
|
ui->satTable->setItem(row, SAT_COL_LATITUDE, new QTableWidgetItem("-90.0"));
|
|
ui->satTable->setItem(row, SAT_COL_LONGITUDE, new QTableWidgetItem("-180.0"));
|
|
ui->satTable->setItem(row, SAT_COL_ALT, new QTableWidgetItem("50000"));
|
|
ui->satTable->setItem(row, SAT_COL_RANGE, new QTableWidgetItem("50000"));
|
|
ui->satTable->setItem(row, SAT_COL_RANGE_RATE, new QTableWidgetItem("10.0"));
|
|
ui->satTable->setItem(row, SAT_COL_DOPPLER, new QTableWidgetItem("10000"));
|
|
ui->satTable->setItem(row, SAT_COL_PATH_LOSS, new QTableWidgetItem("100"));
|
|
ui->satTable->setItem(row, SAT_COL_NORAD_ID, new QTableWidgetItem("123456"));
|
|
ui->satTable->resizeColumnsToContents();
|
|
ui->satTable->setRowCount(row);
|
|
}
|
|
|
|
// As we only have limited space in table, display time plus number of days to AOS/LOS
|
|
// unless it's greater than 10 days, in which case just display the date
|
|
QString SatelliteTrackerGUI::formatDaysTime(qint64 days, QDateTime dateTime)
|
|
{
|
|
QDateTime dt;
|
|
|
|
if (m_settings.m_utc) {
|
|
dt = dateTime.toUTC();
|
|
} else {
|
|
dt = dateTime.toLocalTime();
|
|
}
|
|
|
|
if (abs(days) > 10) {
|
|
return dt.date().toString(m_settings.m_dateFormat);
|
|
} else if (days == 0) {
|
|
return dt.time().toString("hh:mm");
|
|
} else if (days > 0) {
|
|
return dt.time().toString(QString("hh:mm +%1").arg(days));
|
|
} else {
|
|
return dt.time().toString(QString("hh:mm %1").arg(days));
|
|
}
|
|
}
|
|
|
|
QString SatelliteTrackerGUI::formatSecondsAsHHMMSS(qint64 seconds)
|
|
{
|
|
char const* sign = "";
|
|
|
|
if (seconds < 0)
|
|
{
|
|
sign = "-";
|
|
seconds = -seconds;
|
|
}
|
|
|
|
int minutes = seconds / 60;
|
|
seconds = seconds % 60;
|
|
int hours = minutes / 60;
|
|
minutes = minutes % 60;
|
|
|
|
if (hours > 0) {
|
|
return QString("%1%2:%3:%4").arg(sign).arg(hours).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
|
|
} else {
|
|
return QString("%1%2:%3").arg(sign).arg(minutes).arg(seconds, 2, 10, QChar('0'));
|
|
}
|
|
}
|
|
|
|
// Table item showing some text, but sorted by datetime set as user data
|
|
class DateTimeSortedTableWidgetItem : public QTableWidgetItem {
|
|
public:
|
|
bool operator<(const QTableWidgetItem& other) const
|
|
{
|
|
QVariant v1 = data(Qt::UserRole);
|
|
QVariant v2 = other.data(Qt::UserRole);
|
|
|
|
if (v1.isValid() && v2.isValid()) {
|
|
return v1.toDateTime() < v2.toDateTime();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Handle sorting for next column, which can have times as HH:MM:SS or MM:SS
|
|
class NextEventTableWidgetItem : public QTableWidgetItem
|
|
{
|
|
public:
|
|
bool operator<(const QTableWidgetItem &other) const override
|
|
{
|
|
QString t1 = text();
|
|
QString t2 = other.text();
|
|
int t1Colons = t1.count(":");
|
|
int t2Colons = t2.count(":");
|
|
|
|
if (t1Colons == t2Colons)
|
|
{
|
|
QCollator coll;
|
|
coll.setNumericMode(true);
|
|
return coll.compare(t1, t2) < 0;
|
|
}
|
|
else
|
|
{
|
|
return t1Colons < t2Colons;
|
|
}
|
|
}
|
|
};
|
|
|
|
class NaturallySortedTableWidgetItem : public QTableWidgetItem
|
|
{
|
|
public:
|
|
bool operator<(const QTableWidgetItem &other) const override
|
|
{
|
|
QCollator coll;
|
|
coll.setNumericMode(true);
|
|
return coll.compare(text() , other.text()) < 0;
|
|
}
|
|
};
|
|
|
|
#define SPEED_OF_LIGHT 299792458.0
|
|
|
|
// Frequency in Hz, speed in m/s
|
|
static double doppler(double frequency, double speed)
|
|
{
|
|
return frequency * speed / SPEED_OF_LIGHT;
|
|
}
|
|
|
|
// Frequency in Hz, speed in m/s
|
|
static double freeSpaceLoss(double frequency, double distance)
|
|
{
|
|
return 20.0 * log10(distance) + 20 * log10(frequency) + 20 * log10(4*M_PI/SPEED_OF_LIGHT);
|
|
}
|
|
|
|
// Distance in m, delay in s
|
|
static double propagationDelay(double distance)
|
|
{
|
|
return distance / SPEED_OF_LIGHT;
|
|
}
|
|
|
|
// Update satellite data table with latest data for the satellite
|
|
void SatelliteTrackerGUI::updateTable(SatelliteState *satState)
|
|
{
|
|
// Does the table already contain this satellite?
|
|
QList<QTableWidgetItem *> matches = ui->satTable->findItems(satState->m_name, Qt::MatchExactly);
|
|
QTableWidgetItem *items[SAT_COL_COLUMNS];
|
|
|
|
if (matches.size() == 0)
|
|
{
|
|
// Add a new row
|
|
ui->satTable->setSortingEnabled(false);
|
|
int row = ui->satTable->rowCount();
|
|
ui->satTable->setRowCount(row + 1);
|
|
|
|
for (int i = 0; i < SAT_COL_COLUMNS; i++)
|
|
{
|
|
if ((i == SAT_COL_AOS) || (i == SAT_COL_LOS)) {
|
|
items[i] = new DateTimeSortedTableWidgetItem();
|
|
} else if ((i == SAT_COL_NAME) || (i == SAT_COL_NORAD_ID)) {
|
|
items[i] = new QTableWidgetItem();
|
|
} else if (i == SAT_COL_TNE) {
|
|
items[i] = new NextEventTableWidgetItem();
|
|
} else {
|
|
items[i] = new NaturallySortedTableWidgetItem();
|
|
}
|
|
|
|
items[i]->setToolTip(ui->satTable->horizontalHeaderItem(i)->toolTip());
|
|
ui->satTable->setItem(row, i, items[i]);
|
|
}
|
|
|
|
ui->satTable->setSortingEnabled(true);
|
|
// Static columns
|
|
items[SAT_COL_NAME]->setText(satState->m_name);
|
|
|
|
if (m_satellites.contains(satState->m_name))
|
|
{
|
|
SatNogsSatellite *sat = m_satellites.value(satState->m_name);
|
|
items[SAT_COL_NORAD_ID]->setData(Qt::DisplayRole, sat->m_noradCatId);
|
|
}
|
|
|
|
// Text alignment
|
|
for (int col : {SAT_COL_AZ, SAT_COL_EL, SAT_COL_TNE, SAT_COL_DUR, SAT_COL_MAX_EL,
|
|
SAT_COL_LATITUDE, SAT_COL_LONGITUDE,
|
|
SAT_COL_ALT, SAT_COL_RANGE, SAT_COL_RANGE_RATE, SAT_COL_DOPPLER,
|
|
SAT_COL_PATH_LOSS, SAT_COL_DELAY})
|
|
{
|
|
items[col]->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update existing row
|
|
int row = ui->satTable->row(matches[0]);
|
|
|
|
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
|
|
items[i] = ui->satTable->item(row, i);
|
|
}
|
|
}
|
|
|
|
items[SAT_COL_AZ]->setData(Qt::DisplayRole, (int)round(satState->m_azimuth));
|
|
items[SAT_COL_EL]->setData(Qt::DisplayRole, (int)round(satState->m_elevation));
|
|
|
|
if (satState->m_passes.size() > 0)
|
|
{
|
|
// Get number of days to AOS/LOS
|
|
QDateTime currentDateTime = m_satelliteTracker->currentDateTime();
|
|
int daysToAOS = currentDateTime.daysTo(satState->m_passes[0].m_aos);
|
|
int daysToLOS = currentDateTime.daysTo(satState->m_passes[0].m_los);
|
|
|
|
if (satState->m_passes[0].m_aos > currentDateTime) {
|
|
items[SAT_COL_TNE]->setText(formatSecondsAsHHMMSS(currentDateTime.secsTo(satState->m_passes[0].m_aos))+" AOS");
|
|
} else {
|
|
items[SAT_COL_TNE]->setText(formatSecondsAsHHMMSS(currentDateTime.secsTo(satState->m_passes[0].m_los))+" LOS");
|
|
}
|
|
|
|
items[SAT_COL_DUR]->setText(formatSecondsAsHHMMSS(satState->m_passes[0].m_aos.secsTo(satState->m_passes[0].m_los)));
|
|
items[SAT_COL_AOS]->setText(formatDaysTime(daysToAOS, satState->m_passes[0].m_aos));
|
|
items[SAT_COL_AOS]->setData(Qt::UserRole, satState->m_passes[0].m_aos);
|
|
items[SAT_COL_LOS]->setText(formatDaysTime(daysToLOS, satState->m_passes[0].m_los));
|
|
items[SAT_COL_LOS]->setData(Qt::UserRole, satState->m_passes[0].m_los);
|
|
items[SAT_COL_MAX_EL]->setData(Qt::DisplayRole, (int)round(satState->m_passes[0].m_maxElevation));
|
|
|
|
if (satState->m_passes[0].m_northToSouth) {
|
|
items[SAT_COL_DIR]->setText(QString("%1").arg(QChar(0x2193))); // Down arrow
|
|
} else {
|
|
items[SAT_COL_DIR]->setText(QString("%1").arg(QChar(0x2191))); // Up arrow
|
|
}
|
|
}
|
|
else
|
|
{
|
|
items[SAT_COL_TNE]->setText("");
|
|
items[SAT_COL_DUR]->setText("");
|
|
items[SAT_COL_AOS]->setText("");
|
|
items[SAT_COL_LOS]->setText("");
|
|
items[SAT_COL_MAX_EL]->setData(Qt::DisplayRole, QVariant());
|
|
items[SAT_COL_DIR]->setText("");
|
|
}
|
|
|
|
items[SAT_COL_LATITUDE]->setData(Qt::DisplayRole, satState->m_latitude);
|
|
items[SAT_COL_LONGITUDE]->setData(Qt::DisplayRole, satState->m_longitude);
|
|
items[SAT_COL_ALT]->setData(Qt::DisplayRole, (int)round(satState->m_altitude));
|
|
items[SAT_COL_RANGE]->setData(Qt::DisplayRole, (int)round(satState->m_range));
|
|
items[SAT_COL_RANGE_RATE]->setData(Qt::DisplayRole, QString::number(satState->m_rangeRate, 'f', 3));
|
|
items[SAT_COL_DOPPLER]->setData(Qt::DisplayRole, (int)round(-doppler(m_settings.m_defaultFrequency, satState->m_rangeRate*1000.0)));
|
|
items[SAT_COL_PATH_LOSS]->setData(Qt::DisplayRole, QString::number(freeSpaceLoss(m_settings.m_defaultFrequency, satState->m_range*1000.0), 'f', 1));
|
|
items[SAT_COL_DELAY]->setData(Qt::DisplayRole, QString::number(propagationDelay(satState->m_range*1000.0)*1000.0, 'f', 1));
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_satTable_cellDoubleClicked(int row, int column)
|
|
{
|
|
(void) column;
|
|
|
|
QString sat = ui->satTable->item(row, SAT_COL_NAME)->text();
|
|
FeatureWebAPIUtils::mapFind(sat);
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_satTableHeader_sortIndicatorChanged(int logicalIndex, Qt::SortOrder order)
|
|
{
|
|
m_settings.m_columnSort = logicalIndex;
|
|
m_settings.m_columnSortOrder = order;
|
|
m_settingsKeys.append("columnSort");
|
|
m_settingsKeys.append("columnSortOrder");
|
|
|
|
applySettings();
|
|
}
|
|
|
|
// Columns in table reordered
|
|
void SatelliteTrackerGUI::satTable_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
|
{
|
|
(void) oldVisualIndex;
|
|
m_settings.m_columnIndexes[logicalIndex] = newVisualIndex;
|
|
}
|
|
|
|
// Column in table resized (when hidden size is 0)
|
|
void SatelliteTrackerGUI::satTable_sectionResized(int logicalIndex, int oldSize, int newSize)
|
|
{
|
|
(void) oldSize;
|
|
m_settings.m_columnSizes[logicalIndex] = newSize;
|
|
}
|
|
|
|
// Right click in table header - show column select menu
|
|
void SatelliteTrackerGUI::columnSelectMenu(QPoint pos)
|
|
{
|
|
menu->popup(ui->satTable->horizontalHeader()->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
// Hide/show column when menu selected
|
|
void SatelliteTrackerGUI::columnSelectMenuChecked(bool checked)
|
|
{
|
|
(void) checked;
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
|
if (action != nullptr)
|
|
{
|
|
int idx = action->data().toInt(nullptr);
|
|
ui->satTable->setColumnHidden(idx, !action->isChecked());
|
|
}
|
|
}
|
|
|
|
// Create column select menu item
|
|
QAction *SatelliteTrackerGUI::createCheckableItem(QString &text, int idx, bool checked)
|
|
{
|
|
QAction *action = new QAction(text, this);
|
|
action->setCheckable(true);
|
|
action->setChecked(checked);
|
|
action->setData(QVariant(idx));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(columnSelectMenuChecked()));
|
|
return action;
|
|
}
|
|
|
|
void SatelliteTrackerGUI::updateDeviceFeatureCombo()
|
|
{
|
|
if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_MAP) {
|
|
updateMapList();
|
|
} else if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_FILE) {
|
|
updateFileInputList();
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerGUI::updateDeviceFeatureCombo(const QStringList &items, const QString &selected)
|
|
{
|
|
// Remove items no longer in list
|
|
int i = 0;
|
|
while (i < ui->deviceFeatureSelect->count())
|
|
{
|
|
if (!items.contains(ui->deviceFeatureSelect->itemText(i))) {
|
|
ui->deviceFeatureSelect->removeItem(i);
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
// Add new items to list
|
|
for (auto item : items)
|
|
{
|
|
int idx = ui->deviceFeatureSelect->findText(item);
|
|
|
|
if (idx == -1) {
|
|
ui->deviceFeatureSelect->addItem(item);
|
|
}
|
|
}
|
|
|
|
ui->deviceFeatureSelect->setCurrentIndex(ui->deviceFeatureSelect->findText(selected));
|
|
}
|
|
|
|
void SatelliteTrackerGUI::updateFileInputList()
|
|
{
|
|
// Create list of File Input devices
|
|
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
|
|
int deviceIndex = 0;
|
|
QStringList items;
|
|
|
|
for (std::vector<DeviceSet*>::const_iterator it = deviceSets.begin(); it != deviceSets.end(); ++it, deviceIndex++)
|
|
{
|
|
if ((*it)->m_deviceAPI && (*it)->m_deviceAPI->getHardwareId() == "FileInput") {
|
|
items.append(QString("R%1").arg(deviceIndex));
|
|
}
|
|
}
|
|
|
|
updateDeviceFeatureCombo(items, m_settings.m_fileInputDevice);
|
|
}
|
|
|
|
void SatelliteTrackerGUI::updateMapList()
|
|
{
|
|
// Create list of Map features
|
|
std::vector<FeatureSet*>& featureSets = MainCore::instance()->getFeatureeSets();
|
|
int featureIndex = 0;
|
|
QStringList items;
|
|
|
|
for (std::vector<FeatureSet*>::const_iterator it = featureSets.begin(); it != featureSets.end(); ++it, featureIndex++)
|
|
{
|
|
for (int fi = 0; fi < (*it)->getNumberOfFeatures(); fi++)
|
|
{
|
|
Feature *feature = (*it)->getFeatureAt(fi);
|
|
|
|
if (feature->getURI() == "sdrangel.feature.map") {
|
|
items.append(QString("F%1:%2").arg(featureIndex).arg(fi));
|
|
}
|
|
}
|
|
}
|
|
|
|
updateDeviceFeatureCombo(items, m_settings.m_mapFeature);
|
|
}
|
|
|
|
void SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
|
|
if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::FROM_MAP)
|
|
{
|
|
m_settings.m_mapFeature = ui->deviceFeatureSelect->currentText();
|
|
m_settingsKeys.append("mapFeature");
|
|
} else
|
|
{
|
|
m_settings.m_fileInputDevice = ui->deviceFeatureSelect->currentText();
|
|
m_settingsKeys.append("fileInputDevice");
|
|
}
|
|
|
|
applySettings();
|
|
}
|
|
|
|
void SatelliteTrackerGUI::makeUIConnections()
|
|
{
|
|
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &SatelliteTrackerGUI::on_startStop_toggled);
|
|
QObject::connect(ui->useMyPosition, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_useMyPosition_clicked);
|
|
QObject::connect(ui->latitude, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SatelliteTrackerGUI::on_latitude_valueChanged);
|
|
QObject::connect(ui->longitude, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SatelliteTrackerGUI::on_longitude_valueChanged);
|
|
QObject::connect(ui->target, &QComboBox::currentTextChanged, this, &SatelliteTrackerGUI::on_target_currentTextChanged);
|
|
QObject::connect(ui->displaySettings, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_displaySettings_clicked);
|
|
QObject::connect(ui->radioControl, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_radioControl_clicked);
|
|
QObject::connect(ui->dateTimeSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_dateTimeSelect_currentIndexChanged);
|
|
QObject::connect(ui->dateTime, &WrappingDateTimeEdit::dateTimeChanged, this, &SatelliteTrackerGUI::on_dateTime_dateTimeChanged);
|
|
QObject::connect(ui->viewOnMap, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_viewOnMap_clicked);
|
|
QObject::connect(ui->updateSatData, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_updateSatData_clicked);
|
|
QObject::connect(ui->selectSats, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_selectSats_clicked);
|
|
QObject::connect(ui->autoTarget, &ButtonSwitch::clicked, this, &SatelliteTrackerGUI::on_autoTarget_clicked);
|
|
QObject::connect(ui->chartSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_chartSelect_currentIndexChanged);
|
|
QObject::connect(ui->nextPass, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_nextPass_clicked);
|
|
QObject::connect(ui->prevPass, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_prevPass_clicked);
|
|
QObject::connect(ui->darkTheme, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_darkTheme_clicked);
|
|
QObject::connect(ui->satTable, &QTableWidget::cellDoubleClicked, this, &SatelliteTrackerGUI::on_satTable_cellDoubleClicked);
|
|
QObject::connect(ui->satTable->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &SatelliteTrackerGUI::on_satTableHeader_sortIndicatorChanged);
|
|
QObject::connect(ui->deviceFeatureSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged);
|
|
}
|
|
|