mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-03 23:41:16 -05:00
cddc8c9b83
Plot Sun and Moon on sky temperature chart. Plot markers on Galactic line-of-sight chart. Create animations from Galactic line-of-sight chart. Allow weather at antenna location to be downloaded from openweathermap.org Allow target to be entered as Galactic longitude / latitude. Add azimuth and elevation offsets to support scans around targets. Add S7, S8 and S9 targets. Refactor some code from GUI to main plugin, so computed values can be used in other plugins.
2023 lines
68 KiB
C++
2023 lines
68 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
|
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
|
// //
|
|
// This program is free software; you can redistribute it and/or modify //
|
|
// it under the terms of the GNU General Public License as published by //
|
|
// the Free Software Foundation as version 3 of the License, or //
|
|
// (at your option) any later version. //
|
|
// //
|
|
// This program is distributed in the hope that it will be useful, //
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
// GNU General Public License V3 for more details. //
|
|
// //
|
|
// You should have received a copy of the GNU General Public License //
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <QMessageBox>
|
|
#include <QLineEdit>
|
|
#include <QRegExp>
|
|
#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/featureuiset.h"
|
|
#include "feature/featurewebapiutils.h"
|
|
#include "gui/basicfeaturesettingsdialog.h"
|
|
#include "gui/dmsspinbox.h"
|
|
#include "gui/graphicsviewzoom.h"
|
|
#include "mainwindow.h"
|
|
#include "device/deviceuiset.h"
|
|
#include "util/units.h"
|
|
#include "util/astronomy.h"
|
|
#include "util/interpolation.h"
|
|
#include "util/png.h"
|
|
|
|
#include "ui_startrackergui.h"
|
|
#include "startracker.h"
|
|
#include "startrackergui.h"
|
|
#include "startrackerreport.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))
|
|
{
|
|
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;
|
|
m_settings = cfg.getSettings();
|
|
blockApplySettings(true);
|
|
displaySettings();
|
|
blockApplySettings(false);
|
|
|
|
return true;
|
|
}
|
|
else if (StarTrackerReport::MsgReportAzAl::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportAzAl& azAl = (StarTrackerReport::MsgReportAzAl&) message;
|
|
blockApplySettings(true);
|
|
ui->azimuth->setValue(azAl.getAzimuth());
|
|
ui->elevation->setValue(azAl.getElevation());
|
|
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());
|
|
ui->rightAscension->setText(m_settings.m_ra);
|
|
ui->declination->setText(m_settings.m_dec);
|
|
}
|
|
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::MsgReportGalactic::match(message))
|
|
{
|
|
StarTrackerReport::MsgReportGalactic& galactic = (StarTrackerReport::MsgReportGalactic&) message;
|
|
blockApplySettings(true);
|
|
ui->galacticLongitude->setValue(galactic.getL());
|
|
ui->galacticLatitude->setValue(galactic.getB());
|
|
blockApplySettings(false);
|
|
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");
|
|
ui->azimuth->setValue(swgSettings->getAzimuth());
|
|
ui->elevation->setValue(swgSettings->getElevation());
|
|
}
|
|
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;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void StarTrackerGUI::handleInputMessages()
|
|
{
|
|
Message* message;
|
|
|
|
while ((message = getInputMessageQueue()->pop()))
|
|
{
|
|
if (handleMessage(*message)) {
|
|
delete message;
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
|
{
|
|
(void) widget;
|
|
(void) rollDown;
|
|
}
|
|
|
|
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_lastFeatureState(0),
|
|
m_azElLineChart(nullptr),
|
|
m_azElPolarChart(nullptr),
|
|
m_solarFluxChart(nullptr),
|
|
m_networkManager(nullptr),
|
|
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_milkyWayImages{QPixmap(":/startracker/startracker/milkyway.png"),
|
|
QPixmap(":/startracker/startracker/milkywayannotated.png")},
|
|
m_sunRA(0.0),
|
|
m_sunDec(0.0),
|
|
m_moonRA(0.0),
|
|
m_moonDec(0.0)
|
|
{
|
|
ui->setupUi(this);
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
setChannelWidget(false);
|
|
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
|
m_starTracker = reinterpret_cast<StarTracker*>(feature);
|
|
m_starTracker->setMessageQueueToGUI(&m_inputMessageQueue);
|
|
|
|
m_featureUISet->addRollupWidget(this);
|
|
|
|
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(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("");
|
|
|
|
// Intialise 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);
|
|
|
|
// 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();
|
|
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
|
|
|
readSolarFlux();
|
|
connect(&m_solarFluxTimer, SIGNAL(timeout()), this, SLOT(autoUpdateSolarFlux()));
|
|
m_solarFluxTimer.start(1000*60*60*24); // Update every 24hours
|
|
autoUpdateSolarFlux();
|
|
|
|
createGalacticLineOfSightScene();
|
|
plotChart();
|
|
}
|
|
|
|
StarTrackerGUI::~StarTrackerGUI()
|
|
{
|
|
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
|
delete m_networkManager;
|
|
delete ui;
|
|
}
|
|
|
|
void StarTrackerGUI::blockApplySettings(bool block)
|
|
{
|
|
m_doApplySettings = !block;
|
|
}
|
|
|
|
void StarTrackerGUI::displaySettings()
|
|
{
|
|
setTitleColor(m_settings.m_rgbColor);
|
|
setWindowTitle(m_settings.m_title);
|
|
blockApplySettings(true);
|
|
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->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->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_azOffset);
|
|
ui->elevationOffset->setValue(m_settings.m_elOffset);
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
ui->dateTime->setDateTime(QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs));
|
|
ui->dateTime->setVisible(true);
|
|
ui->dateTimeSelect->setCurrentIndex(1);
|
|
}
|
|
if ((m_settings.m_solarFluxData != StarTrackerSettings::DRAO_2800) && !m_solarFluxesValid)
|
|
autoUpdateSolarFlux();
|
|
ui->frequency->setValue(m_settings.m_frequency/1000000.0);
|
|
ui->beamwidth->setValue(m_settings.m_beamwidth);
|
|
updateForTarget();
|
|
plotChart();
|
|
blockApplySettings(false);
|
|
}
|
|
|
|
void StarTrackerGUI::leaveEvent(QEvent*)
|
|
{
|
|
}
|
|
|
|
void StarTrackerGUI::enterEvent(QEvent*)
|
|
{
|
|
}
|
|
|
|
void StarTrackerGUI::onMenuDialogCalled(const QPoint &p)
|
|
{
|
|
if (m_contextMenuType == ContextMenuChannelSettings)
|
|
{
|
|
BasicFeatureSettingsDialog dialog(this);
|
|
dialog.setTitle(m_settings.m_title);
|
|
dialog.setColor(m_settings.m_rgbColor);
|
|
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.move(p);
|
|
dialog.exec();
|
|
|
|
m_settings.m_rgbColor = dialog.getColor().rgb();
|
|
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();
|
|
|
|
setWindowTitle(m_settings.m_title);
|
|
setTitleColor(m_settings.m_rgbColor);
|
|
|
|
applySettings();
|
|
}
|
|
|
|
resetContextMenuType();
|
|
}
|
|
|
|
void StarTrackerGUI::on_startStop_toggled(bool checked)
|
|
{
|
|
if (m_doApplySettings)
|
|
{
|
|
StarTracker::MsgStartStop *message = StarTracker::MsgStartStop::create(checked);
|
|
m_starTracker->getInputMessageQueue()->push(message);
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_latitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_latitude = value;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_longitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_longitude = value;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_rightAscension_editingFinished()
|
|
{
|
|
m_settings.m_ra = ui->rightAscension->text();
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_declination_editingFinished()
|
|
{
|
|
m_settings.m_dec = ui->declination->text();
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_azimuth_valueChanged(double value)
|
|
{
|
|
m_settings.m_az = value;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_elevation_valueChanged(double value)
|
|
{
|
|
m_settings.m_el = value;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_azimuthOffset_valueChanged(double value)
|
|
{
|
|
m_settings.m_azOffset = value;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_elevationOffset_valueChanged(double value)
|
|
{
|
|
m_settings.m_elOffset = value;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_galacticLatitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_b = value;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_galacticLongitude_valueChanged(double value)
|
|
{
|
|
m_settings.m_l = value;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::updateForTarget()
|
|
{
|
|
if (m_settings.m_target == "Sun")
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->rightAscension->setText("");
|
|
ui->declination->setText("");
|
|
}
|
|
else if (m_settings.m_target == "Moon")
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->rightAscension->setText("");
|
|
ui->declination->setText("");
|
|
}
|
|
else if (m_settings.m_target == "Custom RA/Dec")
|
|
{
|
|
ui->rightAscension->setReadOnly(false);
|
|
ui->declination->setReadOnly(false);
|
|
}
|
|
else 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
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->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();
|
|
}
|
|
if (m_settings.m_target != "Custom Az/El")
|
|
{
|
|
ui->azimuth->setReadOnly(true);
|
|
ui->elevation->setReadOnly(true);
|
|
// Clear as no longer valid when target has changed
|
|
ui->azimuth->setText("");
|
|
ui->elevation->setText("");
|
|
}
|
|
else
|
|
{
|
|
ui->rightAscension->setReadOnly(true);
|
|
ui->declination->setReadOnly(true);
|
|
ui->azimuth->setReadOnly(false);
|
|
ui->elevation->setReadOnly(false);
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_target_currentTextChanged(const QString &text)
|
|
{
|
|
m_settings.m_target = text;
|
|
applySettings();
|
|
updateForTarget();
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::updateLST()
|
|
{
|
|
QDateTime dt;
|
|
|
|
if (m_settings.m_dateTime.isEmpty())
|
|
dt = QDateTime::currentDateTime();
|
|
else
|
|
dt = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
|
|
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, force);
|
|
m_starTracker->getInputMessageQueue()->push(message);
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_link_clicked(bool checked)
|
|
{
|
|
m_settings.m_link = checked;
|
|
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;
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
|
|
// Show settings dialog
|
|
void StarTrackerGUI::on_displaySettings_clicked()
|
|
{
|
|
StarTrackerSettingsDialog dialog(&m_settings);
|
|
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() == 1)
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_dateTimeSelect_currentTextChanged(const QString &text)
|
|
{
|
|
if (text == "Now")
|
|
{
|
|
m_settings.m_dateTime = "";
|
|
ui->dateTime->setVisible(false);
|
|
}
|
|
else
|
|
{
|
|
m_settings.m_dateTime = ui->dateTime->dateTime().toString(Qt::ISODateWithMs);
|
|
ui->dateTime->setVisible(true);
|
|
}
|
|
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);
|
|
applySettings();
|
|
plotChart();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::plotChart()
|
|
{
|
|
if (ui->chartSelect->currentIndex() == 0)
|
|
{
|
|
if (ui->chartSubSelect->currentIndex() == 0) {
|
|
plotElevationLineChart();
|
|
} else {
|
|
plotElevationPolarChart();
|
|
}
|
|
}
|
|
else if (ui->chartSelect->currentIndex() == 1)
|
|
{
|
|
plotSolarFluxChart();
|
|
}
|
|
else if (ui->chartSelect->currentIndex() == 2)
|
|
{
|
|
plotSkyTemperatureChart();
|
|
}
|
|
else if (ui->chartSelect->currentIndex() == 3)
|
|
{
|
|
plotGalacticLineOfSight();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::raDecChanged()
|
|
{
|
|
if (ui->chartSelect->currentIndex() == 2) {
|
|
plotSkyTemperatureChart();
|
|
} else if (ui->chartSelect->currentIndex() == 3) {
|
|
plotGalacticLineOfSight();
|
|
}
|
|
}
|
|
|
|
void StarTrackerGUI::on_frequency_valueChanged(int value)
|
|
{
|
|
m_settings.m_frequency = value*1000000.0;
|
|
applySettings();
|
|
if (ui->chartSelect->currentIndex() != 0)
|
|
{
|
|
updateChartSubSelect();
|
|
plotChart();
|
|
}
|
|
displaySolarFlux();
|
|
}
|
|
|
|
void StarTrackerGUI::on_beamwidth_valueChanged(double value)
|
|
{
|
|
m_settings.m_beamwidth = value;
|
|
applySettings();
|
|
updateChartSubSelect();
|
|
if (ui->chartSelect->currentIndex() == 2)
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::plotSolarFluxChart()
|
|
{
|
|
ui->chart->setVisible(true);
|
|
ui->image->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);
|
|
|
|
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
|
|
if (m_settings.m_dateTime == "") {
|
|
dt = QDateTime::currentDateTime();
|
|
} else {
|
|
dt = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
}
|
|
|
|
// 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_zoom = new GraphicsViewZoom(ui->image); // Deleted automatically when view is deleted
|
|
|
|
QGraphicsScene *scene = new QGraphicsScene(ui->image);
|
|
scene->setBackgroundBrush(QBrush(Qt::black));
|
|
|
|
// Milkyway images
|
|
for (int i = 0; i < m_milkyWayImages.size(); i++)
|
|
{
|
|
m_milkyWayItems.append(scene->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 = scene->addLine(511, 708, 511, 708, pen);
|
|
|
|
ui->image->setScene(scene);
|
|
ui->image->show();
|
|
}
|
|
|
|
void StarTrackerGUI::plotGalacticLineOfSight()
|
|
{
|
|
if (!ui->image->isVisible())
|
|
{
|
|
// Start zoomed out
|
|
ui->image->fitInView(m_milkyWayItems[0], Qt::KeepAspectRatio);
|
|
}
|
|
|
|
// Draw top-down image of Milky Way
|
|
ui->chart->setVisible(false);
|
|
ui->image->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(true);
|
|
ui->clearAnimation->setVisible(true);
|
|
ui->saveAnimation->setVisible(true);
|
|
|
|
// 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 = Astronomy::raToDecimal(m_settings.m_ra);
|
|
float dec = Astronomy::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::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::plotSkyTemperatureChart()
|
|
{
|
|
ui->chart->setVisible(true);
|
|
ui->image->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);
|
|
|
|
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 = Astronomy::raToDecimal(m_settings.m_ra);
|
|
float dec = Astronomy::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->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);
|
|
|
|
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));
|
|
|
|
double maxElevation = -90.0;
|
|
|
|
QLineSeries *elSeries = new QLineSeries();
|
|
QList<QLineSeries *> azSeriesList;
|
|
QLineSeries *azSeries = new QLineSeries();
|
|
azSeriesList.append(azSeries);
|
|
QPen pen(getSeriesColor(1), 2, Qt::SolidLine);
|
|
azSeries->setPen(pen);
|
|
|
|
QDateTime dt;
|
|
if (m_settings.m_dateTime.isEmpty())
|
|
dt = QDateTime::currentDateTime();
|
|
else
|
|
dt = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
dt.setTime(QTime(0,0));
|
|
QDateTime startTime = dt;
|
|
QDateTime endTime = dt;
|
|
double prevAz;
|
|
int timestep = 10*60;
|
|
for (int step = 0; step <= 24*60*60/timestep; step++)
|
|
{
|
|
AzAlt aa;
|
|
RADec rd;
|
|
|
|
// Calculate elevation of desired object
|
|
if (m_settings.m_target == "Sun")
|
|
Astronomy::sunPosition(aa, rd, m_settings.m_latitude, m_settings.m_longitude, dt);
|
|
else if (m_settings.m_target == "Moon")
|
|
Astronomy::moonPosition(aa, rd, m_settings.m_latitude, m_settings.m_longitude, dt);
|
|
else
|
|
{
|
|
rd.ra = Astronomy::raToDecimal(m_settings.m_ra);
|
|
rd.dec = Astronomy::decToDecimal(m_settings.m_dec);
|
|
aa = Astronomy::raDecToAzAlt(rd, m_settings.m_latitude, m_settings.m_longitude, dt, !m_settings.m_jnow);
|
|
}
|
|
|
|
if (aa.alt > maxElevation)
|
|
maxElevation = aa.alt;
|
|
|
|
// Adjust for refraction
|
|
if (m_settings.m_refraction == "Positional Astronomy Library")
|
|
{
|
|
aa.alt += Astronomy::refractionPAL(aa.alt, m_settings.m_pressure, m_settings.m_temperature, m_settings.m_humidity,
|
|
m_settings.m_frequency, m_settings.m_latitude, m_settings.m_heightAboveSeaLevel,
|
|
m_settings.m_temperatureLapseRate);
|
|
if (aa.alt > 90.0)
|
|
aa.alt = 90.0f;
|
|
}
|
|
else if (m_settings.m_refraction == "Saemundsson")
|
|
{
|
|
aa.alt += Astronomy::refractionSaemundsson(aa.alt, m_settings.m_pressure, m_settings.m_temperature);
|
|
if (aa.alt > 90.0)
|
|
aa.alt = 90.0f;
|
|
}
|
|
|
|
if (step == 0)
|
|
prevAz = aa.az;
|
|
|
|
if (((prevAz >= 270) && (aa.az < 90)) || ((prevAz < 90) && (aa.az >= 270)))
|
|
{
|
|
azSeries = new QLineSeries();
|
|
azSeriesList.append(azSeries);
|
|
azSeries->setPen(pen);
|
|
}
|
|
|
|
elSeries->append(dt.toMSecsSinceEpoch(), aa.alt);
|
|
azSeries->append(dt.toMSecsSinceEpoch(), aa.az);
|
|
|
|
endTime = dt;
|
|
prevAz = aa.az;
|
|
dt = dt.addSecs(timestep); // addSecs accounts for daylight savings jumps
|
|
}
|
|
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);
|
|
}
|
|
elSeries->attachAxis(xAxis);
|
|
elSeries->attachAxis(yLeftAxis);
|
|
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 (maxElevation < 0)
|
|
m_azElLineChart->setTitle("Not visible from this latitude");
|
|
else
|
|
m_azElLineChart->setTitle("");
|
|
ui->chart->setChart(m_azElLineChart);
|
|
|
|
delete oldChart;
|
|
}
|
|
|
|
// Plot target elevation angle over the day
|
|
void StarTrackerGUI::plotElevationPolarChart()
|
|
{
|
|
ui->chart->setVisible(true);
|
|
ui->image->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);
|
|
|
|
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 dt;
|
|
if (m_settings.m_dateTime.isEmpty())
|
|
dt = QDateTime::currentDateTime();
|
|
else
|
|
dt = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
dt.setTime(QTime(0,0));
|
|
QDateTime startTime = dt;
|
|
QDateTime endTime = dt;
|
|
QDateTime riseTime;
|
|
QDateTime setTime;
|
|
int riseIdx = -1;
|
|
int setIdx = -1;
|
|
int idx = 0;
|
|
int timestep = 10*60; // Rise/set times accurate to nearest 10 minutes
|
|
double prevAlt;
|
|
for (int step = 0; step <= 24*60*60/timestep; step++)
|
|
{
|
|
AzAlt aa;
|
|
RADec rd;
|
|
|
|
// Calculate elevation of desired object
|
|
if (m_settings.m_target == "Sun")
|
|
Astronomy::sunPosition(aa, rd, m_settings.m_latitude, m_settings.m_longitude, dt);
|
|
else if (m_settings.m_target == "Moon")
|
|
Astronomy::moonPosition(aa, rd, m_settings.m_latitude, m_settings.m_longitude, dt);
|
|
else
|
|
{
|
|
rd.ra = Astronomy::raToDecimal(m_settings.m_ra);
|
|
rd.dec = Astronomy::decToDecimal(m_settings.m_dec);
|
|
aa = Astronomy::raDecToAzAlt(rd, m_settings.m_latitude, m_settings.m_longitude, dt, !m_settings.m_jnow);
|
|
}
|
|
|
|
if (aa.alt > maxElevation)
|
|
maxElevation = aa.alt;
|
|
|
|
// Adjust for refraction
|
|
if (m_settings.m_refraction == "Positional Astronomy Library")
|
|
{
|
|
aa.alt += Astronomy::refractionPAL(aa.alt, m_settings.m_pressure, m_settings.m_temperature, m_settings.m_humidity,
|
|
m_settings.m_frequency, m_settings.m_latitude, m_settings.m_heightAboveSeaLevel,
|
|
m_settings.m_temperatureLapseRate);
|
|
if (aa.alt > 90.0)
|
|
aa.alt = 90.0f;
|
|
}
|
|
else if (m_settings.m_refraction == "Saemundsson")
|
|
{
|
|
aa.alt += Astronomy::refractionSaemundsson(aa.alt, m_settings.m_pressure, m_settings.m_temperature);
|
|
if (aa.alt > 90.0)
|
|
aa.alt = 90.0f;
|
|
}
|
|
|
|
if (idx == 0)
|
|
prevAlt = aa.alt;
|
|
|
|
// We can have set before rise in a day, if the object starts > 0
|
|
if ((aa.alt >= 0.0) && (prevAlt < 0.0))
|
|
{
|
|
riseTime = dt;
|
|
riseIdx = idx;
|
|
}
|
|
if ((aa.alt < 0.0) && (prevAlt >= 0.0))
|
|
{
|
|
setTime = endTime;
|
|
setIdx = idx;
|
|
}
|
|
|
|
polarSeries->append(aa.az, 90 - aa.alt);
|
|
idx++;
|
|
|
|
endTime = dt;
|
|
prevAlt = aa.alt;
|
|
|
|
dt = dt.addSecs(timestep); // addSecs accounts for daylight savings jumps
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
if (maxElevation < 0)
|
|
m_azElPolarChart->setTitle("Not visible from this latitude");
|
|
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() == 2)
|
|
{
|
|
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();
|
|
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");
|
|
}
|
|
ui->chartSubSelect->blockSignals(oldState);
|
|
plotChart();
|
|
}
|
|
|
|
void StarTrackerGUI::on_chartSubSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
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
|
|
QRegExp 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\\/]+)");
|
|
if (re.indexIn(string) != -1)
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
m_solarFluxes[i] = re.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();
|
|
QRegExp re("\\<th\\>Observed Flux Density\\<\\/th\\>\\<td\\>([0-9]+(\\.[0-9]+)?)\\<\\/td\\>");
|
|
if (re.indexIn(answer) != -1)
|
|
{
|
|
m_solarFlux = re.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://www.sws.bom.gov.au/Category/World Data Centre/Data Display and Download/Solar Radio/station/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();
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::on_drawSun_clicked(bool checked)
|
|
{
|
|
m_settings.m_drawSunOnSkyTempChart = checked;
|
|
plotChart();
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::on_drawMoon_clicked(bool checked)
|
|
{
|
|
m_settings.m_drawMoonOnSkyTempChart = checked;
|
|
plotChart();
|
|
applySettings();
|
|
}
|
|
|
|
void StarTrackerGUI::downloadFinished(const QString& filename, bool success)
|
|
{
|
|
(void) filename;
|
|
if (success)
|
|
readSolarFlux();
|
|
}
|