mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-10-09 08:40:49 -04:00
MessageQueue::pop() transfers ownership of the popped message to the caller, but many handleInputMessages() implementations only deleted the message when handleMessage() returned true. When a message was not handled, ownership had already been transferred from the queue and the message was leaked. This was identified while investigating an ASAN leak in the audio subsystem. Update the applicable handleInputMessages() implementations to always delete messages after handleMessage() returns. Unhandled messages are also logged with the component and message identifier so unexpected message routing is visible during debugging. This change: - Ensures every message popped from a queue has its ownership resolved. - Prevents leaks when handleMessage() returns false. - Makes unhandled messages visible instead of silently discarding them. - Preserves the existing message handling logic and does not require components to handle message types that are legitimately irrelevant to them. The changes are intentionally mechanical and limited to the common message queue ownership and diagnostic pattern. Signed-off-by: Robin Getz <rgetz503@gmail.com>
3447 lines
118 KiB
C++
3447 lines
118 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2021-2026 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 //
|
|
// 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 <algorithm>
|
|
#include <QMessageBox>
|
|
#include <QLineEdit>
|
|
#include <QRegularExpression>
|
|
#include <QNetworkAccessManager>
|
|
#include <QNetworkReply>
|
|
#include <QGraphicsScene>
|
|
|
|
#include <QtCharts/QChartView>
|
|
#include <QtCharts/QLineSeries>
|
|
#include <QtCharts/QDateTimeAxis>
|
|
#include <QtCharts/QValueAxis>
|
|
|
|
#include "SWGStarTrackerDisplaySettings.h"
|
|
#include "SWGStarTrackerDisplayLoSSettings.h"
|
|
|
|
#include "feature/featureset.h"
|
|
#include "feature/featureuiset.h"
|
|
#include "feature/featureutils.h"
|
|
#include "feature/featurewebapiutils.h"
|
|
#include "channel/channelwebapiutils.h"
|
|
#include "gui/basicfeaturesettingsdialog.h"
|
|
#include "gui/dmsspinbox.h"
|
|
#include "gui/graphicsviewzoom.h"
|
|
#include "gui/dialogpositioner.h"
|
|
#include "util/units.h"
|
|
#include "util/astronomy.h"
|
|
#include "util/interpolation.h"
|
|
#include "util/png.h"
|
|
#include "util/profiler.h"
|
|
#include "maincore.h"
|
|
|
|
#include "ui_startrackergui.h"
|
|
#include "startracker.h"
|
|
#include "startrackergui.h"
|
|
#include "startrackersettingsdialog.h"
|
|
|
|
StarTrackerGUI* StarTrackerGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
|
|
{
|
|
StarTrackerGUI* gui = new StarTrackerGUI(pluginAPI, featureUISet, feature);
|
|
return gui;
|
|
}
|
|
|
|
void StarTrackerGUI::destroy()
|
|
{
|
|
qDeleteAll(m_lineOfSightMarkers);
|
|
delete this;
|
|
}
|
|
|
|
void StarTrackerGUI::resetToDefaults()
|
|
{
|
|
m_settings.resetToDefaults();
|
|
displaySettings();
|
|
applySettings(true);
|
|
}
|
|
|
|
QByteArray StarTrackerGUI::serialize() const
|
|
{
|
|
return m_settings.serialize();
|
|
}
|
|
|
|
bool StarTrackerGUI::deserialize(const QByteArray& data)
|
|
{
|
|
if (m_settings.deserialize(data))
|
|
{
|
|
m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
|
|
displaySettings();
|
|
applySettings(true);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
resetToDefaults();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool StarTrackerGUI::handleMessage(const Message& message)
|
|
{
|
|
if (StarTracker::MsgConfigureStarTracker::match(message))
|
|
{
|
|
qDebug("StarTrackerGUI::handleMessage: StarTracker::MsgConfigureStarTracker");
|
|
const StarTracker::MsgConfigureStarTracker& cfg = (StarTracker::MsgConfigureStarTracker&) message;
|
|
|
|
if (cfg.getForce()) {
|
|
m_settings = cfg.getSettings();
|
|
} else {
|
|
m_settings.applySettings(cfg.getSettingsKeys(), cfg.getSettings());
|
|
}
|
|
|
|
displaySettings();
|
|
|
|
return true;
|
|
}
|
|
else if (StarTrackerReport::MsgReportAzAl::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportAzAl& azAl = (StarTrackerReport::MsgReportAzAl&) message;
|
|
blockApplySettings(true);
|
|
blockPlotChart();
|
|
ui->azimuth->setValue(azAl.getAzimuth());
|
|
ui->elevation->setValue(azAl.getElevation());
|
|
unblockPlotChartAndPlot();
|
|
blockApplySettings(false);
|
|
return true;
|
|
}
|
|
else if (StarTrackerReport::MsgReportRADec::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportRADec& raDec = (StarTrackerReport::MsgReportRADec&) message;
|
|
QString target = raDec.getTarget();
|
|
if (target == "target")
|
|
{
|
|
m_settings.m_ra = Units::decimalHoursToHoursMinutesAndSeconds(raDec.getRA());
|
|
m_settings.m_dec = Units::decimalDegreesToDegreeMinutesAndSeconds(raDec.getDec());
|
|
blockPlotChart();
|
|
ui->rightAscension->setText(m_settings.m_ra);
|
|
ui->declination->setText(m_settings.m_dec);
|
|
unblockPlotChartAndPlot();
|
|
}
|
|
else if (target == "sun")
|
|
{
|
|
m_sunRA = raDec.getRA();
|
|
m_sunDec = raDec.getDec();
|
|
}
|
|
else if (target == "moon")
|
|
{
|
|
m_moonRA = raDec.getRA();
|
|
m_moonDec = raDec.getDec();
|
|
}
|
|
raDecChanged();
|
|
return true;
|
|
}
|
|
else if (StarTrackerReport::MsgReportAzElVsTime::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportAzElVsTime& azElVsTime = (StarTrackerReport::MsgReportAzElVsTime&) message;
|
|
m_azElVsTimeTarget = azElVsTime.getTarget();
|
|
m_azimuths = azElVsTime.getAzimuths();
|
|
m_elevations = azElVsTime.getElevations();
|
|
m_dateTimes = azElVsTime.getDateTimes();
|
|
plotChart();
|
|
return true;
|
|
}
|
|
else if (StarTrackerReport::MsgReportGalactic::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportGalactic& galactic = (StarTrackerReport::MsgReportGalactic&) message;
|
|
blockApplySettings(true);
|
|
blockPlotChart();
|
|
ui->galacticLongitude->setValue(galactic.getL());
|
|
ui->galacticLatitude->setValue(galactic.getB());
|
|
unblockPlotChartAndPlot();
|
|
blockApplySettings(false);
|
|
return true;
|
|
}
|
|
else if (StarTrackerReport::MsgReportSolarSystemPositions::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportSolarSystemPositions& report = (StarTrackerReport::MsgReportSolarSystemPositions&) message;
|
|
updateSolarSystemPositions(report.getNames(), report.getPositions(), report.getOrbit());
|
|
return true;
|
|
}
|
|
else if (StarTrackerReport::MsgReportJupiter::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportJupiter& report = (StarTrackerReport::MsgReportJupiter&) message;
|
|
ui->jupiterElevation->setText(QString("%1%2").arg((int) std::round(report.getElevation())).arg(QChar(0xb0)));
|
|
ui->cml->setText(QString("%1%2").arg((int) std::round(report.getCML())).arg(QChar(0xb0)));
|
|
ui->ioPhase->setText(QString("%1%2").arg((int) std::round(report.getIoPhase())).arg(QChar(0xb0)));
|
|
ui->ganymedePhase->setText(QString("%1%2").arg((int) std::round(report.getGanymedePhase())).arg(QChar(0xb0)));
|
|
updateJupiterMoonPosition(report.getCML(), report.getIoPhase(), report.getGanymedePhase());
|
|
return true;
|
|
}
|
|
else if (StarTrackerReport::MsgReportJupiterData::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportJupiterData& report = (StarTrackerReport::MsgReportJupiterData&) message;
|
|
updateJupiterMoonPositions(report);
|
|
return true;
|
|
}
|
|
else if (MainCore::MsgStarTrackerDisplaySettings::match(message))
|
|
{
|
|
if (m_settings.m_link)
|
|
{
|
|
MainCore::MsgStarTrackerDisplaySettings& settings = (MainCore::MsgStarTrackerDisplaySettings&) message;
|
|
SWGSDRangel::SWGStarTrackerDisplaySettings *swgSettings = settings.getSWGStarTrackerDisplaySettings();
|
|
ui->dateTimeSelect->setCurrentText("Custom");
|
|
QDateTime dt = QDateTime::fromString(*swgSettings->getDateTime(), Qt::ISODateWithMs);
|
|
ui->dateTime->setDateTime(dt);
|
|
ui->target->setCurrentText("Custom Az/El");
|
|
blockPlotChart();
|
|
ui->azimuth->setValue(swgSettings->getAzimuth());
|
|
ui->elevation->setValue(swgSettings->getElevation());
|
|
unblockPlotChartAndPlot();
|
|
}
|
|
return true;
|
|
}
|
|
else if (MainCore::MsgStarTrackerDisplayLoSSettings::match(message))
|
|
{
|
|
MainCore::MsgStarTrackerDisplayLoSSettings& settings = (MainCore::MsgStarTrackerDisplayLoSSettings&) message;
|
|
SWGSDRangel::SWGStarTrackerDisplayLoSSettings *swgSettings = settings.getSWGStarTrackerDisplayLoSSettings();
|
|
bool found = false;
|
|
for (int i = 0; i < m_lineOfSightMarkers.size(); i++)
|
|
{
|
|
if (m_lineOfSightMarkers[i]->m_name == *swgSettings->getName())
|
|
{
|
|
if (swgSettings->getD() == 0.0)
|
|
{
|
|
// Delete
|
|
ui->image->scene()->removeItem(m_lineOfSightMarkers[i]->m_text);
|
|
delete m_lineOfSightMarkers[i]->m_text;
|
|
delete m_lineOfSightMarkers[i];
|
|
m_lineOfSightMarkers.removeAt(i);
|
|
}
|
|
else
|
|
{
|
|
// Update
|
|
m_lineOfSightMarkers[i]->m_l = swgSettings->getL();
|
|
m_lineOfSightMarkers[i]->m_b = swgSettings->getB();
|
|
m_lineOfSightMarkers[i]->m_d = swgSettings->getD();
|
|
plotGalacticMarker(m_lineOfSightMarkers[i]);
|
|
}
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found && (swgSettings->getD() != 0.0))
|
|
{
|
|
// Create new
|
|
LoSMarker* marker = new LoSMarker();
|
|
marker->m_name = *swgSettings->getName();
|
|
marker->m_l = swgSettings->getL();
|
|
marker->m_b = swgSettings->getB();
|
|
marker->m_d = swgSettings->getD();
|
|
marker->m_text = ui->image->scene()->addText(marker->m_name);
|
|
m_lineOfSightMarkers.append(marker);
|
|
plotGalacticMarker(marker);
|
|
}
|
|
return true;
|
|
}
|
|
else if (StarTracker::MsgReportAvailableFeatures::match(message))
|
|
{
|
|
StarTracker::MsgReportAvailableFeatures& report = (StarTracker::MsgReportAvailableFeatures&) message;
|
|
updateFeatureList(report.getFeatures());
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void StarTrackerGUI::updateFeatureList(const AvailableChannelOrFeatureList& features)
|
|
{
|
|
// Update list of plugins we can get target from
|
|
ui->target->blockSignals(true);
|
|
|
|
if (m_settings.m_targetSource == "SDRangel")
|
|
{
|
|
// 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") || text.contains("SkyMap"))
|
|
{
|
|
for (const auto& feature : features)
|
|
{
|
|
if (feature.getLongId() == text)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
ui->target->removeItem(i);
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// Add new targets
|
|
for (const auto& feature : features)
|
|
{
|
|
QString name = feature.getLongId();
|
|
if (ui->target->findText(name) == -1) {
|
|
ui->target->addItem(name);
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
ui->target->setCurrentIndex(index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Save feature list for use in updateTargetList
|
|
m_availableFeatures.clear();
|
|
for (const auto& feature : features) {
|
|
m_availableFeatures.append(feature.getLongId());
|
|
}
|
|
}
|
|
|
|
ui->target->blockSignals(false);
|
|
}
|
|
|
|
void StarTrackerGUI::handleInputMessages()
|
|
{
|
|
Message* message;
|
|
|
|
while ((message = getInputMessageQueue()->pop()))
|
|
{
|
|
if (!handleMessage(*message)) {
|
|
qDebug("%s: unhandled message: %s", Q_FUNC_INFO, message->getIdentifier());
|
|
}
|
|
delete message;
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
|
{
|
|
(void) widget;
|
|
(void) rollDown;
|
|
|
|
RollupContents *rollupContents = getRollupContents();
|
|
|
|
rollupContents->saveState(m_rollupState);
|
|
applySettings();
|
|
}
|
|
|
|
StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
|
|
FeatureGUI(parent),
|
|
ui(new Ui::StarTrackerGUI),
|
|
m_pluginAPI(pluginAPI),
|
|
m_featureUISet(featureUISet),
|
|
m_doApplySettings(true),
|
|
m_doPlotChart(true),
|
|
m_lastFeatureState(0),
|
|
m_azElLineChart(nullptr),
|
|
m_azElPolarChart(nullptr),
|
|
m_solarFluxChart(nullptr),
|
|
m_networkManager(nullptr),
|
|
m_startAfterDownload(false),
|
|
m_jplHorizons(nullptr),
|
|
m_spiceEphemerides(this),
|
|
m_solarFlux(0.0),
|
|
m_solarFluxesValid(false),
|
|
m_images{QImage(":/startracker/startracker/150mhz_ra_dec.png"),
|
|
QImage(":/startracker/startracker/150mhz_galactic.png"),
|
|
QImage(":/startracker/startracker/408mhz_ra_dec.png"),
|
|
QImage(":/startracker/startracker/408mhz_galactic.png"),
|
|
QImage(":/startracker/startracker/1420mhz_ra_dec.png"),
|
|
QImage(":/startracker/startracker/1420mhz_galactic.png")},
|
|
m_zoom(nullptr),
|
|
m_milkyWayImages{QPixmap(":/startracker/startracker/milkyway.png"),
|
|
QPixmap(":/startracker/startracker/milkywayannotated.png")},
|
|
m_solarSystemLabelFontMetrics(font()),
|
|
m_planetImages{
|
|
{"callisto", QPixmap(":/startracker/startracker/callisto-250.png")},
|
|
{"deimos", QPixmap(":/startracker/startracker/deimos-250.png")},
|
|
{"earth", QPixmap(":/startracker/startracker/earth-250.png")},
|
|
{"ganymede", QPixmap(":/startracker/startracker/ganymede-250.png")},
|
|
{"io", QPixmap(":/startracker/startracker/io-250.png")},
|
|
{"jupiter", QPixmap(":/startracker/startracker/jupiter-250.png")},
|
|
{"mars", QPixmap(":/startracker/startracker/mars-250.png")},
|
|
{"mercury", QPixmap(":/startracker/startracker/mercury-250.png")},
|
|
{"moon", QPixmap(":/startracker/startracker/moon-250.png")},
|
|
{"neptune", QPixmap(":/startracker/startracker/neptune-250.png")},
|
|
{"phobos", QPixmap(":/startracker/startracker/phobos-250.png")},
|
|
{"pluto", QPixmap(":/startracker/startracker/pluto-250.png")},
|
|
{"saturn", QPixmap(":/startracker/startracker/saturn-250.png")},
|
|
{"sun", QPixmap(":/startracker/startracker/sun-250.png")},
|
|
{"uranus", QPixmap(":/startracker/startracker/uranus-250.png")},
|
|
{"venus", QPixmap(":/startracker/startracker/venus-250.png")}
|
|
},
|
|
m_jupiterImages{QPixmap(":/startracker/startracker/io-phase-vs-cml.png"),
|
|
QPixmap(":/startracker/startracker/ganymede-phase-vs-cml.png"),
|
|
QPixmap(":/startracker/startracker/phase-cml-legend.png")
|
|
},
|
|
m_sunRA(0.0),
|
|
m_sunDec(0.0),
|
|
m_moonRA(0.0),
|
|
m_moonDec(0.0)
|
|
{
|
|
m_feature = feature;
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
m_helpURL = "plugins/feature/startracker/readme.md";
|
|
RollupContents *rollupContents = getRollupContents();
|
|
ui->setupUi(rollupContents);
|
|
rollupContents->arrangeRollups();
|
|
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
|
|
|
m_starTracker = reinterpret_cast<StarTracker*>(feature);
|
|
m_starTracker->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_dlm, &HttpDownloadManager::downloadComplete, this, &StarTrackerGUI::downloadFinished);
|
|
|
|
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
|
m_statusTimer.start(1000);
|
|
|
|
connect(&m_redrawTimer, &QTimer::timeout, this, &StarTrackerGUI::plotChart);
|
|
|
|
connect(ui->azimuth, SIGNAL(valueChanged(double)), this, SLOT(on_azimuth_valueChanged(double)));
|
|
ui->azimuth->setRange(0, 360.0);
|
|
ui->elevation->setRange(-90.0, 90.0);
|
|
ui->galacticLongitude->setRange(0, 360.0);
|
|
ui->galacticLatitude->setRange(-90.0, 90.0);
|
|
ui->galacticLatitude->setText("");
|
|
ui->galacticLongitude->setText("");
|
|
|
|
// Initialise chart
|
|
m_chart.legend()->hide();
|
|
ui->chart->setChart(&m_chart);
|
|
ui->chart->setRenderHint(QPainter::Antialiasing);
|
|
m_chart.addAxis(&m_chartXAxis, Qt::AlignBottom);
|
|
m_chart.addAxis(&m_chartYAxis, Qt::AlignLeft);
|
|
m_chart.layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_chart.setMargins(QMargins(1, 1, 1, 1));
|
|
|
|
// Create axes that are static
|
|
|
|
m_skyTempGalacticLXAxis.setTitleText(QString("Galactic longitude (%1)").arg(QChar(0xb0)));
|
|
m_skyTempGalacticLXAxis.setMin(0);
|
|
m_skyTempGalacticLXAxis.setMax(360);
|
|
m_skyTempGalacticLXAxis.append("180", 0);
|
|
m_skyTempGalacticLXAxis.append("90", 90);
|
|
m_skyTempGalacticLXAxis.append("0/360", 180);
|
|
m_skyTempGalacticLXAxis.append("270", 270);
|
|
//m_skyTempGalacticLXAxis.append("180", 360); // Note - labels need to be unique, so can't have 180 at start and end
|
|
m_skyTempGalacticLXAxis.setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
|
|
m_skyTempGalacticLXAxis.setGridLineVisible(false);
|
|
|
|
m_skyTempRAXAxis.setTitleText(QString("Right ascension (hours)"));
|
|
m_skyTempRAXAxis.setMin(0);
|
|
m_skyTempRAXAxis.setMax(24);
|
|
m_skyTempRAXAxis.append("12", 0);
|
|
m_skyTempRAXAxis.append("9", 3);
|
|
m_skyTempRAXAxis.append("6", 6);
|
|
m_skyTempRAXAxis.append("3", 9);
|
|
m_skyTempRAXAxis.append("0", 12);
|
|
m_skyTempRAXAxis.append("21", 15);
|
|
m_skyTempRAXAxis.append("18", 18);
|
|
m_skyTempRAXAxis.append("15", 21);
|
|
//m_skyTempRAXAxis.append("12", 24); // Note - labels need to be unique, so can't have 12 at start and end
|
|
m_skyTempRAXAxis.setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
|
|
m_skyTempRAXAxis.setGridLineVisible(false);
|
|
|
|
m_skyTempYAxis.setGridLineVisible(false);
|
|
m_skyTempYAxis.setRange(-90.0, 90.0);
|
|
m_skyTempYAxis.setGridLineVisible(false);
|
|
|
|
ui->dateTime->setDateTime(QDateTime::currentDateTime());
|
|
displaySettings();
|
|
applySettings(true);
|
|
disconnect(ui->azimuth, SIGNAL(valueChanged(double)), this, SLOT(on_azimuth_valueChanged(double)));
|
|
makeUIConnections();
|
|
m_resizer.enableChildMouseTracking();
|
|
|
|
// Populate subchart menu
|
|
on_chartSelect_currentIndexChanged(0);
|
|
|
|
connect(&m_chart, SIGNAL(plotAreaChanged(QRectF)), this, SLOT(plotAreaChanged(QRectF)));
|
|
|
|
// Use My Position from preferences, if none set
|
|
if ((m_settings.m_latitude == 0.0) && (m_settings.m_longitude == 0.0)) {
|
|
on_useMyPosition_clicked();
|
|
}
|
|
|
|
/*
|
|
printf("saemundsson=[");
|
|
for (int i = 0; i <= 90; i+= 5)
|
|
printf("%f ", Astronomy::refractionSaemundsson(i, m_settings.m_pressure, m_settings.m_temperature));
|
|
printf("];\n");
|
|
printf("palRadio=[");
|
|
for (int i = 0; i <= 90; i+= 5)
|
|
printf("%f ", Astronomy::refractionPAL(i, m_settings.m_pressure, m_settings.m_temperature, m_settings.m_humidity,
|
|
100000000, m_settings.m_latitude, m_settings.m_heightAboveSeaLevel,
|
|
m_settings.m_temperatureLapseRate));
|
|
printf("];\n");
|
|
printf("palLight=[");
|
|
for (int i = 0; i <= 90; i+= 5)
|
|
printf("%f ",Astronomy::refractionPAL(i, m_settings.m_pressure, m_settings.m_temperature, m_settings.m_humidity,
|
|
7.5e14, m_settings.m_latitude, m_settings.m_heightAboveSeaLevel,
|
|
m_settings.m_temperatureLapseRate));
|
|
printf("];\n");
|
|
*/
|
|
|
|
m_networkManager = new QNetworkAccessManager();
|
|
QObject::connect(
|
|
m_networkManager,
|
|
&QNetworkAccessManager::finished,
|
|
this,
|
|
&StarTrackerGUI::networkManagerFinished
|
|
);
|
|
|
|
readSolarFlux();
|
|
connect(&m_solarFluxTimer, SIGNAL(timeout()), this, SLOT(autoUpdateSolarFlux()));
|
|
m_solarFluxTimer.start(1000*60*60*24); // Update every 24hours
|
|
autoUpdateSolarFlux();
|
|
|
|
createGalacticLineOfSightScene();
|
|
createSolarSystemScene();
|
|
createJupiterScene();
|
|
plotChart();
|
|
|
|
StarTracker::MsgRequestAvailableFeatures *message = StarTracker::MsgRequestAvailableFeatures::create();
|
|
m_starTracker->getInputMessageQueue()->push(message);
|
|
|
|
connect(&m_spiceEphemerides, &SpiceEphemerides::allDownloadsComplete, this, &StarTrackerGUI::spiceDownloadsComplete);
|
|
|
|
m_jplHorizons = JPLHorizons::create();
|
|
if (m_jplHorizons)
|
|
{
|
|
connect(m_jplHorizons, &JPLHorizons::majorBodiesUpdated, this, &StarTrackerGUI::majorBodiesUpdated);
|
|
m_jplHorizons->getMajorBodiesList();
|
|
}
|
|
}
|
|
|
|
StarTrackerGUI::~StarTrackerGUI()
|
|
{
|
|
if (m_jplHorizons)
|
|
{
|
|
disconnect(m_jplHorizons, &JPLHorizons::majorBodiesUpdated, this, &StarTrackerGUI::majorBodiesUpdated);
|
|
delete m_jplHorizons;
|
|
}
|
|
QObject::disconnect(
|
|
m_networkManager,
|
|
&QNetworkAccessManager::finished,
|
|
this,
|
|
&StarTrackerGUI::networkManagerFinished
|
|
);
|
|
delete m_networkManager;
|
|
delete ui;
|
|
}
|
|
|
|
void StarTrackerGUI::setWorkspaceIndex(int index)
|
|
{
|
|
m_settings.m_workspaceIndex = index;
|
|
m_settingsKeys.append("workspaceIndex");
|
|
m_feature->setWorkspaceIndex(index);
|
|
}
|
|
|
|
void StarTrackerGUI::blockApplySettings(bool block)
|
|
{
|
|
m_doApplySettings = !block;
|
|
}
|
|
|
|
void StarTrackerGUI::blockPlotChart()
|
|
{
|
|
m_doPlotChart = false;
|
|
}
|
|
|
|
void StarTrackerGUI::unblockPlotChartAndPlot()
|
|
{
|
|
m_doPlotChart = true;
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::displaySettings()
|
|
{
|
|
setTitleColor(m_settings.m_rgbColor);
|
|
setWindowTitle(m_settings.m_title);
|
|
setTitle(m_settings.m_title);
|
|
blockApplySettings(true);
|
|
blockPlotChart();
|
|
ui->darkTheme->setChecked(m_settings.m_chartsDarkTheme);
|
|
|
|
if (m_solarFluxChart) {
|
|
m_solarFluxChart->setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
|
}
|
|
|
|
m_chart.setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
|
ui->night->setChecked(m_settings.m_night);
|
|
ui->logScale->setChecked(m_settings.m_logScale);
|
|
ui->drawSun->setChecked(m_settings.m_drawSunOnSkyTempChart);
|
|
ui->drawMoon->setChecked(m_settings.m_drawMoonOnSkyTempChart);
|
|
ui->link->setChecked(m_settings.m_link);
|
|
ui->latitude->setValue(m_settings.m_latitude);
|
|
ui->longitude->setValue(m_settings.m_longitude);
|
|
ui->targetSource->setCurrentIndex(ui->targetSource->findText(m_settings.m_targetSource));
|
|
ui->target->setEditable(m_settings.m_targetSource == "Horizons");
|
|
if (ui->target->lineEdit()) {
|
|
QObject::connect(ui->target->lineEdit(), &QLineEdit::editingFinished, this, &StarTrackerGUI::on_target_editingFinished, Qt::UniqueConnection);
|
|
}
|
|
ui->target->setCurrentIndex(ui->target->findText(m_settings.m_target));
|
|
ui->azimuth->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
|
|
ui->elevation->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
|
|
ui->galacticLatitude->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
|
|
ui->galacticLongitude->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
|
|
ui->azimuthOffset->setValue(m_settings.m_azimuthOffset);
|
|
ui->elevationOffset->setValue(m_settings.m_elevationOffset);
|
|
|
|
if (m_settings.m_target == "Custom RA/Dec")
|
|
{
|
|
ui->rightAscension->setText(m_settings.m_ra);
|
|
ui->declination->setText(m_settings.m_dec);
|
|
}
|
|
else if (m_settings.m_target == "Custom Az/El")
|
|
{
|
|
ui->azimuth->setValue(m_settings.m_az);
|
|
ui->elevation->setValue(m_settings.m_el);
|
|
}
|
|
else if ((m_settings.m_target == "Custom l/b")
|
|
|| (m_settings.m_target == "S7")
|
|
|| (m_settings.m_target == "S8")
|
|
|| (m_settings.m_target == "S9")
|
|
)
|
|
{
|
|
ui->galacticLatitude->setValue(m_settings.m_b);
|
|
ui->galacticLongitude->setValue(m_settings.m_l);
|
|
}
|
|
if (m_settings.m_dateTime == "")
|
|
{
|
|
ui->dateTimeSelect->setCurrentIndex(0);
|
|
ui->dateTime->setVisible(false);
|
|
ui->utc->setVisible(false);
|
|
ui->setTimeToNow->setVisible(false);
|
|
}
|
|
else
|
|
{
|
|
ui->dateTime->setDateTime(QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs));
|
|
ui->dateTime->setVisible(true);
|
|
ui->dateTimeSelect->setCurrentIndex(1);
|
|
ui->utc->setVisible(true);
|
|
ui->setTimeToNow->setVisible(true);
|
|
}
|
|
ui->utc->setChecked(m_settings.m_utc);
|
|
|
|
if ((m_settings.m_solarFluxData != StarTrackerSettings::DRAO_2800) && !m_solarFluxesValid) {
|
|
autoUpdateSolarFlux();
|
|
}
|
|
|
|
ui->chartSelect->setCurrentIndex((int) m_settings.m_chartSelect);
|
|
ui->chartSubSelect->setCurrentIndex(m_settings.m_chartSubSelect);
|
|
|
|
ui->frequency->setValue(m_settings.m_frequency/1000000.0);
|
|
ui->beamwidth->setValue(m_settings.m_beamwidth);
|
|
updateForTarget();
|
|
getRollupContents()->restoreState(m_rollupState);
|
|
unblockPlotChartAndPlot();
|
|
blockApplySettings(false);
|
|
}
|
|
|
|
void StarTrackerGUI::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 StarTrackerGUI::downloadSPICEEphemerides()
|
|
{
|
|
m_spiceEphemerides.download(m_settings.m_spiceEphemerides);
|
|
}
|
|
|
|
bool StarTrackerGUI::checkSPICEEphemerides()
|
|
{
|
|
return m_spiceEphemerides.checkDownloaded(m_settings.m_spiceEphemerides);
|
|
}
|
|
|
|
void StarTrackerGUI::spiceDownloadsComplete()
|
|
{
|
|
updateTargetList();
|
|
on_chartSelect_currentIndexChanged(ui->chartSelect->currentIndex());
|
|
|
|
if (m_startAfterDownload)
|
|
{
|
|
if (checkSPICEEphemerides())
|
|
{
|
|
m_startAfterDownload = false;
|
|
StarTracker::MsgStartStop *message = StarTracker::MsgStartStop::create(true);
|
|
m_starTracker->getInputMessageQueue()->push(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_startStop_toggled(bool checked)
|
|
{
|
|
if (m_doApplySettings)
|
|
{
|
|
if (checked)
|
|
{
|
|
if (checkSPICEEphemerides())
|
|
{
|
|
StarTracker::MsgStartStop *message = StarTracker::MsgStartStop::create(checked);
|
|
m_starTracker->getInputMessageQueue()->push(message);
|
|
}
|
|
else
|
|
{
|
|
m_startAfterDownload = true;
|
|
downloadSPICEEphemerides();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StarTracker::MsgStartStop *message = StarTracker::MsgStartStop::create(checked);
|
|
m_starTracker->getInputMessageQueue()->push(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_latitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_latitude = value;
|
|
m_settingsKeys.append("latitude");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_longitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_longitude = value;
|
|
m_settingsKeys.append("longitude");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_rightAscension_editingFinished()
|
|
{
|
|
m_settings.m_ra = ui->rightAscension->text();
|
|
m_settingsKeys.append("ra");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_declination_editingFinished()
|
|
{
|
|
m_settings.m_dec = ui->declination->text();
|
|
m_settingsKeys.append("dec");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_azimuth_valueChanged(double value)
|
|
{
|
|
m_settings.m_az = value;
|
|
m_settingsKeys.append("azimuth");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_elevation_valueChanged(double value)
|
|
{
|
|
m_settings.m_el = value;
|
|
m_settingsKeys.append("elevation");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_azimuthOffset_valueChanged(double value)
|
|
{
|
|
m_settings.m_azimuthOffset = value;
|
|
m_settingsKeys.append("azimuthOffset");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_elevationOffset_valueChanged(double value)
|
|
{
|
|
m_settings.m_elevationOffset = value;
|
|
m_settingsKeys.append("elevationOffset");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_galacticLatitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_b = value;
|
|
m_settingsKeys.append("b");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_galacticLongitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_l = value;
|
|
m_settingsKeys.append("l");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::updateForTarget()
|
|
{
|
|
const QStringList raDecTargets = {
|
|
"PSR B0329+54", "PSR B0833-45", "Sagittarius A", "Cassiopeia A", "Cygnus A", "Taurus A (M1)", "Virgo A (M87)"
|
|
};
|
|
const QStringList lbTargets = {
|
|
"S7", "S8", "S9"
|
|
};
|
|
|
|
if ((m_settings.m_target == "Sun") || (m_settings.m_target == "Moon"))
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->azimuth->setReadOnly(true);
|
|
ui->elevation->setReadOnly(true);
|
|
ui->galacticLatitude->setReadOnly(true);
|
|
ui->galacticLongitude->setReadOnly(true);
|
|
ui->rightAscension->setText("");
|
|
ui->declination->setText("");
|
|
}
|
|
else if (raDecTargets.contains(m_settings.m_target))
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->azimuth->setReadOnly(true);
|
|
ui->elevation->setReadOnly(true);
|
|
ui->galacticLatitude->setReadOnly(true);
|
|
ui->galacticLongitude->setReadOnly(true);
|
|
if (m_settings.m_target == "PSR B0329+54")
|
|
{
|
|
ui->rightAscension->setText("03h32m59.35s");
|
|
ui->declination->setText(QString("54%0134'45.05\"").arg(QChar(0xb0)));
|
|
}
|
|
else if (m_settings.m_target == "PSR B0833-45")
|
|
{
|
|
ui->rightAscension->setText("08h35m20.66s");
|
|
ui->declination->setText(QString("-45%0110'35.15\"").arg(QChar(0xb0)));
|
|
}
|
|
else if (m_settings.m_target == "Sagittarius A")
|
|
{
|
|
ui->rightAscension->setText("17h45m40.04s");
|
|
ui->declination->setText(QString("-29%0100'28.17\"").arg(QChar(0xb0)));
|
|
}
|
|
else if (m_settings.m_target == "Cassiopeia A")
|
|
{
|
|
ui->rightAscension->setText("23h23m24s");
|
|
ui->declination->setText(QString("58%0148'54\"").arg(QChar(0xb0)));
|
|
}
|
|
else if (m_settings.m_target == "Cygnus A")
|
|
{
|
|
ui->rightAscension->setText("19h59m28.36s");
|
|
ui->declination->setText(QString("40%0144'02.1\"").arg(QChar(0xb0)));
|
|
}
|
|
else if (m_settings.m_target == "Taurus A (M1)")
|
|
{
|
|
ui->rightAscension->setText("05h34m31.94s");
|
|
ui->declination->setText(QString("22%0100'52.2\"").arg(QChar(0xb0)));
|
|
}
|
|
else if (m_settings.m_target == "Virgo A (M87)")
|
|
{
|
|
ui->rightAscension->setText("12h30m49.42s");
|
|
ui->declination->setText(QString("12%0123'28.04\"").arg(QChar(0xb0)));
|
|
}
|
|
on_rightAscension_editingFinished();
|
|
on_declination_editingFinished();
|
|
}
|
|
else if (lbTargets.contains(m_settings.m_target))
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->azimuth->setReadOnly(true);
|
|
ui->elevation->setReadOnly(true);
|
|
ui->galacticLatitude->setReadOnly(true);
|
|
ui->galacticLongitude->setReadOnly(true);
|
|
ui->rightAscension->setText("");
|
|
ui->declination->setText("");
|
|
if (m_settings.m_target == "S7")
|
|
{
|
|
ui->galacticLatitude->setValue(-1.0);
|
|
ui->galacticLongitude->setValue(132.0);
|
|
}
|
|
else if (m_settings.m_target == "S8")
|
|
{
|
|
ui->galacticLatitude->setValue(-15.0);
|
|
ui->galacticLongitude->setValue(207.0);
|
|
}
|
|
else if (m_settings.m_target == "S9")
|
|
{
|
|
ui->galacticLatitude->setValue(-4.0);
|
|
ui->galacticLongitude->setValue(356.0);
|
|
}
|
|
}
|
|
else if (m_settings.m_target == "Custom RA/Dec")
|
|
{
|
|
ui->rightAscension->setReadOnly(false);
|
|
ui->declination->setReadOnly(false);
|
|
ui->azimuth->setReadOnly(true);
|
|
ui->elevation->setReadOnly(true);
|
|
ui->galacticLatitude->setReadOnly(true);
|
|
ui->galacticLongitude->setReadOnly(true);
|
|
m_settingsKeys.append("ra");
|
|
m_settingsKeys.append("dec");
|
|
applySettings();
|
|
}
|
|
else if (m_settings.m_target == "Custom Az/El")
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->azimuth->setReadOnly(false);
|
|
ui->elevation->setReadOnly(false);
|
|
ui->galacticLatitude->setReadOnly(true);
|
|
ui->galacticLongitude->setReadOnly(true);
|
|
ui->rightAscension->setText("");
|
|
ui->declination->setText("");
|
|
m_settingsKeys.append("azimuth");
|
|
m_settingsKeys.append("elevation");
|
|
applySettings();
|
|
}
|
|
else if (m_settings.m_target == "Custom l/b")
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->azimuth->setReadOnly(true);
|
|
ui->elevation->setReadOnly(true);
|
|
ui->galacticLatitude->setReadOnly(false);
|
|
ui->galacticLongitude->setReadOnly(false);
|
|
ui->rightAscension->setText("");
|
|
ui->declination->setText("");
|
|
m_settingsKeys.append("l");
|
|
m_settingsKeys.append("b");
|
|
applySettings();
|
|
}
|
|
else if (m_settings.m_target.contains("SatelliteTracker"))
|
|
{
|
|
ui->azimuth->setReadOnly(true);
|
|
ui->elevation->setReadOnly(true);
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->galacticLatitude->setReadOnly(true);
|
|
ui->galacticLongitude->setReadOnly(true);
|
|
ui->rightAscension->setText("");
|
|
ui->declination->setText("");
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_target_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
|
|
QString text = ui->target->currentText();
|
|
if (!text.isEmpty())
|
|
{
|
|
m_settings.m_target = text;
|
|
m_settingsKeys.append("target");
|
|
applySettings();
|
|
updateForTarget();
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_target_editingFinished()
|
|
{
|
|
QString text = ui->target->currentText();
|
|
if (!text.isEmpty())
|
|
{
|
|
m_settings.m_target = text;
|
|
m_settingsKeys.append("target");
|
|
applySettings();
|
|
updateForTarget();
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_targetSource_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
|
|
m_settings.m_targetSource = ui->targetSource->currentText();
|
|
m_settingsKeys.append("targetSource");
|
|
applySettings();
|
|
|
|
if (m_settings.m_targetSource == "SPICE")
|
|
{
|
|
if (!checkSPICEEphemerides()) {
|
|
downloadSPICEEphemerides();
|
|
}
|
|
}
|
|
|
|
ui->target->setEditable(m_settings.m_targetSource == "Horizons");
|
|
if (ui->target->lineEdit()) {
|
|
QObject::connect(ui->target->lineEdit(), &QLineEdit::editingFinished, this, &StarTrackerGUI::on_target_editingFinished, Qt::UniqueConnection);
|
|
}
|
|
updateTargetList();
|
|
on_target_currentIndexChanged(ui->target->currentIndex()); // updateTargetList blocks signals, so update target manually
|
|
}
|
|
|
|
void StarTrackerGUI::updateLST()
|
|
{
|
|
QDateTime dt;
|
|
|
|
if (m_settings.m_dateTime.isEmpty()) {
|
|
dt = QDateTime::currentDateTime();
|
|
}
|
|
else
|
|
{
|
|
dt = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
if (m_settings.m_utc) {
|
|
dt.setTimeZone(QTimeZone::utc());
|
|
}
|
|
}
|
|
|
|
double lst = Astronomy::localSiderealTime(dt, m_settings.m_longitude);
|
|
ui->lst->setText(Units::decimalHoursToHoursMinutesAndSeconds(lst/15.0, 0));
|
|
}
|
|
|
|
void StarTrackerGUI::updateStatus()
|
|
{
|
|
int state = m_starTracker->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_starTracker->getErrorMessage());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_lastFeatureState = state;
|
|
}
|
|
|
|
updateLST();
|
|
}
|
|
|
|
void StarTrackerGUI::applySettings(bool force)
|
|
{
|
|
if (m_doApplySettings)
|
|
{
|
|
StarTracker::MsgConfigureStarTracker* message = StarTracker::MsgConfigureStarTracker::create(m_settings, m_settingsKeys, force);
|
|
m_starTracker->getInputMessageQueue()->push(message);
|
|
}
|
|
|
|
m_settingsKeys.clear();
|
|
}
|
|
|
|
void StarTrackerGUI::on_link_clicked(bool checked)
|
|
{
|
|
m_settings.m_link = checked;
|
|
m_settingsKeys.append("link");
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::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 StarTrackerGUI::on_displaySettings_clicked()
|
|
{
|
|
StarTrackerSettingsDialog dialog(&m_settings, m_settingsKeys);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
applySettings();
|
|
ui->elevation->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
|
|
ui->azimuth->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
|
|
ui->galacticLatitude->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
|
|
ui->galacticLongitude->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
|
|
displaySolarFlux();
|
|
|
|
if (ui->chartSelect->currentIndex() <= StarTrackerSettings::CHART_SOLAR_FLUX_VS_FREQUENCY) {
|
|
plotChart();
|
|
}
|
|
if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_SOLAR_SYSTEM) {
|
|
on_chartSelect_currentIndexChanged(ui->chartSelect->currentIndex()); // Update list of bodies
|
|
}
|
|
|
|
checkSPICEEphemerides();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_dateTimeSelect_currentTextChanged(const QString &text)
|
|
{
|
|
if (text == "Now")
|
|
{
|
|
m_settings.m_dateTime = "";
|
|
ui->dateTime->setVisible(false);
|
|
ui->utc->setVisible(false);
|
|
ui->setTimeToNow->setVisible(false);
|
|
}
|
|
else
|
|
{
|
|
m_settings.m_dateTime = ui->dateTime->dateTime().toString(Qt::ISODateWithMs);
|
|
ui->dateTime->setVisible(true);
|
|
ui->utc->setVisible(true);
|
|
ui->setTimeToNow->setVisible(true);
|
|
}
|
|
|
|
m_settingsKeys.append("dateTime");
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::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();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_utc_clicked(bool checked)
|
|
{
|
|
m_settings.m_utc = checked;
|
|
m_settingsKeys.append("utc");
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::on_setTimeToNow_clicked(bool checked)
|
|
{
|
|
(void) checked;
|
|
|
|
ui->dateTime->setDateTime(QDateTime::currentDateTime());
|
|
}
|
|
|
|
void StarTrackerGUI::plotChart()
|
|
{
|
|
if (!m_doPlotChart) {
|
|
return;
|
|
}
|
|
if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_ELEVATION_VS_TIME)
|
|
{
|
|
if (ui->chartSubSelect->currentIndex() == 0) {
|
|
plotElevationLineChart();
|
|
} else {
|
|
plotElevationPolarChart();
|
|
}
|
|
}
|
|
else if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_SOLAR_FLUX_VS_FREQUENCY)
|
|
{
|
|
plotSolarFluxChart();
|
|
}
|
|
else if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_SKY_TEMPERATURE)
|
|
{
|
|
plotSkyTemperatureChart();
|
|
}
|
|
else if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_GALACTIC_LINE_OF_SIGHT)
|
|
{
|
|
plotGalacticLineOfSight();
|
|
}
|
|
else if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_SOLAR_SYSTEM)
|
|
{
|
|
plotSolarSystem();
|
|
}
|
|
else if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_JUPITER)
|
|
{
|
|
plotJupiter();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::raDecChanged()
|
|
{
|
|
if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_SKY_TEMPERATURE) {
|
|
plotSkyTemperatureChart();
|
|
} else if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_GALACTIC_LINE_OF_SIGHT) {
|
|
plotGalacticLineOfSight();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_frequency_valueChanged(int value)
|
|
{
|
|
m_settings.m_frequency = value*1000000.0;
|
|
m_settingsKeys.append("frequency");
|
|
applySettings();
|
|
|
|
if (ui->chartSelect->currentIndex() != 0)
|
|
{
|
|
updateChartSubSelect();
|
|
plotChart();
|
|
}
|
|
|
|
displaySolarFlux();
|
|
}
|
|
|
|
void StarTrackerGUI::on_beamwidth_valueChanged(double value)
|
|
{
|
|
m_settings.m_beamwidth = value;
|
|
m_settingsKeys.append("beamwidth");
|
|
applySettings();
|
|
updateChartSubSelect();
|
|
|
|
if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_SKY_TEMPERATURE) {
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::plotSolarFluxChart()
|
|
{
|
|
ui->chart->setVisible(true);
|
|
ui->image->setVisible(false);
|
|
ui->night->setVisible(false);
|
|
ui->logScale->setVisible(false);
|
|
ui->drawSun->setVisible(false);
|
|
ui->drawMoon->setVisible(false);
|
|
ui->darkTheme->setVisible(true);
|
|
ui->zoomIn->setVisible(false);
|
|
ui->zoomOut->setVisible(false);
|
|
ui->addAnimationFrame->setVisible(false);
|
|
ui->clearAnimation->setVisible(false);
|
|
ui->saveAnimation->setVisible(false);
|
|
ui->jupiterElevationLabel->setVisible(false);
|
|
ui->jupiterElevation->setVisible(false);
|
|
ui->cmlLabel->setVisible(false);
|
|
ui->cml->setVisible(false);
|
|
ui->ioPhaseLabel->setVisible(false);
|
|
ui->ioPhase->setVisible(false);
|
|
ui->ganymedePhaseLabel->setVisible(false);
|
|
ui->ganymedePhase->setVisible(false);
|
|
|
|
QChart *oldChart = m_solarFluxChart;
|
|
|
|
m_solarFluxChart = new QChart();
|
|
|
|
if (m_solarFluxesValid)
|
|
{
|
|
|
|
m_solarFluxChart->setTitle("");
|
|
m_solarFluxChart->legend()->hide();
|
|
m_solarFluxChart->layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_solarFluxChart->setMargins(QMargins(1, 1, 1, 1));
|
|
m_solarFluxChart->setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
|
|
|
double maxValue = -std::numeric_limits<double>::infinity();
|
|
double minValue = std::numeric_limits<double>::infinity();
|
|
QLineSeries *series = new QLineSeries();
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
double value = convertSolarFluxUnits(m_solarFluxes[i]);
|
|
series->append(m_solarFluxFrequencies[i], value);
|
|
maxValue = std::max(value, maxValue);
|
|
minValue = std::min(value, minValue);
|
|
}
|
|
series->setPointLabelsVisible(true);
|
|
series->setPointLabelsFormat("@yPoint");
|
|
series->setPointLabelsClipping(false);
|
|
m_solarFluxChart->addSeries(series);
|
|
|
|
QLogValueAxis *chartSolarFluxXAxis = new QLogValueAxis();
|
|
QValueAxis *chartSolarFluxYAxis = new QValueAxis();
|
|
|
|
chartSolarFluxXAxis->setTitleText(QString("Frequency (MHz)"));
|
|
chartSolarFluxXAxis->setMinorTickCount(-1);
|
|
|
|
chartSolarFluxYAxis->setTitleText(QString("Solar flux density (%1)").arg(solarFluxUnit()));
|
|
chartSolarFluxYAxis->setMinorTickCount(-1);
|
|
if (m_settings.m_solarFluxUnits == StarTrackerSettings::SFU)
|
|
{
|
|
chartSolarFluxYAxis->setLabelFormat("%d");
|
|
chartSolarFluxYAxis->setRange(0.0, ((((int)maxValue)+99)/100)*100);
|
|
}
|
|
else if (m_settings.m_solarFluxUnits == StarTrackerSettings::JANSKY)
|
|
{
|
|
chartSolarFluxYAxis->setLabelFormat("%.2g");
|
|
chartSolarFluxYAxis->setRange(0, ((((int)maxValue)+999999)/100000)*100000);
|
|
}
|
|
else
|
|
{
|
|
chartSolarFluxYAxis->setLabelFormat("%.2g");
|
|
// Bug in QtCharts for values < ~1e-12 https://bugreports.qt.io/browse/QTBUG-95304
|
|
// Set range to 0-1 here, then real range after axis have been attached
|
|
chartSolarFluxYAxis->setRange(0.0, 1.0);
|
|
}
|
|
|
|
m_solarFluxChart->addAxis(chartSolarFluxXAxis, Qt::AlignBottom);
|
|
m_solarFluxChart->addAxis(chartSolarFluxYAxis, Qt::AlignLeft);
|
|
series->attachAxis(chartSolarFluxXAxis);
|
|
series->attachAxis(chartSolarFluxYAxis);
|
|
|
|
if (m_settings.m_solarFluxUnits == StarTrackerSettings::WATTS_M_HZ) {
|
|
chartSolarFluxYAxis->setRange(minValue, maxValue);
|
|
}
|
|
|
|
}
|
|
else
|
|
m_solarFluxChart->setTitle("Press download Solar flux density data to view");
|
|
|
|
ui->chart->setChart(m_solarFluxChart);
|
|
|
|
delete oldChart;
|
|
}
|
|
|
|
QList<QLineSeries*> StarTrackerGUI::createDriftScan(bool galactic)
|
|
{
|
|
QList<QLineSeries *>list;
|
|
QLineSeries *series = new QLineSeries();
|
|
list.append(series);
|
|
|
|
QDateTime dt;
|
|
|
|
// Get date and time to calculate position at
|
|
dt = m_settings.getDateTime();
|
|
|
|
// Create a list of RA/Dec points of drift scan path
|
|
AzAlt aa;
|
|
aa.alt = m_settings.m_el;
|
|
aa.az = m_settings.m_az;
|
|
double prevX;
|
|
// Plot every 30min over a day
|
|
for (int i = 0; i <= 24*2; i++)
|
|
{
|
|
dt = dt.addSecs(30*60);
|
|
RADec rd = Astronomy::azAltToRaDec(aa, m_settings.m_latitude, m_settings.m_longitude, dt);
|
|
double x, y;
|
|
mapRaDec(rd.ra, rd.dec, galactic, x, y);
|
|
if (i == 0)
|
|
{
|
|
series->append(x, y);
|
|
}
|
|
else
|
|
{
|
|
// Check for crossing edge of chart
|
|
if (galactic)
|
|
{
|
|
if (((prevX < 90.0) && (x > 270.0)) || ((prevX > 270.0) && (x < 90.0)))
|
|
{
|
|
// Start new series, so we don't have lines crossing across the chart
|
|
series = new QLineSeries();
|
|
list.append(series);
|
|
}
|
|
}
|
|
series->append(x, y);
|
|
}
|
|
prevX = x;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
void StarTrackerGUI::mapRaDec(double ra, double dec, bool galactic, double& x, double& y)
|
|
{
|
|
if (galactic)
|
|
{
|
|
// Convert to category coordinates
|
|
double l, b;
|
|
Astronomy::equatorialToGalactic(ra, dec, l, b);
|
|
// Map to linear axis
|
|
double lAxis;
|
|
if (l < 180.0) {
|
|
lAxis = 180.0 - l;
|
|
} else {
|
|
lAxis = 360.0 - l + 180.0;
|
|
}
|
|
|
|
x = lAxis;
|
|
y = b;
|
|
}
|
|
else
|
|
{
|
|
// Map to category axis
|
|
double raAxis;
|
|
if (ra <= 12.0) {
|
|
raAxis = 12.0 - ra;
|
|
} else {
|
|
raAxis = 24 - ra + 12;
|
|
}
|
|
|
|
x = raAxis;
|
|
y = dec;
|
|
}
|
|
}
|
|
|
|
// Is there a way to get this from the theme? Got these values from the source
|
|
QColor StarTrackerGUI::getSeriesColor(int series)
|
|
{
|
|
if (m_settings.m_chartsDarkTheme)
|
|
{
|
|
if (series == 0) {
|
|
return QColor(0x38ad6b);
|
|
} else if (series == 1) {
|
|
return QColor(0x3c84a7);
|
|
} else {
|
|
return QColor(0xeb8817);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (series == 0) {
|
|
return QColor(0x209fdf);
|
|
} else if (series == 1) {
|
|
return QColor(0x99ca53);
|
|
} else {
|
|
return QColor(0xf6a625);
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::createGalacticLineOfSightScene()
|
|
{
|
|
m_galacticLineOfSightScene = new QGraphicsScene(ui->image);
|
|
m_galacticLineOfSightScene->setBackgroundBrush(QBrush(Qt::black));
|
|
|
|
// Milkyway images
|
|
for (int i = 0; i < m_milkyWayImages.size(); i++)
|
|
{
|
|
m_milkyWayItems.append(m_galacticLineOfSightScene->addPixmap(m_milkyWayImages[i]));
|
|
m_milkyWayItems[i]->setPos(0, 0);
|
|
m_milkyWayItems[i]->setVisible(i == 0);
|
|
}
|
|
|
|
// Line of sight
|
|
QPen pen(QColor(255, 0, 0), 4, Qt::SolidLine);
|
|
m_lineOfSight = m_galacticLineOfSightScene->addLine(511, 708, 511, 708, pen);
|
|
}
|
|
|
|
// Draw top-down image of Milky Way
|
|
void StarTrackerGUI::plotGalacticLineOfSight()
|
|
{
|
|
ui->chart->setVisible(false);
|
|
ui->image->setVisible(true);
|
|
ui->night->setVisible(false);
|
|
ui->logScale->setVisible(false);
|
|
ui->drawSun->setVisible(false);
|
|
ui->drawMoon->setVisible(false);
|
|
ui->darkTheme->setVisible(false);
|
|
ui->zoomIn->setVisible(true);
|
|
ui->zoomOut->setVisible(true);
|
|
ui->addAnimationFrame->setVisible(true);
|
|
ui->clearAnimation->setVisible(true);
|
|
ui->saveAnimation->setVisible(true);
|
|
ui->jupiterElevationLabel->setVisible(false);
|
|
ui->jupiterElevation->setVisible(false);
|
|
ui->cmlLabel->setVisible(false);
|
|
ui->cml->setVisible(false);
|
|
ui->ioPhaseLabel->setVisible(false);
|
|
ui->ioPhase->setVisible(false);
|
|
ui->ganymedePhaseLabel->setVisible(false);
|
|
ui->ganymedePhase->setVisible(false);
|
|
|
|
if (ui->image->scene() != m_galacticLineOfSightScene)
|
|
{
|
|
ui->image->setScene(m_galacticLineOfSightScene);
|
|
ui->image->resetTransform();
|
|
// Start zoomed out
|
|
ui->image->fitInView(m_milkyWayItems[0], Qt::KeepAspectRatio);
|
|
ui->image->setDragMode(QGraphicsView::ScrollHandDrag);
|
|
}
|
|
|
|
if (!m_zoom)
|
|
{
|
|
m_zoom = new GraphicsViewZoom(ui->image); // Deleted automatically when view is deleted
|
|
connect(m_zoom, &GraphicsViewZoom::zoomed, this, &StarTrackerGUI::zoomed);
|
|
}
|
|
|
|
// Select which Milky Way image to show
|
|
int imageIdx = ui->chartSubSelect->currentIndex();
|
|
for (int i = 0; i < m_milkyWayItems.size(); i++) {
|
|
m_milkyWayItems[i]->setVisible(i == imageIdx);
|
|
}
|
|
|
|
// Calculate Galactic longitude we're observing
|
|
float ra = Units::raToDecimal(m_settings.m_ra);
|
|
float dec = Units::decToDecimal(m_settings.m_dec);
|
|
double l, b;
|
|
Astronomy::equatorialToGalactic(ra, dec, l, b);
|
|
|
|
//l = ui->azimuth->value(); // For testing, just use azimuth
|
|
|
|
// Calculate length of line, as Sun is not at centre
|
|
// we assume end point lies on an ellipse, with Sun at foci
|
|
// See https://en.wikipedia.org/wiki/Ellipse Polar form relative to focus
|
|
QPointF sun(511, 708); // Location of Sun on Milky Way image
|
|
float a = sun.x() - 112; // semi-major axis
|
|
float c = sun.y() - sun.x(); // linear eccentricity
|
|
float e = c / a; // eccentricity
|
|
float r = a * (1.0f - e*e) / (1.0f - e * cos(Units::degreesToRadians(l)));
|
|
|
|
// Draw line from Sun along observation galactic longitude
|
|
QTransform rotation = QTransform().translate(sun.x(), -sun.y()).rotate(l).translate(-sun.x(), sun.y()); // Flip Y
|
|
QPointF point = rotation.map(QPointF(511, -sun.y() + r));
|
|
m_lineOfSight->setLine(sun.x(), sun.y(), point.x(), -point.y());
|
|
}
|
|
|
|
void StarTrackerGUI::plotGalacticMarker(LoSMarker* marker)
|
|
{
|
|
QPointF sun(511, 708); // Location of Sun on Milky Way image
|
|
double pixelsPerKPC = 564.0/22.995; // 75,000ly = 23kpc
|
|
QTransform rotation = QTransform().translate(sun.x(), -sun.y()).rotate(marker->m_l).translate(-sun.x(), sun.y()); // Flip Y
|
|
QPointF point = rotation.map(QPointF(511, -sun.y() + pixelsPerKPC*marker->m_d));
|
|
marker->m_text->setPos(point.x(), -point.y());
|
|
}
|
|
|
|
void StarTrackerGUI::on_zoomIn_clicked()
|
|
{
|
|
m_zoom->gentleZoom(1.25);
|
|
}
|
|
|
|
void StarTrackerGUI::on_zoomOut_clicked()
|
|
{
|
|
m_zoom->gentleZoom(0.75);
|
|
}
|
|
|
|
void StarTrackerGUI::zoomed()
|
|
{
|
|
if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_SOLAR_SYSTEM) {
|
|
scaleSolarSystemItems();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_addAnimationFrame_clicked()
|
|
{
|
|
QImage image(ui->image->size(), QImage::Format_ARGB32);
|
|
image.fill(Qt::black);
|
|
QPainter painter(&image);
|
|
ui->image->render(&painter);
|
|
m_animationImages.append(image);
|
|
|
|
ui->saveAnimation->setEnabled(true);
|
|
ui->clearAnimation->setEnabled(true);
|
|
}
|
|
|
|
void StarTrackerGUI::on_clearAnimation_clicked()
|
|
{
|
|
m_animationImages.clear();
|
|
ui->saveAnimation->setEnabled(false);
|
|
ui->clearAnimation->setEnabled(false);
|
|
}
|
|
|
|
void StarTrackerGUI::on_saveAnimation_clicked()
|
|
{
|
|
// Get filename of animation file
|
|
QFileDialog fileDialog(nullptr, "Select file to save animation to", "", "*.png");
|
|
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
if (fileDialog.exec())
|
|
{
|
|
QStringList fileNames = fileDialog.selectedFiles();
|
|
if (fileNames.size() > 0)
|
|
{
|
|
APNG apng(m_animationImages.size());
|
|
for (int i = 0; i < m_animationImages.size(); i++) {
|
|
apng.addImage(m_animationImages[i]);
|
|
}
|
|
if (!apng.save(fileNames[0])) {
|
|
QMessageBox::critical(this, "Star Tracker", QString("Failed to write to file %1").arg(fileNames[0]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::createSolarSystemScene()
|
|
{
|
|
m_solarSystemScene = new QGraphicsScene(ui->image);
|
|
m_solarSystemScene->setBackgroundBrush(QBrush(Qt::black));
|
|
|
|
m_solarSystemLabelFont = m_solarSystemScene->font();
|
|
m_solarSystemLabelFont.setPointSize(6);
|
|
m_solarSystemLabelFontMetrics = QFontMetrics(m_solarSystemLabelFont);
|
|
|
|
// Pluto is ~56 from Sun on log scale
|
|
const double r = 57;
|
|
m_solarSystemScene->setSceneRect(-r, -r, r * 2, r * 2);
|
|
}
|
|
|
|
QPixmap *StarTrackerGUI::getPlanetPixmap(const QString& name)
|
|
{
|
|
QString nameLower = name.toLower();
|
|
if (m_planetImages.contains(nameLower)) {
|
|
return &m_planetImages[nameLower];
|
|
} else {
|
|
return &m_planetImages["mercury"]; // Use mercury as generic grey moon
|
|
}
|
|
}
|
|
|
|
static float getScale(const QString& name)
|
|
{
|
|
if (name == "mercury") {
|
|
return 0.5f;
|
|
} else if (name == "venus") {
|
|
return 0.7f;
|
|
} else if (name == "earth") {
|
|
return 0.7f;
|
|
} else if (name == "moon") {
|
|
return 0.5f;
|
|
} else if (name == "mars") {
|
|
return 0.6f;
|
|
} else if (name == "jupiter") {
|
|
return 1.0f;
|
|
} else if (name == "saturn") {
|
|
return 0.9f;
|
|
} else if (name == "uranus") {
|
|
return 0.8f;
|
|
} else if (name == "neptune") {
|
|
return 0.8f;
|
|
} else if (name == "pluto") {
|
|
return 0.4f;
|
|
} else {
|
|
return 1.0f;
|
|
}
|
|
}
|
|
|
|
static void logScale(QVector3D &p)
|
|
{
|
|
// Rectangular to spherical
|
|
float r = p.length();
|
|
float az = std::atan2(p.y(), p.x());
|
|
float el = r > 0.0f ? std::acos(p.z() / r) : 0.0f;
|
|
|
|
// Log scale radius (log10(mercuryRadiusKM) = 7.7, log10(neptuneRadiusKM) = 9.6)
|
|
if (r >= 1e7) {
|
|
r = (std::log10(r) - 7.0f) * 20.0f;
|
|
} else {
|
|
r = 0.0f;
|
|
}
|
|
|
|
// Spherical to rectangular
|
|
p.setX(r * std::sin(el) * std::cos(az));
|
|
p.setY(r * std::sin(el) * std::sin(az));
|
|
p.setZ(r * std::cos(el));
|
|
}
|
|
|
|
void StarTrackerGUI::updateSolarSystemPositions(const QStringList &names, const QList<QVector3D> &positions, const QList<QList<QPointF>> &orbits)
|
|
{
|
|
const double kmToAU = 6.68459e-9; // Convert position from kM to AU
|
|
const double pixelScale = 20.0; // Mercury is 0.4AU. Neptune is 30 AU
|
|
bool scale = false;
|
|
QPen orbitPen(Qt::gray);
|
|
orbitPen.setWidth(0);
|
|
|
|
for (int i = 0; i < names.size(); i++)
|
|
{
|
|
SolarSystemItem *item;
|
|
|
|
if (m_solarSystemItems.contains(names[i]))
|
|
{
|
|
item = m_solarSystemItems.value(names[i]);
|
|
}
|
|
else
|
|
{
|
|
QString name = names[i];
|
|
name = name.replace(" BARYCENTER", "");
|
|
|
|
QPixmap* pixmap = getPlanetPixmap(name);
|
|
|
|
QBrush brush(QColor(200, 200, 200));
|
|
item = new SolarSystemItem();
|
|
item->m_textItem = m_solarSystemScene->addText(name, m_solarSystemLabelFont);
|
|
item->m_textItem->setZValue(2);
|
|
item->m_scale = getScale(name.toLower());
|
|
item->m_pixmapItem = m_solarSystemScene->addPixmap(*pixmap);
|
|
item->m_pixmapItem->setOffset(-pixmap->width() / 2, -pixmap->height() / 2);
|
|
item->m_pixmapItem->setZValue(1);
|
|
|
|
if (orbits[i].size() > 0) {
|
|
item->m_orbitItem = m_solarSystemScene->addPolygon(QPolygonF(), orbitPen);
|
|
} else {
|
|
item->m_orbitItem = nullptr;
|
|
}
|
|
|
|
m_solarSystemItems.insert(names[i], item);
|
|
scale = true;
|
|
}
|
|
|
|
// Optional log scaling of orbital radius, so outer planets have similar separation to inner planets
|
|
QVector3D scaledPos = positions[i];
|
|
if (m_settings.m_logScale) {
|
|
logScale(scaledPos);
|
|
} else {
|
|
scaledPos = pixelScale * kmToAU * scaledPos;
|
|
}
|
|
|
|
// Draw an ellipse for planet's orbit
|
|
if (item->m_orbitItem)
|
|
{
|
|
QPolygonF polygon;
|
|
|
|
for (int j = 0; j < orbits[i].size(); j++)
|
|
{
|
|
QPointF point = orbits[i][j];
|
|
|
|
if (m_settings.m_logScale)
|
|
{
|
|
QVector3D v = {(float) point.x(), (float) point.y(), 0.0f};
|
|
logScale(v);
|
|
point.setX(v.x());
|
|
point.setY(v.y());
|
|
}
|
|
else
|
|
{
|
|
point = pixelScale * kmToAU * point;
|
|
}
|
|
|
|
polygon << point;
|
|
}
|
|
|
|
item->m_orbitItem->setPolygon(polygon);
|
|
}
|
|
|
|
// Position label to right
|
|
const int textYOffset = m_solarSystemLabelFontMetrics.height();
|
|
item->m_pixmapItem->setPos(scaledPos.x(), scaledPos.y());
|
|
item->m_textItem->setPos(scaledPos.x() - 1.1 * item->m_pixmapItem->offset().x() * item->m_pixmapItem->scale(), scaledPos.y() - textYOffset * item->m_textItem->scale());
|
|
}
|
|
|
|
if (scale) {
|
|
scaleSolarSystemItems();
|
|
}
|
|
|
|
// Remove no longer needed items
|
|
QMutableHashIterator itr(m_solarSystemItems);
|
|
|
|
while (itr.hasNext())
|
|
{
|
|
itr.next();
|
|
if (!names.contains(itr.key()))
|
|
{
|
|
SolarSystemItem* item = itr.value();
|
|
m_solarSystemScene->removeItem(item->m_pixmapItem);
|
|
m_solarSystemScene->removeItem(item->m_textItem);
|
|
m_solarSystemScene->removeItem(item->m_orbitItem);
|
|
delete item;
|
|
itr.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::scaleSolarSystemItems()
|
|
{
|
|
float pixmapScale;
|
|
float textScale;
|
|
QTransform tf = ui->image->transform();
|
|
|
|
if (tf.m11() <= 1)
|
|
{
|
|
pixmapScale = 0.01 / tf.m11(); // Keep fixed size as we zoom out
|
|
textScale = 1.0 / tf.m11();
|
|
}
|
|
else if (tf.m11() >= 15)
|
|
{
|
|
pixmapScale = 0.01 * 15 / tf.m11(); // Keep fixed size as we zoom in to planetary scale
|
|
textScale = 1.0 / tf.m11();
|
|
}
|
|
else
|
|
{
|
|
pixmapScale = 0.01; // Get larger as we zoom in
|
|
textScale = 1.0 / tf.m11(); // Keep fixed size
|
|
}
|
|
|
|
for (auto& item : m_solarSystemItems)
|
|
{
|
|
item->m_textItem->setScale(textScale);
|
|
|
|
if (item->m_pixmapItem)
|
|
{
|
|
item->m_pixmapItem->setScale(pixmapScale * item->m_scale);
|
|
QPointF pos = item->m_pixmapItem->pos();
|
|
int textYOffset = m_solarSystemLabelFontMetrics.height();
|
|
item->m_textItem->setPos(pos.x() - 1.1 * item->m_pixmapItem->offset().x() * item->m_pixmapItem->scale(), pos.y() - textYOffset * item->m_textItem->scale());
|
|
}
|
|
}
|
|
|
|
centerOnSolarSystemBody();
|
|
}
|
|
|
|
// Draw top-down image of Solar System
|
|
void StarTrackerGUI::plotSolarSystem()
|
|
{
|
|
ui->chart->setVisible(false);
|
|
ui->image->setVisible(true);
|
|
ui->night->setVisible(false);
|
|
ui->logScale->setVisible(true);
|
|
ui->drawSun->setVisible(false);
|
|
ui->drawMoon->setVisible(false);
|
|
ui->darkTheme->setVisible(false);
|
|
ui->zoomIn->setVisible(true);
|
|
ui->zoomOut->setVisible(true);
|
|
ui->addAnimationFrame->setVisible(false);
|
|
ui->clearAnimation->setVisible(false);
|
|
ui->saveAnimation->setVisible(false);
|
|
ui->jupiterElevationLabel->setVisible(false);
|
|
ui->jupiterElevation->setVisible(false);
|
|
ui->jupiterElevationLabel->setVisible(false);
|
|
ui->jupiterElevation->setVisible(false);
|
|
ui->cmlLabel->setVisible(false);
|
|
ui->cml->setVisible(false);
|
|
ui->ioPhaseLabel->setVisible(false);
|
|
ui->ioPhase->setVisible(false);
|
|
ui->ganymedePhaseLabel->setVisible(false);
|
|
ui->ganymedePhase->setVisible(false);
|
|
|
|
if (ui->image->scene() != m_solarSystemScene)
|
|
{
|
|
ui->image->setScene(m_solarSystemScene);
|
|
ui->image->resetTransform();
|
|
ui->image->fitInView(m_solarSystemScene->sceneRect(), Qt::KeepAspectRatio);
|
|
scaleSolarSystemItems();
|
|
}
|
|
|
|
if (!m_zoom)
|
|
{
|
|
m_zoom = new GraphicsViewZoom(ui->image); // Deleted automatically when view is deleted
|
|
connect(m_zoom, &GraphicsViewZoom::zoomed, this, &StarTrackerGUI::zoomed);
|
|
}
|
|
|
|
centerOnSolarSystemBody();
|
|
}
|
|
|
|
void StarTrackerGUI::centerOnSolarSystemBody()
|
|
{
|
|
// Centre on selected body
|
|
QString selectedBody = ui->chartSubSelect->currentText();
|
|
if (m_solarSystemItems.contains(selectedBody))
|
|
{
|
|
SolarSystemItem *item = m_solarSystemItems.value(selectedBody);
|
|
ui->image->centerOn(item->m_pixmapItem);
|
|
ui->image->setDragMode(QGraphicsView::NoDrag);
|
|
}
|
|
else
|
|
{
|
|
ui->image->setDragMode(QGraphicsView::ScrollHandDrag);
|
|
}
|
|
}
|
|
|
|
QList<QGraphicsTextItem *> StarTrackerGUI::createJupiterLegendLabels(int legendXRight, int legendYBottom, int legendStep, int legendMax)
|
|
{
|
|
QList<QGraphicsTextItem *> list;
|
|
|
|
int legendSteps = legendMax / legendStep;
|
|
int legendYStep = m_jupiterImages[2].height() / legendSteps;
|
|
int maxLegendYLabelWidth = 0;
|
|
for (int i = 0; i <= legendSteps; i++)
|
|
{
|
|
QString legendStr = QString::number(i * legendStep);
|
|
QGraphicsTextItem *legendLabel = m_jupiterScene->addText(legendStr);
|
|
int yw = legendLabel->boundingRect().width();
|
|
maxLegendYLabelWidth = std::max(maxLegendYLabelWidth, yw);
|
|
legendLabel->setPos(legendXRight, legendYBottom - i * legendYStep - legendLabel->boundingRect().height() / 2);
|
|
list.append(legendLabel);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
void StarTrackerGUI::createJupiterScene()
|
|
{
|
|
m_jupiterScene = new QGraphicsScene(ui->image);
|
|
m_jupiterScene->setBackgroundBrush(QBrush(Qt::black));
|
|
|
|
// Phase vs CML images
|
|
for (int i = 0; i < m_jupiterImages.size(); i++) {
|
|
m_jupiterItems.append(m_jupiterScene->addPixmap(m_jupiterImages[i]));
|
|
}
|
|
m_jupiterItems[0]->setPos(0, 0);
|
|
m_jupiterItems[1]->setPos(0, 0);
|
|
m_jupiterItems[1]->setVisible(false);
|
|
|
|
// Legend
|
|
|
|
int legendSpacing = 10;
|
|
int legendYTop = (m_jupiterImages[0].height() - m_jupiterImages[2].height()) / 2;
|
|
int legendYBottom = legendYTop + m_jupiterImages[2].height();
|
|
int legendXLeft = m_jupiterImages[0].width() + legendSpacing;
|
|
int legendXRight = legendXLeft + m_jupiterImages[2].width();
|
|
m_jupiterItems[2]->setPos(legendXLeft, legendYTop);
|
|
m_ioLegendLabels = createJupiterLegendLabels(legendXRight, legendYBottom, 10, 60);
|
|
m_ganymedeLegendLabels = createJupiterLegendLabels(legendXRight, legendYBottom, 5, 15);
|
|
|
|
QGraphicsTextItem *legendLabel = m_jupiterScene->addText("Probability (%)");
|
|
legendLabel->setTransformOriginPoint(legendLabel->boundingRect().center());
|
|
legendLabel->setRotation(-90);
|
|
legendLabel->setPos(legendXRight, m_jupiterImages[0].height() / 2);
|
|
|
|
// Y axis labels
|
|
|
|
int phaseMax = 360;
|
|
int phaseStep = 45;
|
|
int phaseSteps = phaseMax / phaseStep;
|
|
int yStep = m_jupiterImages[0].height() / phaseSteps;
|
|
int maxYLabelWidth = 0;
|
|
|
|
for (int i = 0; i <= phaseSteps; i++)
|
|
{
|
|
QString phaseStr = QString::number(i * phaseStep);
|
|
QGraphicsTextItem *yLabel = m_jupiterScene->addText(phaseStr);
|
|
int yw = yLabel->boundingRect().width();
|
|
maxYLabelWidth = std::max(maxYLabelWidth, yw);
|
|
yLabel->setPos(-yw, m_jupiterImages[0].height() - i * yStep - yLabel->boundingRect().height() / 2);
|
|
}
|
|
|
|
QGraphicsTextItem *phaseLabel = m_jupiterScene->addText(QString("Phase (%1)").arg(QChar(0xb0)));
|
|
phaseLabel->setTransformOriginPoint(phaseLabel->boundingRect().center());
|
|
phaseLabel->setRotation(-90);
|
|
phaseLabel->setPos(-2 * phaseLabel->boundingRect().height() - maxYLabelWidth, m_jupiterImages[0].height() / 2);
|
|
|
|
// X axis labels
|
|
|
|
QGraphicsTextItem *cmlLabel = m_jupiterScene->addText(QString("CML (%1)").arg(QChar(0xb0)));
|
|
int cmlLabelWidth = cmlLabel->boundingRect().width();
|
|
cmlLabel->setPos(m_jupiterImages[0].width() / 2 - cmlLabelWidth / 2, m_jupiterImages[0].height() + cmlLabel->boundingRect().height());
|
|
|
|
int cmlMax = 360;
|
|
int cmlStep = 45;
|
|
int cmlSteps = cmlMax / cmlStep;
|
|
int xStep = m_jupiterImages[0].width() / cmlSteps;
|
|
|
|
for (int i = 0; i <= cmlSteps; i++)
|
|
{
|
|
QString cmlStr = QString::number(i * cmlStep);
|
|
QGraphicsTextItem *xLabel = m_jupiterScene->addText(cmlStr);
|
|
int xw = xLabel->boundingRect().width();
|
|
xLabel->setPos(i * xStep - xw / 2, m_jupiterImages[0].height());
|
|
}
|
|
|
|
// Moon images
|
|
|
|
const qreal moonScale = 0.1;
|
|
|
|
QPixmap *ioPixmap = getPlanetPixmap("io");
|
|
m_ioItem = m_jupiterScene->addPixmap(*ioPixmap);
|
|
m_ioItem->setOffset(-ioPixmap->width() / 2, -ioPixmap->height() / 2);
|
|
m_ioItem->setScale(moonScale);
|
|
m_ioItem->setZValue(2);
|
|
|
|
QPixmap *ganymedePixmap = getPlanetPixmap("ganymede");
|
|
m_ganymedeItem = m_jupiterScene->addPixmap(*ganymedePixmap);
|
|
m_ganymedeItem->setOffset(-ganymedePixmap->width() / 2, -ganymedePixmap->height() / 2);
|
|
m_ganymedeItem->setScale(moonScale);
|
|
m_ganymedeItem->setZValue(2);
|
|
|
|
int yLabelAreaWidth = maxYLabelWidth + phaseLabel->boundingRect().height();
|
|
int topMargin = (ioPixmap->height() * moonScale) / 2;
|
|
|
|
m_jupiterRect.setRect(
|
|
-yLabelAreaWidth,
|
|
-topMargin,
|
|
yLabelAreaWidth + m_jupiterImages[0].width() + legendSpacing + m_jupiterImages[2].width(),
|
|
m_jupiterImages[0].height() + 2 * cmlLabel->boundingRect().height() + topMargin
|
|
);
|
|
}
|
|
|
|
void StarTrackerGUI::updateJupiterMoonPosition(double cml, double ioPhase, double ganymedePhase)
|
|
{
|
|
int x = (cml / 360.0) * m_jupiterImages[0].width();
|
|
int y = m_jupiterImages[0].height() - ((ioPhase / 360.0) * m_jupiterImages[0].height());
|
|
m_ioItem->setPos(x, y);
|
|
|
|
y = m_jupiterImages[0].height() - ((ganymedePhase / 360.0) * m_jupiterImages[0].height());
|
|
m_ganymedeItem->setPos(x, y);
|
|
}
|
|
|
|
void StarTrackerGUI::updateJupiterMoonPositions(const StarTrackerReport::MsgReportJupiterData& report)
|
|
{
|
|
QList<StarTrackerReport::JupiterData> jupiterData = report.getJupiterData();
|
|
QList<StarTrackerReport::JupiterMoonData> moonData = report.getMoonData();
|
|
|
|
qDeleteAll(m_jupiterLines);
|
|
m_jupiterLines.clear();
|
|
qDeleteAll(m_jupiterTexts);
|
|
m_jupiterTexts.clear();
|
|
|
|
for (int i = 0; i < jupiterData.size() - 1; i++)
|
|
{
|
|
QString hour = QString::number(jupiterData[i].m_dateTime.time().hour());
|
|
double cml1 = moonData[i].m_cml;
|
|
double cml2 = moonData[i+1].m_cml;
|
|
double phase1 = moonData[i].m_phase;
|
|
double phase2 = moonData[i+1].m_phase;
|
|
|
|
const int w = m_jupiterImages[0].width();
|
|
const int h = m_jupiterImages[0].height();
|
|
int x1 = (cml1 / 360.0) * w;
|
|
int y1 = h - ((phase1 / 360.0) * h);
|
|
int x2 = (cml2 / 360.0) * w;
|
|
int y2 = h - ((phase2 / 360.0) * h);
|
|
int x2n, y2n;
|
|
|
|
bool interpolated = false;
|
|
if ((x2 < x1) && (y2 > y1))
|
|
{
|
|
x2n = 0;
|
|
y2n = h - Interpolation::interpolate(x1 - w, y1, x2, h - y2, 0);
|
|
x2 = Interpolation::interpolate(y1, x1, y2 - h, x2 + w, 0);
|
|
y2 = 0;
|
|
interpolated = true;
|
|
}
|
|
else if (x2 < x1)
|
|
{
|
|
y2 = Interpolation::interpolate(x1, y1, x2 + w, y2, w);
|
|
x2 = w;
|
|
interpolated = true;
|
|
x2n = 0;
|
|
y2n = y2;
|
|
}
|
|
else if (y2 > y1)
|
|
{
|
|
x2 = Interpolation::interpolate(y1, x1, y2 - h, x2, 0);
|
|
y2 = 0;
|
|
interpolated = true;
|
|
x2n = x2;
|
|
y2n = h;
|
|
}
|
|
|
|
QGraphicsLineItem *line = m_jupiterScene->addLine(x1, y1, x2, y2, QPen(Qt::white));
|
|
m_jupiterLines.append(line);
|
|
|
|
if (interpolated && (i < jupiterData.size() - 1))
|
|
{
|
|
int x3 = (cml2 / 360.0) * w;
|
|
int y3 = h - ((phase2 / 360.0) * h);
|
|
|
|
x2 = x2n;
|
|
y2 = y2n;
|
|
|
|
line = m_jupiterScene->addLine(x2, y2, x3, y3, QPen(Qt::white));
|
|
m_jupiterLines.append(line);
|
|
}
|
|
|
|
QGraphicsLineItem *dash = m_jupiterScene->addLine(x1, y1, x1, y1 + 3, QPen(Qt::white));
|
|
m_jupiterLines.append(dash);
|
|
|
|
QGraphicsTextItem *text = m_jupiterScene->addText(hour);
|
|
const int tw = text->boundingRect().width();
|
|
text->setPos(x1 - tw / 2, y1);
|
|
m_jupiterTexts.append(text);
|
|
}
|
|
|
|
if (jupiterData.size() > 0)
|
|
{
|
|
int i = jupiterData.size() - 1;
|
|
|
|
QString hour = QString::number(jupiterData[i].m_dateTime.time().hour());
|
|
double cml1 = moonData[i].m_cml;
|
|
double phase1 = moonData[i].m_phase;
|
|
|
|
int x1 = (cml1 / 360.0) * m_jupiterImages[0].width();
|
|
int y1 = m_jupiterImages[0].height() - ((phase1 / 360.0) * m_jupiterImages[0].height());
|
|
|
|
QGraphicsLineItem *dash = m_jupiterScene->addLine(x1, y1, x1, y1 + 3, QPen(Qt::white));
|
|
m_jupiterLines.append(dash);
|
|
|
|
QGraphicsTextItem *text = m_jupiterScene->addText(hour);
|
|
int w = text->boundingRect().width();
|
|
text->setPos(x1 - w / 2, y1);
|
|
m_jupiterTexts.append(text);
|
|
}
|
|
|
|
}
|
|
|
|
void StarTrackerGUI::plotJupiter()
|
|
{
|
|
// Select which Moon to show
|
|
int imageIdx = ui->chartSubSelect->currentIndex();
|
|
bool plotIO = imageIdx == 0;
|
|
bool ployGanymede = imageIdx == 1;
|
|
|
|
ui->chart->setVisible(false);
|
|
ui->image->setVisible(true);
|
|
ui->night->setVisible(false);
|
|
ui->logScale->setVisible(false);
|
|
ui->drawSun->setVisible(false);
|
|
ui->drawMoon->setVisible(false);
|
|
ui->darkTheme->setVisible(false);
|
|
ui->zoomIn->setVisible(false);
|
|
ui->zoomOut->setVisible(false);
|
|
ui->addAnimationFrame->setVisible(false);
|
|
ui->clearAnimation->setVisible(false);
|
|
ui->saveAnimation->setVisible(false);
|
|
ui->jupiterElevationLabel->setVisible(true);
|
|
ui->jupiterElevation->setVisible(true);
|
|
ui->cmlLabel->setVisible(true);
|
|
ui->cml->setVisible(true);
|
|
ui->ioPhaseLabel->setVisible(plotIO);
|
|
ui->ioPhase->setVisible(plotIO);
|
|
ui->ganymedePhaseLabel->setVisible(ployGanymede);
|
|
ui->ganymedePhase->setVisible(ployGanymede);
|
|
|
|
if (ui->image->scene() != m_jupiterScene)
|
|
{
|
|
ui->image->setScene(m_jupiterScene);
|
|
ui->image->setDragMode(QGraphicsView::NoDrag);
|
|
}
|
|
|
|
if (m_zoom)
|
|
{
|
|
delete m_zoom;
|
|
m_zoom = nullptr;
|
|
}
|
|
|
|
m_jupiterItems[0]->setVisible(plotIO);
|
|
m_jupiterItems[1]->setVisible(ployGanymede);
|
|
|
|
m_ioItem->setVisible(plotIO);
|
|
m_ganymedeItem->setVisible(ployGanymede);
|
|
for (auto& item : m_ioLegendLabels) {
|
|
item->setVisible(plotIO);
|
|
}
|
|
for (auto& item : m_ganymedeLegendLabels) {
|
|
item->setVisible(ployGanymede);
|
|
}
|
|
|
|
// Expand to available view
|
|
ui->image->fitInView(m_jupiterRect, Qt::KeepAspectRatio);
|
|
}
|
|
|
|
void StarTrackerGUI::resizeEvent(QResizeEvent *event)
|
|
{
|
|
if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_JUPITER) {
|
|
ui->image->fitInView(m_jupiterRect, Qt::KeepAspectRatio);
|
|
}
|
|
FeatureGUI::resizeEvent(event);
|
|
}
|
|
|
|
void StarTrackerGUI::plotSkyTemperatureChart()
|
|
{
|
|
ui->chart->setVisible(true);
|
|
ui->image->setVisible(false);
|
|
ui->night->setVisible(false);
|
|
ui->logScale->setVisible(false);
|
|
ui->drawSun->setVisible(true);
|
|
ui->drawMoon->setVisible(true);
|
|
ui->darkTheme->setVisible(false);
|
|
ui->zoomIn->setVisible(false);
|
|
ui->zoomOut->setVisible(false);
|
|
ui->addAnimationFrame->setVisible(false);
|
|
ui->clearAnimation->setVisible(false);
|
|
ui->saveAnimation->setVisible(false);
|
|
ui->jupiterElevationLabel->setVisible(false);
|
|
ui->jupiterElevation->setVisible(false);
|
|
ui->cmlLabel->setVisible(false);
|
|
ui->cml->setVisible(false);
|
|
ui->ioPhaseLabel->setVisible(false);
|
|
ui->ioPhase->setVisible(false);
|
|
ui->ganymedePhaseLabel->setVisible(false);
|
|
ui->ganymedePhase->setVisible(false);
|
|
|
|
bool galactic = (ui->chartSubSelect->currentIndex() & 1) == 1;
|
|
|
|
m_chart.removeAllSeries();
|
|
removeAllAxes();
|
|
|
|
// Plot drift scan path
|
|
QList<QLineSeries*> lineSeries;
|
|
if (m_settings.m_target == "Custom Az/El")
|
|
{
|
|
lineSeries = createDriftScan(galactic);
|
|
QPen pen(getSeriesColor(1), 2, Qt::SolidLine);
|
|
|
|
for (int i = 0; i < lineSeries.length(); i++) {
|
|
lineSeries[i]->setPen(pen);
|
|
}
|
|
}
|
|
|
|
QScatterSeries *series = new QScatterSeries();
|
|
float ra = Units::raToDecimal(m_settings.m_ra);
|
|
float dec = Units::decToDecimal(m_settings.m_dec);
|
|
|
|
double beamWidth = m_settings.m_beamwidth;
|
|
// Ellipse not supported, so draw circle on shorter axis
|
|
double degPerPixelW = 360.0/m_chart.plotArea().width();
|
|
double degPerPixelH = 180.0/m_chart.plotArea().height();
|
|
double degPerPixel = std::min(degPerPixelW, degPerPixelH);
|
|
double markerSize;
|
|
|
|
double x, y;
|
|
mapRaDec(ra, dec, galactic, x, y);
|
|
series->append(x, y);
|
|
|
|
// Get temperature
|
|
int idx = ui->chartSubSelect->currentIndex();
|
|
|
|
if ((idx == 6) || (idx == 7))
|
|
{
|
|
double temp;
|
|
|
|
if (m_starTracker->calcSkyTemperature(m_settings.m_frequency, m_settings.m_beamwidth, ra, dec, temp))
|
|
{
|
|
series->setPointLabelsVisible(true);
|
|
series->setPointLabelsColor(Qt::red);
|
|
series->setPointLabelsFormat(QString("%1 K").arg(std::round(temp)));
|
|
|
|
// Scale marker size by beamwidth
|
|
markerSize = std::max((int)round(beamWidth * degPerPixel), 5);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Read temperature from selected FITS file at target RA/Dec
|
|
QImage *img = &m_images[idx];
|
|
const FITS *fits = m_starTracker->getTempFITS(idx/2);
|
|
double x;
|
|
|
|
if (ra <= 12.0) {
|
|
x = (12.0 - ra) / 24.0;
|
|
} else {
|
|
x = (24 - ra + 12) / 24.0;
|
|
}
|
|
|
|
int imgX = x * (img->width() - 1);
|
|
|
|
if (imgX >= img->width()) {
|
|
imgX = img->width() - 1;
|
|
}
|
|
|
|
int imgY = (90.0-dec)/180.0 * (img->height() - 1);
|
|
|
|
if (imgY >= img->height()) {
|
|
imgY = img->height() - 1;
|
|
}
|
|
|
|
if (fits->valid())
|
|
{
|
|
double temp = fits->scaledValue(imgX, imgY);
|
|
series->setPointLabelsVisible(true);
|
|
series->setPointLabelsColor(Qt::red);
|
|
series->setPointLabelsFormat(QString("%1 K").arg(std::round(temp)));
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "FITS not valid";
|
|
}
|
|
|
|
// Temperature from just one pixel, but need to make marker visbile
|
|
markerSize = 5;
|
|
}
|
|
|
|
series->setMarkerSize(markerSize);
|
|
series->setColor(getSeriesColor(0));
|
|
m_chart.setTitle("");
|
|
|
|
// We want scatter (for the beam) to be on top of line, but same color even when other series
|
|
for (int i = 0; i < lineSeries.length(); i++) {
|
|
m_chart.addSeries(lineSeries[i]);
|
|
}
|
|
|
|
// Draw Sun on chart if requested
|
|
QScatterSeries *sunSeries = nullptr;
|
|
if (m_settings.m_drawSunOnSkyTempChart)
|
|
{
|
|
sunSeries = new QScatterSeries();
|
|
mapRaDec(m_sunRA, m_sunDec, galactic, x, y);
|
|
sunSeries->append(x, y);
|
|
sunSeries->setMarkerSize((int)std::max(std::round(0.53 * degPerPixel), 5.0));
|
|
sunSeries->setColor(Qt::yellow);
|
|
sunSeries->setBorderColor(Qt::yellow);
|
|
|
|
if (m_settings.m_target != "Sun") // Avoid labels on top of each other
|
|
{
|
|
sunSeries->setPointLabelsVisible(true);
|
|
sunSeries->setPointLabelsColor(Qt::red);
|
|
sunSeries->setPointLabelsFormat("Sun");
|
|
}
|
|
|
|
m_chart.addSeries(sunSeries);
|
|
}
|
|
|
|
// Draw moon on chart if requested
|
|
QScatterSeries *moonSeries = nullptr;
|
|
if (m_settings.m_drawMoonOnSkyTempChart)
|
|
{
|
|
moonSeries = new QScatterSeries();
|
|
mapRaDec(m_moonRA, m_moonDec, galactic, x, y);
|
|
moonSeries->append(x, y);
|
|
moonSeries->setMarkerSize((int)std::max(std::round(0.53 * degPerPixel), 5.0));
|
|
moonSeries->setColor(qRgb(150, 150, 150));
|
|
moonSeries->setBorderColor(qRgb(150, 150, 150));
|
|
|
|
if (m_settings.m_target != "Moon") // Avoid labels on top of each other
|
|
{
|
|
moonSeries->setPointLabelsVisible(true);
|
|
moonSeries->setPointLabelsColor(Qt::red);
|
|
moonSeries->setPointLabelsFormat("Moon");
|
|
}
|
|
|
|
m_chart.addSeries(moonSeries);
|
|
}
|
|
|
|
m_chart.addSeries(series);
|
|
|
|
if (galactic)
|
|
{
|
|
m_chart.addAxis(&m_skyTempGalacticLXAxis, Qt::AlignBottom);
|
|
series->attachAxis(&m_skyTempGalacticLXAxis);
|
|
|
|
if (sunSeries) {
|
|
sunSeries->attachAxis(&m_skyTempGalacticLXAxis);
|
|
}
|
|
if (moonSeries) {
|
|
moonSeries->attachAxis(&m_skyTempGalacticLXAxis);
|
|
}
|
|
|
|
m_skyTempYAxis.setTitleText(QString("Galactic latitude (%1)").arg(QChar(0xb0)));
|
|
m_chart.addAxis(&m_skyTempYAxis, Qt::AlignLeft);
|
|
series->attachAxis(&m_skyTempYAxis);
|
|
|
|
if (sunSeries) {
|
|
sunSeries->attachAxis(&m_skyTempYAxis);
|
|
}
|
|
if (moonSeries) {
|
|
moonSeries->attachAxis(&m_skyTempYAxis);
|
|
}
|
|
|
|
for (int i = 0; i < lineSeries.length(); i++)
|
|
{
|
|
lineSeries[i]->attachAxis(&m_skyTempGalacticLXAxis);
|
|
lineSeries[i]->attachAxis(&m_skyTempYAxis);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_chart.addAxis(&m_skyTempRAXAxis, Qt::AlignBottom);
|
|
series->attachAxis(&m_skyTempRAXAxis);
|
|
|
|
if (sunSeries) {
|
|
sunSeries->attachAxis(&m_skyTempRAXAxis);
|
|
}
|
|
if (moonSeries) {
|
|
moonSeries->attachAxis(&m_skyTempRAXAxis);
|
|
}
|
|
|
|
m_skyTempYAxis.setTitleText(QString("Declination (%1)").arg(QChar(0xb0)));
|
|
m_chart.addAxis(&m_skyTempYAxis, Qt::AlignLeft);
|
|
series->attachAxis(&m_skyTempYAxis);
|
|
|
|
if (sunSeries) {
|
|
sunSeries->attachAxis(&m_skyTempYAxis);
|
|
}
|
|
if (moonSeries) {
|
|
moonSeries->attachAxis(&m_skyTempYAxis);
|
|
}
|
|
|
|
for (int i = 0; i < lineSeries.length(); i++)
|
|
{
|
|
lineSeries[i]->attachAxis(&m_skyTempRAXAxis);
|
|
lineSeries[i]->attachAxis(&m_skyTempYAxis);
|
|
}
|
|
}
|
|
ui->chart->setChart(&m_chart);
|
|
plotAreaChanged(m_chart.plotArea());
|
|
}
|
|
|
|
void StarTrackerGUI::plotAreaChanged(const QRectF &plotArea)
|
|
{
|
|
int width = static_cast<int>(plotArea.width());
|
|
int height = static_cast<int>(plotArea.height());
|
|
int viewW = static_cast<int>(ui->chart->width());
|
|
int viewH = static_cast<int>(ui->chart->height());
|
|
|
|
// Scale the image to fit plot area
|
|
int imageIdx = ui->chartSubSelect->currentIndex();
|
|
|
|
if (imageIdx == -1) {
|
|
return;
|
|
} else if (imageIdx == 6) {
|
|
imageIdx = 2;
|
|
} else if (imageIdx == 7) {
|
|
imageIdx = 3;
|
|
}
|
|
|
|
QImage image = m_images[imageIdx].scaled(QSize(width, height), Qt::IgnoreAspectRatio);
|
|
QImage translated(viewW, viewH, QImage::Format_ARGB32);
|
|
translated.fill(Qt::white);
|
|
QPainter painter(&translated);
|
|
painter.drawImage(plotArea.topLeft(), image);
|
|
|
|
m_chart.setPlotAreaBackgroundBrush(translated);
|
|
m_chart.setPlotAreaBackgroundVisible(true);
|
|
}
|
|
|
|
void StarTrackerGUI::removeAllAxes()
|
|
{
|
|
QList<QAbstractAxis *> axes;
|
|
axes = m_chart.axes(Qt::Horizontal);
|
|
|
|
for (QAbstractAxis *axis : axes) {
|
|
m_chart.removeAxis(axis);
|
|
}
|
|
|
|
axes = m_chart.axes(Qt::Vertical);
|
|
|
|
for (QAbstractAxis *axis : axes) {
|
|
m_chart.removeAxis(axis);
|
|
}
|
|
}
|
|
|
|
// Plot target elevation angle over the day
|
|
void StarTrackerGUI::plotElevationLineChart()
|
|
{
|
|
ui->chart->setVisible(true);
|
|
ui->image->setVisible(false);
|
|
ui->night->setVisible(true);
|
|
ui->logScale->setVisible(false);
|
|
ui->drawSun->setVisible(false);
|
|
ui->drawMoon->setVisible(false);
|
|
ui->darkTheme->setVisible(true);
|
|
ui->zoomIn->setVisible(false);
|
|
ui->zoomOut->setVisible(false);
|
|
ui->addAnimationFrame->setVisible(false);
|
|
ui->clearAnimation->setVisible(false);
|
|
ui->saveAnimation->setVisible(false);
|
|
ui->jupiterElevationLabel->setVisible(false);
|
|
ui->jupiterElevation->setVisible(false);
|
|
ui->cmlLabel->setVisible(false);
|
|
ui->cml->setVisible(false);
|
|
ui->ioPhaseLabel->setVisible(false);
|
|
ui->ioPhase->setVisible(false);
|
|
ui->ganymedePhaseLabel->setVisible(false);
|
|
ui->ganymedePhase->setVisible(false);
|
|
|
|
QChart *oldChart = m_azElLineChart;
|
|
|
|
m_azElLineChart = new QChart();
|
|
m_azElLineChart->setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
|
QDateTimeAxis *xAxis = new QDateTimeAxis();
|
|
QValueAxis *yLeftAxis = new QValueAxis();
|
|
QValueAxis *yRightAxis = new QValueAxis();
|
|
|
|
m_azElLineChart->legend()->hide();
|
|
|
|
m_azElLineChart->layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_azElLineChart->setMargins(QMargins(1, 1, 1, 1));
|
|
|
|
QLineSeries *elSeries = new QLineSeries();
|
|
QList<QLineSeries *> azSeriesList;
|
|
QLineSeries *azSeries = new QLineSeries();
|
|
azSeriesList.append(azSeries);
|
|
QPen pen(getSeriesColor(1), 2, Qt::SolidLine);
|
|
azSeries->setPen(pen);
|
|
|
|
double maxElevation = -90.0;
|
|
double prevAz;
|
|
QDateTime startTime;
|
|
QDateTime endTime;
|
|
|
|
if (m_settings.m_target == m_azElVsTimeTarget)
|
|
{
|
|
if (!m_dateTimes.isEmpty()) {
|
|
startTime = m_dateTimes[0];
|
|
}
|
|
|
|
for (int i = 0; i < m_dateTimes.size(); i++)
|
|
{
|
|
QDateTime dt = m_dateTimes[i];
|
|
|
|
if (m_elevations[i] > maxElevation) {
|
|
maxElevation = m_elevations[i];
|
|
}
|
|
|
|
if (i == 0) {
|
|
prevAz = m_azimuths[i];
|
|
}
|
|
if (((prevAz >= 270) && (m_azimuths[i] < 90)) || ((prevAz < 90) && (m_azimuths[i] >= 270)))
|
|
{
|
|
azSeries = new QLineSeries();
|
|
azSeriesList.append(azSeries);
|
|
azSeries->setPen(pen);
|
|
}
|
|
|
|
azSeries->append(dt.toMSecsSinceEpoch(), m_azimuths[i]);
|
|
elSeries->append(dt.toMSecsSinceEpoch(), m_elevations[i]);
|
|
|
|
endTime = dt;
|
|
prevAz = m_azimuths[i];
|
|
}
|
|
}
|
|
|
|
QDateTime currentTime;
|
|
|
|
if (m_settings.m_dateTime.isEmpty())
|
|
{
|
|
currentTime = QDateTime::currentDateTime();
|
|
}
|
|
else
|
|
{
|
|
currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
if (m_settings.m_utc) {
|
|
currentTime.setTimeZone(QTimeZone::utc());
|
|
}
|
|
}
|
|
|
|
m_azElLineChart->addAxis(xAxis, Qt::AlignBottom);
|
|
m_azElLineChart->addAxis(yLeftAxis, Qt::AlignLeft);
|
|
m_azElLineChart->addAxis(yRightAxis, Qt::AlignRight);
|
|
m_azElLineChart->addSeries(elSeries);
|
|
|
|
for (int i = 0; i < azSeriesList.size(); i++)
|
|
{
|
|
m_azElLineChart->addSeries(azSeriesList[i]);
|
|
azSeriesList[i]->attachAxis(xAxis);
|
|
azSeriesList[i]->attachAxis(yRightAxis);
|
|
}
|
|
|
|
// Plot current target on elevation series
|
|
if (ui->azimuth->hasValue() && ui->elevation->hasValue() && (ui->elevation->value() > 0.0))
|
|
{
|
|
QScatterSeries *posSeries = new QScatterSeries();
|
|
posSeries->setMarkerSize(3);
|
|
posSeries->append(currentTime.toMSecsSinceEpoch(), ui->elevation->value());
|
|
if (m_settings.m_target.startsWith("Custom"))
|
|
{
|
|
posSeries->setPointLabelsVisible(false);
|
|
posSeries->setPointLabelsFormat("");
|
|
}
|
|
else
|
|
{
|
|
posSeries->setPointLabelsVisible(true);
|
|
posSeries->setPointLabelsFormat(m_settings.m_target);
|
|
}
|
|
posSeries->setPointLabelsClipping(false);
|
|
m_azElLineChart->addSeries(posSeries);
|
|
posSeries->attachAxis(xAxis);
|
|
posSeries->attachAxis(yLeftAxis);
|
|
}
|
|
|
|
elSeries->attachAxis(xAxis);
|
|
elSeries->attachAxis(yLeftAxis);
|
|
if (m_settings.m_night) {
|
|
xAxis->setTitleText(QString("%1 - %2 %3").arg(startTime.date().toString()).arg(startTime.date().addDays(1).toString()).arg(startTime.timeZoneAbbreviation()));
|
|
} else {
|
|
xAxis->setTitleText(QString("%1 %2").arg(startTime.date().toString()).arg(startTime.timeZoneAbbreviation()));
|
|
}
|
|
xAxis->setFormat("hh");
|
|
xAxis->setTickCount(7);
|
|
xAxis->setRange(startTime, endTime);
|
|
yLeftAxis->setRange(0.0, 90.0);
|
|
yLeftAxis->setTitleText(QString("Elevation (%1)").arg(QChar(0xb0)));
|
|
yRightAxis->setRange(0.0, 360.0);
|
|
yRightAxis->setTitleText(QString("Azimuth (%1)").arg(QChar(0xb0)));
|
|
|
|
if ((m_settings.m_target != m_azElVsTimeTarget) || m_dateTimes.isEmpty()) {
|
|
m_azElLineChart->setTitle("Waiting for data");
|
|
} else if (maxElevation < 0) {
|
|
m_azElLineChart->setTitle("Not visible from this latitude");
|
|
} else if (m_settings.m_target.contains("SatelliteTracker")) {
|
|
m_azElLineChart->setTitle("See Satellite Tracker for chart that accounts for satellite's movement");
|
|
} else {
|
|
m_azElLineChart->setTitle("");
|
|
}
|
|
|
|
ui->chart->setChart(m_azElLineChart);
|
|
delete oldChart;
|
|
}
|
|
|
|
// Reduce az/el range from 450,180 to 360,90
|
|
void StarTrackerGUI::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 target elevation angle over the day
|
|
void StarTrackerGUI::plotElevationPolarChart()
|
|
{
|
|
ui->chart->setVisible(true);
|
|
ui->image->setVisible(false);
|
|
ui->night->setVisible(true);
|
|
ui->logScale->setVisible(false);
|
|
ui->drawSun->setVisible(false);
|
|
ui->drawMoon->setVisible(false);
|
|
ui->darkTheme->setVisible(true);
|
|
ui->zoomIn->setVisible(false);
|
|
ui->zoomOut->setVisible(false);
|
|
ui->addAnimationFrame->setVisible(false);
|
|
ui->clearAnimation->setVisible(false);
|
|
ui->saveAnimation->setVisible(false);
|
|
ui->jupiterElevationLabel->setVisible(false);
|
|
ui->jupiterElevation->setVisible(false);
|
|
ui->cmlLabel->setVisible(false);
|
|
ui->cml->setVisible(false);
|
|
ui->ioPhaseLabel->setVisible(false);
|
|
ui->ioPhase->setVisible(false);
|
|
ui->ganymedePhaseLabel->setVisible(false);
|
|
ui->ganymedePhase->setVisible(false);
|
|
|
|
QChart *oldChart = m_azElPolarChart;
|
|
|
|
m_azElPolarChart = new QPolarChart();
|
|
m_azElPolarChart->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_azElPolarChart->addAxis(angularAxis, QPolarChart::PolarOrientationAngular);
|
|
m_azElPolarChart->addAxis(radialAxis, QPolarChart::PolarOrientationRadial);
|
|
m_azElPolarChart->legend()->hide();
|
|
m_azElPolarChart->layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_azElPolarChart->setMargins(QMargins(1, 1, 1, 1));
|
|
|
|
double maxElevation = -90.0;
|
|
|
|
QLineSeries *polarSeries = new QLineSeries();
|
|
QDateTime currentTime;
|
|
QDateTime dt;
|
|
|
|
if (m_settings.m_dateTime.isEmpty()) {
|
|
currentTime = QDateTime::currentDateTime();
|
|
} else {
|
|
currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
}
|
|
dt = currentTime;
|
|
dt.setTime(QTime(0,0));
|
|
QDateTime endTime = dt;
|
|
QDateTime riseTime;
|
|
QDateTime setTime;
|
|
int riseIdx = -1;
|
|
int setIdx = -1;
|
|
int idx = 0;
|
|
double prevAlt;
|
|
|
|
if (m_settings.m_target == m_azElVsTimeTarget)
|
|
{
|
|
for (int i = 0; i < m_dateTimes.size(); i++)
|
|
{
|
|
QDateTime dt = m_dateTimes[i];
|
|
|
|
if (m_elevations[i] > maxElevation) {
|
|
maxElevation = m_elevations[i];
|
|
}
|
|
|
|
if (i == 0) {
|
|
prevAlt = m_elevations[i];
|
|
}
|
|
|
|
// We can have set before rise in a day, if the object starts > 0
|
|
if ((m_elevations[i] >= 0.0) && (prevAlt < 0.0))
|
|
{
|
|
riseTime = dt;
|
|
riseIdx = idx;
|
|
}
|
|
|
|
if (( m_elevations[i] < 0.0) && (prevAlt >= 0.0))
|
|
{
|
|
setTime = endTime;
|
|
setIdx = idx;
|
|
}
|
|
|
|
polarSeries->append(m_azimuths[i], 90 - m_elevations[i]);
|
|
idx++;
|
|
endTime = dt;
|
|
prevAlt = m_elevations[i];
|
|
}
|
|
|
|
// 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(getSeriesColor(0), 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 = Interpolation::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 = Interpolation::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_azElPolarChart->addSeries(series[i]);
|
|
series[i]->attachAxis(angularAxis);
|
|
series[i]->attachAxis(radialAxis);
|
|
}
|
|
}
|
|
|
|
if (m_settings.m_drawRotators != StarTrackerSettings::NO_ROTATORS)
|
|
{
|
|
int redrawTime = 0;
|
|
// Plot rotator position
|
|
QString ourSourceName = QString("F:%1 %2").arg(m_starTracker->getIndexInFeatureSet()).arg(m_starTracker->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 == StarTrackerSettings::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_azElPolarChart->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_azElPolarChart->addSeries(rotatorSeries);
|
|
rotatorSeries->attachAxis(angularAxis);
|
|
rotatorSeries->attachAxis(radialAxis);
|
|
|
|
redrawTime = 333;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (redrawTime > 0)
|
|
{
|
|
// Redraw to show updated rotator position
|
|
// Update period may be long or custom time might be fixed
|
|
m_redrawTimer.setSingleShot(true);
|
|
m_redrawTimer.start(redrawTime);
|
|
}
|
|
}
|
|
|
|
// Create series with single point, so we can plot time of rising
|
|
if (riseTime.isValid())
|
|
{
|
|
QLineSeries *riseSeries = new QLineSeries();
|
|
riseSeries->append(polarSeries->at(riseIdx));
|
|
riseSeries->setPointLabelsFormat(QString("Rise %1").arg(riseTime.time().toString("hh:mm")));
|
|
riseSeries->setPointLabelsVisible(true);
|
|
riseSeries->setPointLabelsClipping(false);
|
|
m_azElPolarChart->addSeries(riseSeries);
|
|
riseSeries->attachAxis(angularAxis);
|
|
riseSeries->attachAxis(radialAxis);
|
|
}
|
|
|
|
// Create series with single point, so we can plot time of setting
|
|
if (setTime.isValid())
|
|
{
|
|
QLineSeries *setSeries = new QLineSeries();
|
|
setSeries->append(polarSeries->at(setIdx));
|
|
setSeries->setPointLabelsFormat(QString("Set %1").arg(setTime.time().toString("hh:mm")));
|
|
setSeries->setPointLabelsVisible(true);
|
|
setSeries->setPointLabelsClipping(false);
|
|
m_azElPolarChart->addSeries(setSeries);
|
|
setSeries->attachAxis(angularAxis);
|
|
setSeries->attachAxis(radialAxis);
|
|
}
|
|
|
|
// Plot target current position
|
|
if (ui->azimuth->hasValue() && ui->elevation->hasValue() && (ui->elevation->value() > 0.0))
|
|
{
|
|
QScatterSeries *posSeries = new QScatterSeries();
|
|
posSeries->setMarkerSize(3);
|
|
posSeries->append(ui->azimuth->value(), 90 - ui->elevation->value());
|
|
if (m_settings.m_target.startsWith("Custom"))
|
|
{
|
|
posSeries->setPointLabelsVisible(false);
|
|
posSeries->setPointLabelsFormat("");
|
|
}
|
|
else
|
|
{
|
|
posSeries->setPointLabelsVisible(true);
|
|
posSeries->setPointLabelsFormat(m_settings.m_target);
|
|
}
|
|
posSeries->setPointLabelsClipping(false);
|
|
m_azElPolarChart->addSeries(posSeries);
|
|
posSeries->attachAxis(angularAxis);
|
|
posSeries->attachAxis(radialAxis);
|
|
}
|
|
|
|
if ((m_settings.m_target != m_azElVsTimeTarget) || m_dateTimes.isEmpty()) {
|
|
m_azElPolarChart->setTitle("Waiting for data");
|
|
} else if (maxElevation < 0) {
|
|
m_azElPolarChart->setTitle("Not visible from this latitude");
|
|
} else if (m_settings.m_target.contains("SatelliteTracker")) {
|
|
m_azElPolarChart->setTitle("See Satellite Tracker for chart that accounts for satellite's movement");
|
|
} else {
|
|
m_azElPolarChart->setTitle("");
|
|
}
|
|
|
|
ui->chart->setChart(m_azElPolarChart);
|
|
|
|
delete polarSeries;
|
|
delete oldChart;
|
|
}
|
|
|
|
// Find target on the Map
|
|
void StarTrackerGUI::on_viewOnMap_clicked()
|
|
{
|
|
QString target = m_settings.m_target == "Sun" || m_settings.m_target == "Moon" ? m_settings.m_target : "Star";
|
|
FeatureWebAPIUtils::mapFind(target);
|
|
}
|
|
|
|
void StarTrackerGUI::updateChartSubSelect()
|
|
{
|
|
if (ui->chartSelect->currentIndex() == StarTrackerSettings::CHART_SKY_TEMPERATURE)
|
|
{
|
|
ui->chartSubSelect->setItemText(6, QString("%1 MHz %2%3 Equatorial")
|
|
.arg((int)std::round(m_settings.m_frequency/1e6))
|
|
.arg((int)std::round(m_settings.m_beamwidth))
|
|
.arg(QChar(0xb0)));
|
|
ui->chartSubSelect->setItemText(7, QString("%1 MHz %2%3 Galactic")
|
|
.arg((int)std::round(m_settings.m_frequency/1e6))
|
|
.arg((int)std::round(m_settings.m_beamwidth))
|
|
.arg(QChar(0xb0)));
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_chartSelect_currentIndexChanged(int index)
|
|
{
|
|
bool oldState = ui->chartSubSelect->blockSignals(true);
|
|
ui->chartSubSelect->clear();
|
|
|
|
QString currentText = ui->chartSubSelect->currentText();
|
|
|
|
if (index == 0)
|
|
{
|
|
ui->chartSubSelect->addItem("Az/El vs time");
|
|
ui->chartSubSelect->addItem("Polar");
|
|
}
|
|
else if (index == 2)
|
|
{
|
|
ui->chartSubSelect->addItem(QString("150 MHz 5%1 Equatorial").arg(QChar(0xb0)));
|
|
ui->chartSubSelect->addItem(QString("150 MHz 5%1 Galactic").arg(QChar(0xb0)));
|
|
ui->chartSubSelect->addItem("408 MHz 51' Equatorial");
|
|
ui->chartSubSelect->addItem("408 MHz 51' Galactic");
|
|
ui->chartSubSelect->addItem("1420 MHz 35' Equatorial");
|
|
ui->chartSubSelect->addItem("1420 MHz 35' Galactic");
|
|
ui->chartSubSelect->addItem("Custom Equatorial");
|
|
ui->chartSubSelect->addItem("Custom Galactic");
|
|
ui->chartSubSelect->setCurrentIndex(2);
|
|
updateChartSubSelect();
|
|
}
|
|
else if (index == 3)
|
|
{
|
|
ui->chartSubSelect->addItem("Milky Way");
|
|
ui->chartSubSelect->addItem("Milky Way annotated");
|
|
}
|
|
else if (index == 4)
|
|
{
|
|
for (const auto& body : m_settings.m_solarSystemBodies) {
|
|
ui->chartSubSelect->addItem(body);
|
|
}
|
|
ui->chartSubSelect->addItem("-");
|
|
}
|
|
else if (index == 5)
|
|
{
|
|
ui->chartSubSelect->addItem("Io phase vs CML");
|
|
ui->chartSubSelect->addItem("Ganymede phase vs CML");
|
|
}
|
|
|
|
int idx = ui->chartSubSelect->findText(currentText);
|
|
if (idx >= 0) {
|
|
ui->chartSubSelect->setCurrentIndex(idx);
|
|
}
|
|
|
|
ui->chartSubSelect->blockSignals(oldState);
|
|
plotChart();
|
|
|
|
m_settings.m_chartSelect = (StarTrackerSettings::ChartSelect) index;
|
|
m_settingsKeys.append("chartSelect");
|
|
m_settings.m_chartSubSelect = ui->chartSubSelect->currentIndex();
|
|
m_settingsKeys.append("chartSubSelect");
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::on_chartSubSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
|
|
m_settings.m_chartSubSelect = index;
|
|
m_settingsKeys.append("chartSubSelect");
|
|
applySettings();
|
|
|
|
plotChart();
|
|
}
|
|
|
|
double StarTrackerGUI::convertSolarFluxUnits(double sfu)
|
|
{
|
|
switch (m_settings.m_solarFluxUnits)
|
|
{
|
|
case StarTrackerSettings::SFU:
|
|
return sfu;
|
|
case StarTrackerSettings::JANSKY:
|
|
return Units::solarFluxUnitsToJansky(sfu);
|
|
case StarTrackerSettings::WATTS_M_HZ:
|
|
return Units::solarFluxUnitsToWattsPerMetrePerHertz(sfu);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
QString StarTrackerGUI::solarFluxUnit()
|
|
{
|
|
switch (m_settings.m_solarFluxUnits)
|
|
{
|
|
case StarTrackerSettings::SFU:
|
|
return "sfu";
|
|
case StarTrackerSettings::JANSKY:
|
|
return "Jy";
|
|
case StarTrackerSettings::WATTS_M_HZ:
|
|
return "Wm^-2Hz^-1";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// Calculate solar flux at given frequency in SFU
|
|
double StarTrackerGUI::calcSolarFlux(double freqMhz)
|
|
{
|
|
if (m_solarFluxesValid)
|
|
{
|
|
double solarFlux;
|
|
const int fluxes = sizeof(m_solarFluxFrequencies)/sizeof(*m_solarFluxFrequencies);
|
|
int i;
|
|
|
|
for (i = 0; i < fluxes; i++)
|
|
{
|
|
if (freqMhz < m_solarFluxFrequencies[i]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == 0)
|
|
{
|
|
solarFlux = Interpolation::extrapolate(
|
|
(double)m_solarFluxFrequencies[0],
|
|
(double)m_solarFluxes[0],
|
|
(double)m_solarFluxFrequencies[1],
|
|
(double)m_solarFluxes[1],
|
|
freqMhz
|
|
);
|
|
}
|
|
else if (i == fluxes)
|
|
{
|
|
solarFlux = Interpolation::extrapolate(
|
|
(double)m_solarFluxFrequencies[fluxes-2],
|
|
(double)m_solarFluxes[fluxes-2],
|
|
(double)m_solarFluxFrequencies[fluxes-1],
|
|
(double)m_solarFluxes[fluxes-1],
|
|
freqMhz
|
|
);
|
|
}
|
|
else
|
|
{
|
|
solarFlux = Interpolation::interpolate(
|
|
(double)m_solarFluxFrequencies[i-1],
|
|
(double)m_solarFluxes[i-1],
|
|
(double)m_solarFluxFrequencies[i],
|
|
(double)m_solarFluxes[i],
|
|
freqMhz
|
|
);
|
|
}
|
|
|
|
return solarFlux;
|
|
}
|
|
else
|
|
{
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::displaySolarFlux()
|
|
{
|
|
if (((m_settings.m_solarFluxData == StarTrackerSettings::DRAO_2800) && (m_solarFlux == 0.0))
|
|
|| ((m_settings.m_solarFluxData != StarTrackerSettings::DRAO_2800) && !m_solarFluxesValid))
|
|
{
|
|
ui->solarFlux->setText("");
|
|
}
|
|
else
|
|
{
|
|
double solarFlux;
|
|
double freqMhz = m_settings.m_frequency/1000000.0;
|
|
|
|
if (m_settings.m_solarFluxData == StarTrackerSettings::DRAO_2800)
|
|
{
|
|
solarFlux = m_solarFlux;
|
|
ui->solarFlux->setToolTip(QString("Solar flux density at 2800 MHz"));
|
|
}
|
|
else if (m_settings.m_solarFluxData == StarTrackerSettings::TARGET_FREQ)
|
|
{
|
|
solarFlux = calcSolarFlux(freqMhz);
|
|
ui->solarFlux->setToolTip(QString("Solar flux density interpolated to %1 MHz").arg(freqMhz));
|
|
}
|
|
else
|
|
{
|
|
int idx = m_settings.m_solarFluxData-StarTrackerSettings::L_245;
|
|
solarFlux = m_solarFluxes[idx];
|
|
ui->solarFlux->setToolTip(QString("Solar flux density at %1 MHz").arg(m_solarFluxFrequencies[idx]));
|
|
}
|
|
|
|
ui->solarFlux->setText(QString("%1 %2").arg(convertSolarFluxUnits(solarFlux)).arg(solarFluxUnit()));
|
|
ui->solarFlux->setCursorPosition(0);
|
|
|
|
// Calculate value at target frequency in Jy for Radio Astronomy plugin
|
|
m_starTracker->getInputMessageQueue()->push(StarTracker::MsgSetSolarFlux::create(Units::solarFluxUnitsToJansky(calcSolarFlux(freqMhz))));
|
|
}
|
|
}
|
|
|
|
bool StarTrackerGUI::readSolarFlux()
|
|
{
|
|
QFile file(getSolarFluxFilename());
|
|
QDateTime lastModified = file.fileTime(QFileDevice::FileModificationTime);
|
|
|
|
if (QDateTime::currentDateTime().secsTo(lastModified) >= -(60*60*24))
|
|
{
|
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
{
|
|
QByteArray bytes = file.readLine();
|
|
QString string(bytes);
|
|
// HHMMSS 245 410 610 1415 2695 4995 8800 15400 Mhz
|
|
// 000000 000019 000027 000037 000056 000073 000116 000202 000514 sfu
|
|
// Occasionally, file will contain ////// in a column, presumably to indicate no data
|
|
// Values can be negative
|
|
QRegularExpression re("([0-9]{2})([0-9]{2})([0-9]{2}) (-?[0-9\\/]+) (-?[0-9\\/]+) (-?[0-9\\/]+) (-?[0-9\\/]+) (-?[0-9\\/]+) (-?[0-9\\/]+) (-?[0-9\\/]+) (-?[0-9\\/]+)");
|
|
QRegularExpressionMatch match = re.match(string);
|
|
|
|
if (match.hasMatch())
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
m_solarFluxes[i] = match.capturedTexts()[i+4].toInt();
|
|
m_solarFluxesValid = true;
|
|
displaySolarFlux();
|
|
plotChart();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "StarTrackerGUI::readSolarFlux: No match for " << string;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "StarTrackerGUI::readSolarFlux: Solar flux data is more than 1 day old";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void StarTrackerGUI::networkManagerFinished(QNetworkReply *reply)
|
|
{
|
|
ui->solarFlux->setText(""); // Don't show obsolete data
|
|
QNetworkReply::NetworkError replyError = reply->error();
|
|
|
|
if (replyError)
|
|
{
|
|
qWarning() << "StarTrackerGUI::networkManagerFinished:"
|
|
<< " error(" << (int) replyError
|
|
<< "): " << replyError
|
|
<< ": " << reply->errorString();
|
|
}
|
|
else
|
|
{
|
|
QString answer = reply->readAll();
|
|
QRegularExpression re("\\<th\\>Observed Flux Density\\<\\/th\\>\\<td\\>([0-9]+(\\.[0-9]+)?)\\<\\/td\\>");
|
|
QRegularExpressionMatch match = re.match(answer);
|
|
|
|
if (match.hasMatch())
|
|
{
|
|
m_solarFlux = match.capturedTexts()[1].toDouble();
|
|
displaySolarFlux();
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "StarTrackerGUI::networkManagerFinished - No Solar flux found: " << answer;
|
|
}
|
|
}
|
|
|
|
reply->deleteLater();
|
|
}
|
|
|
|
QString StarTrackerGUI::getSolarFluxFilename()
|
|
{
|
|
return HttpDownloadManager::downloadDir() + "/solar_flux.srd";
|
|
}
|
|
|
|
void StarTrackerGUI::updateSolarFlux(bool all)
|
|
{
|
|
qDebug() << "StarTrackerGUI: Updating Solar flux data";
|
|
|
|
if ((m_settings.m_solarFluxData != StarTrackerSettings::DRAO_2800) || all)
|
|
{
|
|
QDate today = QDateTime::currentDateTimeUtc().date();
|
|
QString solarFluxFile = getSolarFluxFilename();
|
|
|
|
if (m_dlm.confirmDownload(solarFluxFile, nullptr, 0))
|
|
{
|
|
QString urlString = QString("https://downloads.sws.bom.gov.au/wdc/wdc_solradio/data/learmonth/SRD/%1/L%2.SRD")
|
|
.arg(today.year()).arg(today.toString("yyMMdd"));
|
|
m_dlm.download(QUrl(urlString), solarFluxFile, this);
|
|
}
|
|
}
|
|
|
|
if ((m_settings.m_solarFluxData == StarTrackerSettings::DRAO_2800) || all)
|
|
{
|
|
m_networkRequest.setUrl(QUrl("https://www.spaceweather.gc.ca/forecast-prevision/solar-solaire/solarflux/sx-4-en.php"));
|
|
m_networkManager->get(m_networkRequest);
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::autoUpdateSolarFlux()
|
|
{
|
|
updateSolarFlux(false);
|
|
}
|
|
|
|
void StarTrackerGUI::on_downloadSolarFlux_clicked()
|
|
{
|
|
updateSolarFlux(true);
|
|
}
|
|
|
|
void StarTrackerGUI::on_darkTheme_clicked(bool checked)
|
|
{
|
|
m_settings.m_chartsDarkTheme = checked;
|
|
|
|
if (m_solarFluxChart) {
|
|
m_solarFluxChart->setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
|
}
|
|
|
|
m_chart.setTheme(m_settings.m_chartsDarkTheme ? QChart::ChartThemeDark : QChart::ChartThemeLight);
|
|
plotChart();
|
|
m_settingsKeys.append("chartsDarkTheme");
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::on_night_clicked(bool checked)
|
|
{
|
|
m_settings.m_night = checked;
|
|
plotChart();
|
|
m_settingsKeys.append("night");
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::on_logScale_clicked(bool checked)
|
|
{
|
|
m_settings.m_logScale = checked;
|
|
|
|
if (m_settings.m_logScale)
|
|
{
|
|
// Pluto is ~56 from Sun on log scale
|
|
const double r = 57;
|
|
m_solarSystemScene->setSceneRect(-r, -r, r * 2, r * 2);
|
|
}
|
|
else
|
|
{
|
|
// Mercury is 0.4AU. Neptune is 30 AU
|
|
const double max = 32.0;
|
|
const double pixelScale = 20.0;
|
|
m_solarSystemScene->setSceneRect(-max * pixelScale, -max * pixelScale, 2.0 * max * pixelScale, 2.0 * max * pixelScale);
|
|
}
|
|
|
|
plotChart();
|
|
m_settingsKeys.append("logScale");
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::on_drawSun_clicked(bool checked)
|
|
{
|
|
m_settings.m_drawSunOnSkyTempChart = checked;
|
|
plotChart();
|
|
m_settingsKeys.append("drawSunOnSkyTempChart");
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::on_drawMoon_clicked(bool checked)
|
|
{
|
|
m_settings.m_drawMoonOnSkyTempChart = checked;
|
|
plotChart();
|
|
m_settingsKeys.append("drawMoonOnSkyTempChart");
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::downloadFinished(const QString& filename, bool success)
|
|
{
|
|
(void) filename;
|
|
|
|
if (success && filename.endsWith("solar_flux.srd")) {
|
|
readSolarFlux();
|
|
} else if (!success) {
|
|
QMessageBox::warning(this, "Failed to download file", QString("Failed to download %1").arg(filename));
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::majorBodiesUpdated(const QHash<QString, JPLHorizons::BodyID>& bodies)
|
|
{
|
|
m_jplBodies = QStringList();
|
|
|
|
QHashIterator<QString, JPLHorizons::BodyID> itr(bodies);
|
|
while (itr.hasNext())
|
|
{
|
|
itr.next();
|
|
const JPLHorizons::BodyID& body = itr.value();
|
|
m_jplBodies.append(body.m_name);
|
|
}
|
|
|
|
m_jplBodies.sort();
|
|
updateTargetList();
|
|
}
|
|
|
|
void StarTrackerGUI::updateTargetList()
|
|
{
|
|
bool block = ui->target->blockSignals(true);
|
|
|
|
ui->target->clear();
|
|
|
|
if (m_settings.m_targetSource == "SDRangel")
|
|
{
|
|
QStringList builtinTargets = {
|
|
"Sun",
|
|
"Moon",
|
|
"PSR B0329+54",
|
|
"PSR B0833-45",
|
|
"Sagittarius A",
|
|
"Cassiopeia A",
|
|
"Cygnus A",
|
|
"Taurus A (M1)",
|
|
"Virgo A (M87)",
|
|
"Custom RA/Dec",
|
|
"Custom Az/El",
|
|
"Custom l/b",
|
|
"S7",
|
|
"S8",
|
|
"S9"
|
|
};
|
|
|
|
for (const auto& target : builtinTargets) {
|
|
ui->target->addItem(target);
|
|
}
|
|
for (const auto& target : m_availableFeatures) {
|
|
ui->target->addItem(target);
|
|
}
|
|
}
|
|
else if (m_settings.m_targetSource == "SPICE")
|
|
{
|
|
for (const auto& target : m_spiceEphemerides.getTargets(m_settings.m_spiceEphemerides)) {
|
|
ui->target->addItem(target);
|
|
}
|
|
}
|
|
else if (m_settings.m_targetSource == "Horizons")
|
|
{
|
|
for (const auto& body : m_jplBodies) {
|
|
ui->target->addItem(body);
|
|
}
|
|
}
|
|
|
|
ui->target->blockSignals(block);
|
|
|
|
int index = ui->target->findText(m_settings.m_target, Qt::MatchFixedString); // Case insensitive
|
|
if (index >= 0) {
|
|
ui->target->setCurrentIndex(index);
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::makeUIConnections()
|
|
{
|
|
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &StarTrackerGUI::on_startStop_toggled);
|
|
QObject::connect(ui->link, &ButtonSwitch::clicked, this, &StarTrackerGUI::on_link_clicked);
|
|
QObject::connect(ui->useMyPosition, &QToolButton::clicked, this, &StarTrackerGUI::on_useMyPosition_clicked);
|
|
QObject::connect(ui->latitude, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_latitude_valueChanged);
|
|
QObject::connect(ui->longitude, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_longitude_valueChanged);
|
|
QObject::connect(ui->rightAscension, &QLineEdit::editingFinished, this, &StarTrackerGUI::on_rightAscension_editingFinished);
|
|
QObject::connect(ui->declination, &QLineEdit::editingFinished, this, &StarTrackerGUI::on_declination_editingFinished);
|
|
QObject::connect(ui->azimuth, &DMSSpinBox::valueChanged, this, &StarTrackerGUI::on_azimuth_valueChanged);
|
|
QObject::connect(ui->elevation, &DMSSpinBox::valueChanged, this, &StarTrackerGUI::on_elevation_valueChanged);
|
|
QObject::connect(ui->azimuthOffset, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_azimuthOffset_valueChanged);
|
|
QObject::connect(ui->elevationOffset, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_elevationOffset_valueChanged);
|
|
QObject::connect(ui->galacticLatitude, &DMSSpinBox::valueChanged, this, &StarTrackerGUI::on_galacticLatitude_valueChanged);
|
|
QObject::connect(ui->galacticLongitude, &DMSSpinBox::valueChanged, this, &StarTrackerGUI::on_galacticLongitude_valueChanged);
|
|
QObject::connect(ui->frequency, qOverload<int>(&QSpinBox::valueChanged), this, &StarTrackerGUI::on_frequency_valueChanged);
|
|
QObject::connect(ui->beamwidth, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_beamwidth_valueChanged);
|
|
QObject::connect(ui->target, qOverload<int>(&QComboBox::currentIndexChanged), this, &StarTrackerGUI::on_target_currentIndexChanged);
|
|
QObject::connect(ui->targetSource, qOverload<int>(&QComboBox::currentIndexChanged), this, &StarTrackerGUI::on_targetSource_currentIndexChanged);
|
|
QObject::connect(ui->displaySettings, &QToolButton::clicked, this, &StarTrackerGUI::on_displaySettings_clicked);
|
|
QObject::connect(ui->dateTimeSelect, &QComboBox::currentTextChanged, this, &StarTrackerGUI::on_dateTimeSelect_currentTextChanged);
|
|
QObject::connect(ui->dateTime, &WrappingDateTimeEdit::dateTimeChanged, this, &StarTrackerGUI::on_dateTime_dateTimeChanged);
|
|
QObject::connect(ui->utc, &QToolButton::clicked, this, &StarTrackerGUI::on_utc_clicked);
|
|
QObject::connect(ui->setTimeToNow, &QToolButton::clicked, this, &StarTrackerGUI::on_setTimeToNow_clicked);
|
|
QObject::connect(ui->viewOnMap, &QToolButton::clicked, this, &StarTrackerGUI::on_viewOnMap_clicked);
|
|
QObject::connect(ui->chartSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &StarTrackerGUI::on_chartSelect_currentIndexChanged);
|
|
QObject::connect(ui->chartSubSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &StarTrackerGUI::on_chartSubSelect_currentIndexChanged);
|
|
QObject::connect(ui->downloadSolarFlux, &QToolButton::clicked, this, &StarTrackerGUI::on_downloadSolarFlux_clicked);
|
|
QObject::connect(ui->darkTheme, &QToolButton::clicked, this, &StarTrackerGUI::on_darkTheme_clicked);
|
|
QObject::connect(ui->zoomIn, &QToolButton::clicked, this, &StarTrackerGUI::on_zoomIn_clicked);
|
|
QObject::connect(ui->zoomOut, &QToolButton::clicked, this, &StarTrackerGUI::on_zoomOut_clicked);
|
|
QObject::connect(ui->addAnimationFrame, &QToolButton::clicked, this, &StarTrackerGUI::on_addAnimationFrame_clicked);
|
|
QObject::connect(ui->clearAnimation, &QToolButton::clicked, this, &StarTrackerGUI::on_clearAnimation_clicked);
|
|
QObject::connect(ui->saveAnimation, &QToolButton::clicked, this, &StarTrackerGUI::on_saveAnimation_clicked);
|
|
QObject::connect(ui->logScale, &QToolButton::clicked, this, &StarTrackerGUI::on_logScale_clicked);
|
|
QObject::connect(ui->night, &QToolButton::clicked, this, &StarTrackerGUI::on_night_clicked);
|
|
QObject::connect(ui->drawSun, &QToolButton::clicked, this, &StarTrackerGUI::on_drawSun_clicked);
|
|
QObject::connect(ui->drawMoon, &QToolButton::clicked, this, &StarTrackerGUI::on_drawMoon_clicked);
|
|
}
|