From f7ed6622381c57ce38a715e989682884c1b60a4d Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 3 Apr 2023 16:53:51 +0100 Subject: [PATCH] Display rotator on Star Tracker and Satellite Tracker polar charts for #1641. --- plugins/feature/satellitetracker/readme.md | 5 +- .../satellitetracker/satellitetrackergui.cpp | 121 +++- .../satellitetracker/satellitetrackergui.h | 2 + .../satellitetrackersettings.cpp | 251 ++++---- .../satellitetrackersettings.h | 1 + .../satellitetrackersettingsdialog.cpp | 2 + .../satellitetrackersettingsdialog.ui | 195 ++++--- .../satellitetracker/satellitetrackersgp4.cpp | 2 +- plugins/feature/startracker/readme.md | 2 + .../feature/startracker/startrackergui.cpp | 114 +++- plugins/feature/startracker/startrackergui.h | 2 + .../startracker/startrackersettings.cpp | 9 + .../feature/startracker/startrackersettings.h | 1 + .../startracker/startrackersettingsdialog.cpp | 4 + .../startracker/startrackersettingsdialog.ui | 539 +++++++++--------- 15 files changed, 787 insertions(+), 463 deletions(-) diff --git a/plugins/feature/satellitetracker/readme.md b/plugins/feature/satellitetracker/readme.md index d079d1805..d0386545c 100644 --- a/plugins/feature/satellitetracker/readme.md +++ b/plugins/feature/satellitetracker/readme.md @@ -116,7 +116,10 @@ On the display tab, you can set: * The default frequency in MHz that is used for calculating Doppler and free space path loss in the Satellite Data table. * The units used to display azimuth and elevation to the target satellite. This can be in degrees, minutes and seconds or decimal degrees. * The number of points used for ground tracks on the map. More points result in smoother tracks, but require more processing. -* Whether times are displayrf in the local time zone or UTC. +* Which rotators are displayed on the polar chart. This can be All, None or Matching target. When Matching target is selected, the rotator will +only be displayed if the source in the Rotator Controller is set to this Satellite Tracker and Track is enabled. +* The format used for displaying dates. E.g. yyyy/MM/dd +* Whether times are displayed in the local time zone or UTC. * Whether to draw the satellites on the map.

9: Latitude

diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index 7c599294f..817498704 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -28,8 +28,10 @@ #include "device/deviceapi.h" #include "device/deviceset.h" +#include "channel/channelwebapiutils.h" #include "feature/featureset.h" #include "feature/featureuiset.h" +#include "feature/featureutils.h" #include "feature/featurewebapiutils.h" #include "gui/basicfeaturesettingsdialog.h" #include "gui/dialogpositioner.h" @@ -295,6 +297,8 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(1000); + connect(&m_redrawTimer, &QTimer::timeout, this, &SatelliteTrackerGUI::plotChart); + // Intialise charts m_emptyChart.layout()->setContentsMargins(0, 0, 0, 0); m_emptyChart.setMargins(QMargins(1, 1, 1, 1)); @@ -534,6 +538,7 @@ void SatelliteTrackerGUI::on_displaySettings_clicked() m_settingsKeys.append("defaultFrequency"); m_settingsKeys.append("azElUnits"); m_settingsKeys.append("groundTrackPoints"); + m_settingsKeys.append("drawRotators"); m_settingsKeys.append("dateFormat"); m_settingsKeys.append("utc"); m_settingsKeys.append("tles"); @@ -792,6 +797,26 @@ static double interpolate(double x0, double y0, double x1, double y1, double x) return (y0*(x1-x) + y1*(x-x0)) / (x1-x0); } +// Reduce az/el range from 450,180 to 360,90 +void SatelliteTrackerGUI::limitAzElRange(double& azimuth, double& elevation) const +{ + if (elevation > 90.0) + { + elevation = 180.0 - elevation; + if (azimuth < 180.0) { + azimuth += 180.0; + } else { + azimuth -= 180.0; + } + } + if (azimuth > 360.0) { + azimuth -= 360.0f; + } + if (azimuth == 0) { + azimuth = 360.0; + } +} + // Plot pass in polar coords void SatelliteTrackerGUI::plotPolarChart() { @@ -916,6 +941,86 @@ void SatelliteTrackerGUI::plotPolarChart() series[i]->attachAxis(radialAxis); } + int redrawTime = 0; + + if (m_settings.m_drawRotators != SatelliteTrackerSettings::NO_ROTATORS) + { + // Plot rotator position + QString ourSourceName = QString("F0:%1 %2").arg(m_satelliteTracker->getIndexInFeatureSet()).arg(m_satelliteTracker->getIdentifier()); // Only one feature set in practice? + std::vector& featureSets = MainCore::instance()->getFeatureeSets(); + for (int featureSetIndex = 0; featureSetIndex < featureSets.size(); featureSetIndex++) + { + FeatureSet *featureSet = featureSets[featureSetIndex]; + for (int featureIndex = 0; featureIndex < featureSet->getNumberOfFeatures(); featureIndex++) + { + Feature *feature = featureSet->getFeatureAt(featureIndex); + if (FeatureUtils::compareFeatureURIs(feature->getURI(), "sdrangel.feature.gs232controller")) + { + QString source; + ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "source", source); // Will return false if source isn't set in Controller + int track = 0; + ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "track", track); + if ((m_settings.m_drawRotators == SatelliteTrackerSettings::ALL_ROTATORS) || ((source == ourSourceName) && track)) + { + int onTarget = 0; + ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "onTarget", onTarget); + + if (!onTarget) + { + // Target azimuth red dotted line + double targetAzimuth, targetElevation; + bool targetAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetAzimuth", targetAzimuth); + bool targetElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetElevation", targetElevation); + if (targetAzimuthOk && targetElevationOk) + { + limitAzElRange(targetAzimuth, targetElevation); + + QScatterSeries *rotatorSeries = new QScatterSeries(); + QColor color(255, 0, 0, 150); + QPen pen(color); + rotatorSeries->setPen(pen); + rotatorSeries->setColor(color.darker()); + rotatorSeries->setMarkerSize(20); + rotatorSeries->append(targetAzimuth, 90-targetElevation); + m_polarChart->addSeries(rotatorSeries); + rotatorSeries->attachAxis(angularAxis); + rotatorSeries->attachAxis(radialAxis); + + redrawTime = 333; + } + } + + // Current azimuth line. Yellow while off target, green on target. + double currentAzimuth, currentElevation; + bool currentAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentAzimuth", currentAzimuth); + bool currentElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentElevation", currentElevation); + if (currentAzimuthOk && currentElevationOk) + { + limitAzElRange(currentAzimuth, currentElevation); + + QScatterSeries *rotatorSeries = new QScatterSeries(); + QColor color; + if (onTarget) { + color = QColor(0, 255, 0, 150); + } else { + color = QColor(255, 255, 0, 150); + } + rotatorSeries->setPen(QPen(color)); + rotatorSeries->setColor(color.darker()); + rotatorSeries->setMarkerSize(20); + rotatorSeries->append(currentAzimuth, 90-currentElevation); + m_polarChart->addSeries(rotatorSeries); + rotatorSeries->attachAxis(angularAxis); + rotatorSeries->attachAxis(radialAxis); + + redrawTime = 333; + } + } + } + } + } + } + // Create series with single point, so we can plot time of AOS QLineSeries *aosSeries = new QLineSeries(); aosSeries->append(polarSeries->at(0)); @@ -967,7 +1072,8 @@ void SatelliteTrackerGUI::plotPolarChart() if ((currentTime >= pass.m_aos) && (currentTime <= pass.m_los)) { // Create series with single point, so we can plot current time - QLineSeries *nowSeries = new QLineSeries(); + QScatterSeries *nowSeries = new QScatterSeries(); + nowSeries->setMarkerSize(3); // Find closest point to current time int idx = std::round(polarSeries->count() * (currentTime.toMSecsSinceEpoch() - pass.m_aos.toMSecsSinceEpoch()) / (pass.m_los.toMSecsSinceEpoch() - pass.m_aos.toMSecsSinceEpoch())); @@ -978,8 +1084,16 @@ void SatelliteTrackerGUI::plotPolarChart() m_polarChart->addSeries(nowSeries); nowSeries->attachAxis(angularAxis); nowSeries->attachAxis(radialAxis); - // Redraw in 5 seconds (call plotChart, incase user selects a different chart) - QTimer::singleShot(5000, this, &SatelliteTrackerGUI::plotChart); + if (!redrawTime) { + redrawTime = 5000; + } + } + + if (redrawTime > 0) + { + // Redraw to show updated satellite position or rotator position + m_redrawTimer.setSingleShot(true); + m_redrawTimer.start(redrawTime); } delete polarSeries; @@ -1567,3 +1681,4 @@ void SatelliteTrackerGUI::makeUIConnections() QObject::connect(ui->satTable->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &SatelliteTrackerGUI::on_satTableHeader_sortIndicatorChanged); QObject::connect(ui->deviceFeatureSelect, qOverload(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged); } + diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h index b12382582..910226c1a 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.h +++ b/plugins/feature/satellitetracker/satellitetrackergui.h @@ -82,6 +82,7 @@ private: QChart m_emptyChart; QChart *m_lineChart; QPolarChart *m_polarChart; + QTimer m_redrawTimer; QDateTime m_nextTargetAOS; QDateTime m_nextTargetLOS; @@ -138,6 +139,7 @@ private: void updateFileInputList(); void updateMapList(); void makeUIConnections(); + void limitAzElRange(double& azimuth, double& elevation) const; private slots: void onMenuDialogCalled(const QPoint &p); diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.cpp b/plugins/feature/satellitetracker/satellitetrackersettings.cpp index ebf1d6267..648de91e7 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettings.cpp @@ -79,6 +79,7 @@ void SatelliteTrackerSettings::resetToDefaults() m_dateTimeSelect = NOW; m_mapFeature = ""; m_fileInputDevice = ""; + m_drawRotators = MATCHING_TARGET; m_workspaceIndex = 0; m_columnSort = -1; m_columnSortOrder = Qt::AscendingOrder; @@ -142,6 +143,7 @@ QByteArray SatelliteTrackerSettings::serialize() const s.writeBlob(46, m_geometryBytes); s.writeS32(47, m_columnSort); s.writeS32(48, (int)m_columnSortOrder); + s.writeS32(49, (int)m_drawRotators); for (int i = 0; i < SAT_COL_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -237,6 +239,7 @@ bool SatelliteTrackerSettings::deserialize(const QByteArray& data) d.readBlob(46, &m_geometryBytes); d.readS32(47, &m_columnSort, -1); d.readS32(48, (int *)&m_columnSortOrder, (int)Qt::AscendingOrder); + d.readS32(49, (int*)&m_drawRotators, (int)MATCHING_TARGET); for (int i = 0; i < SAT_COL_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); @@ -398,6 +401,15 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co if (settingsKeys.contains("dopplerPeriod")) { m_dopplerPeriod = settings.m_dopplerPeriod; } + if (settingsKeys.contains("predictionPeriod")) { + m_predictionPeriod = settings.m_predictionPeriod; + } + if (settingsKeys.contains("passStartTime")) { + m_passStartTime = settings.m_passStartTime; + } + if (settingsKeys.contains("passFinishTime")) { + m_passFinishTime = settings.m_passFinishTime; + } if (settingsKeys.contains("defaultFrequency")) { m_defaultFrequency = settings.m_defaultFrequency; } @@ -410,23 +422,59 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co if (settingsKeys.contains("aosSpeech")) { m_aosSpeech = settings.m_aosSpeech; } - if (settingsKeys.contains("aosCommand")) { - m_aosCommand = settings.m_aosCommand; - } if (settingsKeys.contains("losSpeech")) { m_losSpeech = settings.m_losSpeech; } + if (settingsKeys.contains("aosCommand")) { + m_aosCommand = settings.m_aosCommand; + } if (settingsKeys.contains("losCommand")) { m_losCommand = settings.m_losCommand; } - if (settingsKeys.contains("predictionPeriod")) { - m_predictionPeriod = settings.m_predictionPeriod; + if (settingsKeys.contains("chartsDarkTheme")) { + m_chartsDarkTheme = settings.m_chartsDarkTheme; } - if (settingsKeys.contains("passStartTime")) { - m_passStartTime = settings.m_passStartTime; + if (settingsKeys.contains("deviceSettings")) { + m_deviceSettings = settings.m_deviceSettings; } - if (settingsKeys.contains("passFinishTime")) { - m_passFinishTime = settings.m_passFinishTime; + if (settingsKeys.contains("replayEnabled")) { + m_replayEnabled = settings.m_replayEnabled; + } + if (settingsKeys.contains("replayStartDateTime")) { + m_replayStartDateTime = settings.m_replayStartDateTime; + } + if (settingsKeys.contains("sendTimeToMap")) { + m_sendTimeToMap = settings.m_sendTimeToMap; + } + if (settingsKeys.contains("dateTimeSelect")) { + m_dateTimeSelect = settings.m_dateTimeSelect; + } + if (settingsKeys.contains("mapFeature")) { + m_mapFeature = settings.m_mapFeature; + } + if (settingsKeys.contains("fileInputDevice")) { + m_fileInputDevice = settings.m_fileInputDevice; + } + if (settingsKeys.contains("drawRotators")) { + m_drawRotators = settings.m_drawRotators; + } + if (settingsKeys.contains("columnSort")) { + m_columnSort = settings.m_columnSort; + } + if (settingsKeys.contains("columnSortOrder")) { + m_columnSortOrder = settings.m_columnSortOrder; + } + if (settingsKeys.contains("columnIndexes")) + { + for (int i = 0; i < SAT_COL_COLUMNS; i++) { + m_columnIndexes[i] = settings.m_columnIndexes[i]; + } + } + if (settingsKeys.contains("columnSizes")) + { + for (int i = 0; i < SAT_COL_COLUMNS; i++) { + m_columnSizes[i] = settings.m_columnSizes[i]; + } } if (settingsKeys.contains("title")) { m_title = settings.m_title; @@ -449,44 +497,8 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co if (settingsKeys.contains("reverseAPIFeatureIndex")) { m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex; } - if (settingsKeys.contains("chartsDarkTheme")) { - m_chartsDarkTheme = settings.m_chartsDarkTheme; - } - if (settingsKeys.contains("replayEnabled")) { - m_replayEnabled = settings.m_replayEnabled; - } - if (settingsKeys.contains("replayStartDateTime")) { - m_replayStartDateTime = settings.m_replayStartDateTime; - } - if (settingsKeys.contains("sendTimeToMap")) { - m_sendTimeToMap = settings.m_sendTimeToMap; - } - if (settingsKeys.contains("dateTimeSelect")) { - m_dateTimeSelect = settings.m_dateTimeSelect; - } - if (settingsKeys.contains("columnSort")) { - m_columnSort = settings.m_columnSort; - } - if (settingsKeys.contains("columnSortOrder")) { - m_columnSortOrder = settings.m_columnSortOrder; - } - - if (settingsKeys.contains("columnIndexes")) - { - for (int i = 0; i < SAT_COL_COLUMNS; i++) { - m_columnIndexes[i] = settings.m_columnIndexes[i]; - } - } - - if (settingsKeys.contains("columnSizes")) - { - for (int i = 0; i < SAT_COL_COLUMNS; i++) { - m_columnSizes[i] = settings.m_columnSizes[i]; - } - } - - if (settingsKeys.contains("deviceSettings")) { - m_deviceSettings = settings.m_deviceSettings; + if (settingsKeys.contains("workspaceIndex")) { + m_workspaceIndex = settings.m_workspaceIndex; } } @@ -503,7 +515,9 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys if (settingsKeys.contains("heightAboveSeaLevel") || force) { ostr << " m_heightAboveSeaLevel: " << m_heightAboveSeaLevel; } - + if (settingsKeys.contains("target") || force) { + ostr << " m_target: " << m_target.toStdString(); + } if (settingsKeys.contains("satellites") || force) { ostr << "m_satellites:"; @@ -512,7 +526,6 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys ostr << " " << satellite.toStdString(); } } - if (settingsKeys.contains("tles") || force) { ostr << " m_tles:"; @@ -521,7 +534,6 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys ostr << " " << tle.toStdString(); } } - if (settingsKeys.contains("dateTime") || force) { ostr << " m_dateTime: " << m_dateTime.toStdString(); } @@ -555,6 +567,15 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys if (settingsKeys.contains("dopplerPeriod") || force) { ostr << " m_dopplerPeriod: " << m_dopplerPeriod; } + if (settingsKeys.contains("predictionPeriod") || force) { + ostr << " m_predictionPeriod: " << m_predictionPeriod; + } + if (settingsKeys.contains("passStartTime") || force) { + ostr << " m_passStartTime: " << m_passStartTime.toString().toStdString(); + } + if (settingsKeys.contains("passFinishTime") || force) { + ostr << " m_passFinishTime: " << m_passFinishTime.toString().toStdString(); + } if (settingsKeys.contains("defaultFrequency") || force) { ostr << " m_defaultFrequency: " << m_defaultFrequency; } @@ -576,14 +597,68 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys if (settingsKeys.contains("losCommand") || force) { ostr << " m_losCommand: " << m_losCommand.toStdString(); } - if (settingsKeys.contains("predictionPeriod") || force) { - ostr << " m_predictionPeriod: " << m_predictionPeriod; + if (settingsKeys.contains("chartsDarkTheme") || force) { + ostr << " m_chartsDarkTheme: " << m_chartsDarkTheme; } - if (settingsKeys.contains("passStartTime") || force) { - ostr << " m_passStartTime: " << m_passStartTime.toString().toStdString(); + if (settingsKeys.contains("deviceSettings")) + { + ostr << "m_deviceSettings: ["; + + for (auto deviceSettingList : m_deviceSettings) + { + ostr << "["; + + for (auto deviceSettings : *deviceSettingList) { + deviceSettings->getDebugString(ostr); + } + + ostr << "]"; + } + + ostr << "]"; } - if (settingsKeys.contains("passFinishTime") || force) { - ostr << " m_passFinishTime: " << m_passFinishTime.toString().toStdString(); + if (settingsKeys.contains("replayEnabled") || force) { + ostr << " m_replayEnabled: " << m_replayEnabled; + } + if (settingsKeys.contains("replayStartDateTime") || force) { + ostr << " m_replayStartDateTime: " << m_replayStartDateTime.toString().toStdString(); + } + if (settingsKeys.contains("sendTimeToMap") || force) { + ostr << " m_sendTimeToMap: " << m_sendTimeToMap; + } + if (settingsKeys.contains("dateTimeSelect") || force) { + ostr << " m_dateTimeSelect: " << m_dateTimeSelect; + } + if (settingsKeys.contains("mapFeature") || force) { + ostr << " m_mapFeature: " << m_mapFeature.toStdString(); + } + if (settingsKeys.contains("fileInputDevice") || force) { + ostr << " m_fileInputDevice: " << m_fileInputDevice.toStdString(); + } + if (settingsKeys.contains("drawRotators") || force) { + ostr << " m_drawRotators: " << m_drawRotators; + } + if (settingsKeys.contains("columnSort") || force) { + ostr << " m_columnSort: " << m_columnSort; + } + if (settingsKeys.contains("columnSortOrder") || force) { + ostr << " m_columnSortOrder: " << m_columnSortOrder; + } + if (settingsKeys.contains("columnIndexes")) + { + ostr << "m_columnIndexes:"; + + for (int i = 0; i < SAT_COL_COLUMNS; i++) { + ostr << " " << m_columnIndexes[i]; + } + } + if (settingsKeys.contains("columnSizes")) + { + ostr << "m_columnSizes:"; + + for (int i = 0; i < SAT_COL_COLUMNS; i++) { + ostr << " " << m_columnSizes[i]; + } } if (settingsKeys.contains("title") || force) { ostr << " m_title: " << m_title.toStdString(); @@ -606,72 +681,9 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys if (settingsKeys.contains("reverseAPIFeatureIndex") || force) { ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex; } - if (settingsKeys.contains("chartsDarkTheme") || force) { - ostr << " m_chartsDarkTheme: " << m_chartsDarkTheme; - } - if (settingsKeys.contains("replayEnabled") || force) { - ostr << " m_replayEnabled: " << m_replayEnabled; - } - if (settingsKeys.contains("replayStartDateTime") || force) { - ostr << " m_replayStartDateTime: " << m_replayStartDateTime.toString().toStdString(); - } - if (settingsKeys.contains("sendTimeToMap") || force) { - ostr << " m_sendTimeToMap: " << m_sendTimeToMap; - } - if (settingsKeys.contains("dateTimeSelect") || force) { - ostr << " m_dateTimeSelect: " << m_dateTimeSelect; - } - if (settingsKeys.contains("mapFeature") || force) { - ostr << " m_mapFeature: " << m_mapFeature.toStdString(); - } - if (settingsKeys.contains("fileInputDevice") || force) { - ostr << " m_fileInputDevice: " << m_fileInputDevice.toStdString(); - } if (settingsKeys.contains("workspaceIndex") || force) { ostr << " m_workspaceIndex: " << m_workspaceIndex; } - if (settingsKeys.contains("columnSort") || force) { - ostr << " m_columnSort: " << m_columnSort; - } - if (settingsKeys.contains("columnSortOrder") || force) { - ostr << " m_columnSortOrder: " << m_columnSortOrder; - } - - if (settingsKeys.contains("columnIndexes")) - { - ostr << "m_columnIndexes:"; - - for (int i = 0; i < SAT_COL_COLUMNS; i++) { - ostr << " " << m_columnIndexes[i]; - } - } - - if (settingsKeys.contains("columnSizes")) - { - ostr << "m_columnSizes:"; - - for (int i = 0; i < SAT_COL_COLUMNS; i++) { - ostr << " " << m_columnSizes[i]; - } - } - - if (settingsKeys.contains("deviceSettings")) - { - ostr << "m_deviceSettings: ["; - - for (auto deviceSettingList : m_deviceSettings) - { - ostr << "["; - - for (auto deviceSettings : *deviceSettingList) { - deviceSettings->getDebugString(ostr); - } - - ostr << "]"; - } - - ostr << "]"; - } return QString(ostr.str().c_str()); } @@ -696,3 +708,4 @@ void SatelliteTrackerSettings::SatelliteDeviceSettings::getDebugString(std::ostr << " m_aosCommand: " << m_aosCommand.toStdString() << " m_losCommand: " << m_losCommand.toStdString(); } + diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.h b/plugins/feature/satellitetracker/satellitetrackersettings.h index 9bd10a54f..92a120b77 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.h +++ b/plugins/feature/satellitetracker/satellitetrackersettings.h @@ -84,6 +84,7 @@ struct SatelliteTrackerSettings enum DateTimeSelect {NOW, CUSTOM, FROM_MAP, FROM_FILE} m_dateTimeSelect; QString m_mapFeature; //!< Which feature when FROM_MAP QString m_fileInputDevice; //!< Which device when FROM_FILE + enum Rotators {ALL_ROTATORS, NO_ROTATORS, MATCHING_TARGET} m_drawRotators; //!< Which rotators to draw on polar chart int m_columnSort; //!< Which column is used for sorting (-1 for none) Qt::SortOrder m_columnSortOrder; diff --git a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp index 7ae16814c..1d36d6733 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp @@ -42,6 +42,7 @@ SatelliteTrackerSettingsDialog::SatelliteTrackerSettingsDialog(SatelliteTrackerS ui->defaultFrequency->setValue(settings->m_defaultFrequency / 1000000.0); ui->azElUnits->setCurrentIndex((int)settings->m_azElUnits); ui->groundTrackPoints->setValue(settings->m_groundTrackPoints); + ui->drawRotators->setCurrentIndex((int)settings->m_drawRotators); ui->dateFormat->setText(settings->m_dateFormat); ui->utc->setChecked(settings->m_utc); ui->drawOnMap->setChecked(settings->m_drawOnMap); @@ -94,6 +95,7 @@ void SatelliteTrackerSettingsDialog::accept() m_settings->m_defaultFrequency = (float)(ui->defaultFrequency->value() * 1000000.0); m_settings->m_azElUnits = (SatelliteTrackerSettings::AzElUnits)ui->azElUnits->currentIndex(); m_settings->m_groundTrackPoints = ui->groundTrackPoints->value(); + m_settings->m_drawRotators = (SatelliteTrackerSettings::Rotators)ui->drawRotators->currentIndex(); m_settings->m_dateFormat = ui->dateFormat->text(); m_settings->m_utc = ui->utc->isChecked(); m_settings->m_drawOnMap = ui->drawOnMap->isChecked(); diff --git a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui index 27f18a45a..9bd312301 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui +++ b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui @@ -339,75 +339,20 @@ Display - - - - Frequency used for Doppler and free space path loss calculations in the satellite table - - - 3 - - - 1.000000000000000 - - - 50000.000000000000000 - - - 100.000000000000000 - - - 100.000000000000000 - - - - - - - Format for dates displayed in the GUI - - - - + - When checked satellite positions are sent to the map + When checked times are dispayed using UTC rather than the local time zone - Draw satellites on map + Display times in UTC - - - - Qt::Vertical - - - - 20 - 40 - - - - - - + + - Update period (s) - - - - - - - Enter the time in seconds between each calculation of the target's position - - - 3600.000000000000000 - - - 1.000000000000000 + Ground track points @@ -418,6 +363,20 @@ + + + + Date format + + + + + + + Format for dates displayed in the GUI + + + @@ -448,6 +407,48 @@ + + + + Frequency used for Doppler and free space path loss calculations in the satellite table + + + 3 + + + 1.000000000000000 + + + 50000.000000000000000 + + + 100.000000000000000 + + + 100.000000000000000 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Update period (s) + + + @@ -455,27 +456,16 @@ - - + + - When checked times are dispayed using UTC rather than the local time zone + Enter the time in seconds between each calculation of the target's position - - Display times in UTC + + 3600.000000000000000 - - - - - - Date format - - - - - - - Ground track points + + 1.000000000000000 @@ -492,6 +482,45 @@ + + + + When checked satellite positions are sent to the map + + + Draw satellites on map + + + + + + + Select which rotators are displayed on the polar chart + + + + All + + + + + None + + + + + Matching target + + + + + + + + Rotators in polar chart + + + @@ -614,8 +643,8 @@ accept() - 248 - 254 + 257 + 533 157 @@ -630,8 +659,8 @@ reject() - 316 - 260 + 325 + 533 286 diff --git a/plugins/feature/satellitetracker/satellitetrackersgp4.cpp b/plugins/feature/satellitetracker/satellitetrackersgp4.cpp index f5d6f3685..c8b1e2442 100644 --- a/plugins/feature/satellitetracker/satellitetrackersgp4.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersgp4.cpp @@ -134,7 +134,7 @@ void getPassAzEl(QLineSeries* azimuth, QLineSeries* elevation, QLineSeries* pola DateTime aosTime = qDateTimeToDateTime(aos); DateTime losTime = qDateTimeToDateTime(los); DateTime currentTime(aosTime); - int steps = 20; + int steps = 150; // Needs to be high enough, so rotator intersect with satellite position double timeStep = (losTime - aosTime).TotalSeconds() / steps; if (timeStep <= 0.0) diff --git a/plugins/feature/startracker/readme.md b/plugins/feature/startracker/readme.md index 1558ee427..fe98fe461 100644 --- a/plugins/feature/startracker/readme.md +++ b/plugins/feature/startracker/readme.md @@ -49,6 +49,8 @@ Pressing this button displays a settings dialog, that allows you to set: * The units to display the solar flux in, either Solar Flux Units, Jansky or Wm^-2Hz-1. 1 sfu equals 10,000 Jansky or 10^-22 Wm^-2Hz-1. * The update period in seconds, which controls how frequently azimuth and elevation are re-calculated. * The IP port number the Stellarium server listens on. +* Which rotators are displayed on the polar chart. This can be All, None or Matching target. When Matching target is selected, the rotator will +only be displayed if the source in the Rotator Controller is set to this Star Tracker and Track is enabled. * Whether to start a Stellarium telescope server. * Whether to draw the Sun in the map. * Whether to draw the Moon on the map. diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index 930b833e3..45a8ecff9 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -33,8 +33,11 @@ #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" @@ -285,6 +288,8 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, 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); @@ -812,7 +817,7 @@ void StarTrackerGUI::on_displaySettings_clicked() ui->galacticLongitude->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits); displaySolarFlux(); - if (ui->chartSelect->currentIndex() == 1) { + if (ui->chartSelect->currentIndex() <= 1) { plotChart(); } } @@ -1651,6 +1656,26 @@ void StarTrackerGUI::plotElevationLineChart() 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() { @@ -1841,6 +1866,92 @@ void StarTrackerGUI::plotElevationPolarChart() series[i]->attachAxis(radialAxis); } + if (m_settings.m_drawRotators != StarTrackerSettings::NO_ROTATORS) + { + int redrawTime = 0; + // Plot rotator position + QString ourSourceName = QString("F0:%1 %2").arg(m_starTracker->getIndexInFeatureSet()).arg(m_starTracker->getIdentifier()); // Only one feature set in practice? + std::vector& featureSets = MainCore::instance()->getFeatureeSets(); + for (int featureSetIndex = 0; featureSetIndex < 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()) { @@ -2242,3 +2353,4 @@ void StarTrackerGUI::makeUIConnections() QObject::connect(ui->drawSun, &QToolButton::clicked, this, &StarTrackerGUI::on_drawSun_clicked); QObject::connect(ui->drawMoon, &QToolButton::clicked, this, &StarTrackerGUI::on_drawMoon_clicked); } + diff --git a/plugins/feature/startracker/startrackergui.h b/plugins/feature/startracker/startrackergui.h index 925232047..03d31762a 100644 --- a/plugins/feature/startracker/startrackergui.h +++ b/plugins/feature/startracker/startrackergui.h @@ -90,6 +90,7 @@ private: QChart *m_azElLineChart; QPolarChart *m_azElPolarChart; + QTimer m_redrawTimer; QChart m_chart; QDateTimeAxis m_chartXAxis; @@ -161,6 +162,7 @@ private: void updateChartSubSelect(); void updateSolarFlux(bool all); void makeUIConnections(); + void limitAzElRange(double& azimuth, double& elevation) const; private slots: void onMenuDialogCalled(const QPoint &p); diff --git a/plugins/feature/startracker/startrackersettings.cpp b/plugins/feature/startracker/startrackersettings.cpp index 9522018a7..9023ccb21 100644 --- a/plugins/feature/startracker/startrackersettings.cpp +++ b/plugins/feature/startracker/startrackersettings.cpp @@ -83,6 +83,7 @@ void StarTrackerSettings::resetToDefaults() m_drawSunOnSkyTempChart = true; m_drawMoonOnSkyTempChart = true; m_workspaceIndex = 0; + m_drawRotators = MATCHING_TARGET; } QByteArray StarTrackerSettings::serialize() const @@ -139,6 +140,7 @@ QByteArray StarTrackerSettings::serialize() const s.writeS32(45, m_workspaceIndex); s.writeBlob(46, m_geometryBytes); + s.writeS32(47, (int)m_drawRotators); return s.final(); } @@ -227,6 +229,7 @@ bool StarTrackerSettings::deserialize(const QByteArray& data) d.readS32(45, &m_workspaceIndex, 0); d.readBlob(46, &m_geometryBytes); + d.readS32(47, (int *)&m_drawRotators, MATCHING_TARGET); return true; } @@ -374,6 +377,9 @@ void StarTrackerSettings::applySettings(const QStringList& settingsKeys, const S if (settingsKeys.contains("workspaceIndex")) { m_workspaceIndex = settings.m_workspaceIndex; } + if (settingsKeys.contains("drawRotators")) { + m_drawRotators = settings.m_drawRotators; + } } QString StarTrackerSettings::getDebugString(const QStringList& settingsKeys, bool force) const @@ -512,6 +518,9 @@ QString StarTrackerSettings::getDebugString(const QStringList& settingsKeys, boo if (settingsKeys.contains("workspaceIndex") || force) { ostr << " m_workspaceIndex: " << m_workspaceIndex; } + if (settingsKeys.contains("drawRotators") || force) { + ostr << " m_drawRotators: " << m_drawRotators; + } return QString(ostr.str().c_str()); } diff --git a/plugins/feature/startracker/startrackersettings.h b/plugins/feature/startracker/startrackersettings.h index f8f2c48a8..8210bda8a 100644 --- a/plugins/feature/startracker/startrackersettings.h +++ b/plugins/feature/startracker/startrackersettings.h @@ -74,6 +74,7 @@ struct StarTrackerSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + enum Rotators {ALL_ROTATORS, NO_ROTATORS, MATCHING_TARGET} m_drawRotators; //!< Which rotators to draw on polar chart StarTrackerSettings(); void resetToDefaults(); diff --git a/plugins/feature/startracker/startrackersettingsdialog.cpp b/plugins/feature/startracker/startrackersettingsdialog.cpp index a85a2f08d..9b5b51e6d 100644 --- a/plugins/feature/startracker/startrackersettingsdialog.cpp +++ b/plugins/feature/startracker/startrackersettingsdialog.cpp @@ -44,8 +44,10 @@ StarTrackerSettingsDialog::StarTrackerSettingsDialog( ui->temperatureLapseRate->setValue(settings->m_temperatureLapseRate); ui->solarFluxData->setCurrentIndex((int)settings->m_solarFluxData); ui->solarFluxUnits->setCurrentIndex((int)settings->m_solarFluxUnits); + ui->drawRotators->setCurrentIndex((int)settings->m_drawRotators); ui->drawSunOnMap->setChecked(settings->m_drawSunOnMap); ui->drawMoonOnMap->setChecked(settings->m_drawMoonOnMap); + ui->drawStarOnMap->setChecked(settings->m_drawStarOnMap); } StarTrackerSettingsDialog::~StarTrackerSettingsDialog() @@ -70,6 +72,7 @@ void StarTrackerSettingsDialog::accept() m_settings->m_temperatureLapseRate = ui->temperatureLapseRate->value(); m_settings->m_solarFluxData = (StarTrackerSettings::SolarFluxData)ui->solarFluxData->currentIndex(); m_settings->m_solarFluxUnits = (StarTrackerSettings::SolarFluxUnits)ui->solarFluxUnits->currentIndex(); + m_settings->m_drawRotators = (StarTrackerSettings::Rotators)ui->drawRotators->currentIndex(); m_settings->m_drawSunOnMap = ui->drawSunOnMap->isChecked(); m_settings->m_drawMoonOnMap = ui->drawMoonOnMap->isChecked(); m_settings->m_drawStarOnMap = ui->drawStarOnMap->isChecked(); @@ -89,6 +92,7 @@ void StarTrackerSettingsDialog::accept() m_settingsKeys.append("temperatureLapseRate"); m_settingsKeys.append("solarFluxData"); m_settingsKeys.append("solarFluxUnits"); + m_settingsKeys.append("drawRotators"); m_settingsKeys.append("drawSunOnMap"); m_settingsKeys.append("drawMoonOnMap"); m_settingsKeys.append("drawStarOnMap"); diff --git a/plugins/feature/startracker/startrackersettingsdialog.ui b/plugins/feature/startracker/startrackersettingsdialog.ui index 38afe4f36..a2d668912 100644 --- a/plugins/feature/startracker/startrackersettingsdialog.ui +++ b/plugins/feature/startracker/startrackersettingsdialog.ui @@ -7,7 +7,7 @@ 0 0 569 - 535 + 556 @@ -23,24 +23,7 @@ - - - - Epoch for custom right ascension and declination - - - - J2000 - - - - - JNOW - - - - - + Enable Stellarium server which allows RA and Dec to be sent to and from Stellarium @@ -50,74 +33,7 @@ - - - - Air pressure (mb) - - - - - - - Relative humidity in % - - - 100 - - - 80 - - - - - - - Temperature lapse rate (K/m) - - - Temperature lapse rate (K/km) - - - - - - - Draw Moon on map - - - - - - - Draw Sun on map - - - - - - - Air temperature (C) - - - - - - - Air pressure in millibars, for use in atmospheric refraction correction - - - 2000.000000000000000 - - - 1.000000000000000 - - - 1010.000000000000000 - - - - + Qt::Vertical @@ -130,52 +46,13 @@ - - + + - Draw target star on map + Draw Sun on map - - - - Weather update period (min) - - - - - - - Solar flux density data - - - - - - - Atmospheric refraction correction - - - 0 - - - - None - - - - - Saemundsson - - - - - Positional Astronomy Library - - - - @@ -206,153 +83,58 @@ - - - - Air temperature in degrees Celsius, for use in atmospheric refraction correction + + + + Air temperature (C) - - -100 + + + + + + Relative humidity in % 100 - 10 + 80 - - - - Solar flux density units - - - - - - - 3 - - - 100.000000000000000 - - - 6.490000000000000 - - - - - - - Refraction correction - - - - - + + - Units to use for the display of the Solar flux density + Atmospheric refraction correction + + + 0 - Solar flux units (sfu) + None - Jansky (Jy) + Saemundsson - Watts per square metre per hertz (W m^-2 Hz-1) + Positional Astronomy Library - - - - Height above sea level (m) - - - - - + + - API key from openweathermap.org to download real-time weather + Temperature lapse rate (K/m) - - - - - Stellarium server port - - - - - - - Epoch for RA & Dec - - - - - - - Azimuth and elevation units - - - - - - - OpenWeatherMap API Key - - - - - - - Stellarium telescope server IP port number - - - 1024 - - - 65535 - - - 10001 - - - - - - - Enter the time in seconds between each calculation of the target's position - - - 1.000000000000000 - - - - - - - Update period (s) - - - - - - - Enter the time in minutes between each weather update - - - 100000 + Temperature lapse rate (K/km) @@ -363,16 +145,68 @@ - - - - Height of observation/antenna location above sea level in metres + + + + Epoch for RA & Dec - - -1000 + + + + + + Weather update period (min) + + + + + + + Air pressure (mb) + + + + + + + Draw target star on map + + + + + + + OpenWeatherMap API Key + + + + + + + Solar flux density units + + + + + + + Air pressure in millibars, for use in atmospheric refraction correction - 20000 + 2000.000000000000000 + + + 1.000000000000000 + + + 1010.000000000000000 + + + + + + + Height above sea level (m) @@ -433,6 +267,201 @@ + + + + 3 + + + 100.000000000000000 + + + 6.490000000000000 + + + + + + + API key from openweathermap.org to download real-time weather + + + + + + + Air temperature in degrees Celsius, for use in atmospheric refraction correction + + + -100 + + + 100 + + + 10 + + + + + + + Draw Moon on map + + + + + + + Update period (s) + + + + + + + Epoch for custom right ascension and declination + + + + J2000 + + + + + JNOW + + + + + + + + Refraction correction + + + + + + + Solar flux density data + + + + + + + Units to use for the display of the Solar flux density + + + + Solar flux units (sfu) + + + + + Jansky (Jy) + + + + + Watts per square metre per hertz (W m^-2 Hz-1) + + + + + + + + Height of observation/antenna location above sea level in metres + + + -1000 + + + 20000 + + + + + + + Enter the time in seconds between each calculation of the target's position + + + 1.000000000000000 + + + + + + + Stellarium telescope server IP port number + + + 1024 + + + 65535 + + + 10001 + + + + + + + Stellarium server port + + + + + + + Enter the time in minutes between each weather update + + + 100000 + + + + + + + Azimuth and elevation units + + + + + + + Rotators in polar chart + + + + + + + Select which rotators are displayed on the polar chart + + + + All + + + + + None + + + + + Matching target + + + +