diff --git a/plugins/feature/satellitetracker/CMakeLists.txt b/plugins/feature/satellitetracker/CMakeLists.txt index 84b2f3ea8..7e630f403 100644 --- a/plugins/feature/satellitetracker/CMakeLists.txt +++ b/plugins/feature/satellitetracker/CMakeLists.txt @@ -81,3 +81,8 @@ if(WIN32) include(DeployQt) windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} ${PROJECT_SOURCE_DIR}/aprs) endif() + +# Install debug symbols +if (WIN32) + install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/feature/satellitetracker/readme.md b/plugins/feature/satellitetracker/readme.md index 35c4ffba3..8b9a48188 100644 --- a/plugins/feature/satellitetracker/readme.md +++ b/plugins/feature/satellitetracker/readme.md @@ -103,6 +103,8 @@ On the Settings tab, you can set: On the TLEs tab, you can provide a list of URL from which satellite Two Line Element files can be downloaded from. TLE files contain the orbital parameters for a satellite and are required in order to be able to calculate a satellites position. +TLEs for may satellites can be obtained from https://www.celestrak.com/NORAD/elements/ +To use a TLE file on a local disk, use a URL such as file:my_tle.txt ![Satellite tracker settings dialog](../../../doc/img/SatelliteTracker_plugin_settingsdialog3.png) diff --git a/plugins/feature/satellitetracker/satellitetracker.cpp b/plugins/feature/satellitetracker/satellitetracker.cpp index 11672beb4..0e8039ccb 100644 --- a/plugins/feature/satellitetracker/satellitetracker.cpp +++ b/plugins/feature/satellitetracker/satellitetracker.cpp @@ -31,6 +31,8 @@ #include "dsp/dspengine.h" #include "util/httpdownloadmanager.h" #include "settings/serializable.h" +#include "channel/channelwebapiutils.h" +#include "feature/featurewebapiutils.h" #include "satellitetrackerworker.h" #include "satellitetracker.h" @@ -76,6 +78,14 @@ void SatelliteTracker::start() { qDebug("SatelliteTracker::start"); + if (m_settings.m_replayEnabled) + { + m_startedDateTime = QDateTime::currentDateTimeUtc(); + if (m_settings.m_sendTimeToMap) { + FeatureWebAPIUtils::mapSetDateTime(currentDateTime()); + } + } + m_worker->reset(); m_worker->setMessageQueueToFeature(getInputMessageQueue()); m_worker->setMessageQueueToGUI(getMessageQueueToGUI()); @@ -1066,18 +1076,40 @@ void SatelliteTracker::updateSatData() qDebug() << "SatelliteTracker::updateSatData: update in progress"; } -// Redirect requests for current time via these methods, so it can be adjusted when testing - +/// Redirect requests for current time via these methods, for replays QDateTime SatelliteTracker::currentDateTimeUtc() { - QDateTime now = QDateTime::currentDateTimeUtc(); - //now = now.addSecs(26*60); - return now; + if (m_settings.m_replayEnabled) + { + if (m_settings.m_useFileInputTime) + { + QString dateTimeStr; + if (ChannelWebAPIUtils::getDeviceReportValue(0, "absoluteTime", dateTimeStr)) + { + return QDateTime::fromString(dateTimeStr, Qt::ISODateWithMs); + } + else + { + return QDateTime::currentDateTimeUtc(); + } + } + else + { + QDateTime now = QDateTime::currentDateTimeUtc(); + return m_settings.m_replayStartDateTime.addSecs(m_startedDateTime.secsTo(now)); + } + } + else + { + return QDateTime::currentDateTimeUtc(); + } } QDateTime SatelliteTracker::currentDateTime() { - QDateTime now = QDateTime::currentDateTime(); - //now = now.addSecs(26*60); - return now; + if (m_settings.m_replayEnabled) { + return currentDateTimeUtc().toLocalTime(); + } else { + return QDateTime::currentDateTime(); + } } diff --git a/plugins/feature/satellitetracker/satellitetracker.h b/plugins/feature/satellitetracker/satellitetracker.h index 04bdaf012..a53d0c53a 100644 --- a/plugins/feature/satellitetracker/satellitetracker.h +++ b/plugins/feature/satellitetracker/satellitetracker.h @@ -162,8 +162,8 @@ public: const QStringList& featureSettingsKeys, SWGSDRangel::SWGFeatureSettings& response); - static QDateTime currentDateTimeUtc(); - static QDateTime currentDateTime(); + QDateTime currentDateTimeUtc(); + QDateTime currentDateTime(); static const char* const m_featureIdURI; static const char* const m_featureId; @@ -186,6 +186,8 @@ private: QHash m_satellitesId; // Same data as m_satellites, but hashed on id, rather than name bool m_firstUpdateSatData; + QDateTime m_startedDateTime; + void start(); void stop(); void applySettings(const SatelliteTrackerSettings& settings, bool force = false); diff --git a/plugins/feature/satellitetracker/satellitetracker.qrc b/plugins/feature/satellitetracker/satellitetracker.qrc index fc9e1498e..debad4dc6 100644 --- a/plugins/feature/satellitetracker/satellitetracker.qrc +++ b/plugins/feature/satellitetracker/satellitetracker.qrc @@ -2,5 +2,6 @@ satellitetracker/iss-32.png satellitetracker/satellite-32.png + satellitetracker/cubesat-32.png diff --git a/plugins/feature/satellitetracker/satellitetracker/cubesat-32.png b/plugins/feature/satellitetracker/satellitetracker/cubesat-32.png new file mode 100644 index 000000000..2fb2209e6 Binary files /dev/null and b/plugins/feature/satellitetracker/satellitetracker/cubesat-32.png differ diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index 0b4603cdc..059e60807 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -268,7 +268,7 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea ui->passChart->setChart(&m_emptyChart); ui->passChart->setRenderHint(QPainter::Antialiasing); - ui->dateTime->setDateTime(SatelliteTracker::currentDateTime()); + ui->dateTime->setDateTime(m_satelliteTracker->currentDateTime()); // Use My Position from preferences, if none set if ((m_settings.m_latitude == 0.0) && (m_settings.m_longitude == 0.0)) { @@ -604,7 +604,7 @@ void SatelliteTrackerGUI::updateTimeToAOS() ui->aos->setText("Now"); else if (m_nextTargetAOS.isValid()) { - QDateTime currentTime = SatelliteTracker::currentDateTime(); + QDateTime currentTime = m_satelliteTracker->currentDateTime(); // FIXME: UTC int secondsToAOS = m_nextTargetAOS.toSecsSinceEpoch() - currentTime.toSecsSinceEpoch(); if (secondsToAOS > 0) { @@ -833,7 +833,7 @@ void SatelliteTrackerGUI::plotPolarChart() QDateTime currentTime; if (m_settings.m_dateTime == "") - currentTime = SatelliteTracker::currentDateTimeUtc(); + currentTime = m_satelliteTracker->currentDateTimeUtc(); else if (m_settings.m_utc) currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs); else @@ -863,7 +863,7 @@ void SatelliteTrackerGUI::plotPolarChart() // Possibly geostationary, just plot current position QDateTime currentTime; if (m_settings.m_dateTime == "") - currentTime = SatelliteTracker::currentDateTimeUtc(); + currentTime = m_satelliteTracker->currentDateTimeUtc(); else if (m_settings.m_utc) currentTime = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs); else @@ -1004,6 +1004,8 @@ void SatelliteTrackerGUI::resizeTable() ui->satTable->setItem(row, SAT_COL_LOS, new QTableWidgetItem("+1 10:17")); ui->satTable->setItem(row, SAT_COL_MAX_EL, new QTableWidgetItem("90")); ui->satTable->setItem(row, SAT_COL_DIR, new QTableWidgetItem("^")); + ui->satTable->setItem(row, SAT_COL_LATITUDE, new QTableWidgetItem("-90.0")); + ui->satTable->setItem(row, SAT_COL_LONGITUDE, new QTableWidgetItem("-180.0")); ui->satTable->setItem(row, SAT_COL_ALT, new QTableWidgetItem("50000")); ui->satTable->setItem(row, SAT_COL_RANGE, new QTableWidgetItem("50000")); ui->satTable->setItem(row, SAT_COL_RANGE_RATE, new QTableWidgetItem("10.0")); @@ -1125,7 +1127,8 @@ void SatelliteTrackerGUI::updateTable(SatelliteState *satState) } // Text alignment - for( int col : {SAT_COL_AZ, SAT_COL_EL, SAT_COL_TNE, SAT_COL_DUR, SAT_COL_MAX_EL, + for (int col : {SAT_COL_AZ, SAT_COL_EL, SAT_COL_TNE, SAT_COL_DUR, SAT_COL_MAX_EL, + SAT_COL_LATITUDE, SAT_COL_LONGITUDE, SAT_COL_ALT, SAT_COL_RANGE, SAT_COL_RANGE_RATE, SAT_COL_DOPPLER, SAT_COL_PATH_LOSS, SAT_COL_DELAY}) items[col]->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -1143,7 +1146,7 @@ void SatelliteTrackerGUI::updateTable(SatelliteState *satState) if (satState->m_passes.size() > 0) { // Get number of days to AOS/LOS - QDateTime currentDateTime = QDateTime::currentDateTime(); + QDateTime currentDateTime = m_satelliteTracker->currentDateTime(); int daysToAOS = currentDateTime.daysTo(satState->m_passes[0]->m_aos); int daysToLOS = currentDateTime.daysTo(satState->m_passes[0]->m_los); if( satState->m_passes[ 0 ]->m_aos > currentDateTime ) @@ -1170,6 +1173,8 @@ void SatelliteTrackerGUI::updateTable(SatelliteState *satState) items[SAT_COL_MAX_EL]->setData(Qt::DisplayRole, QVariant()); items[SAT_COL_DIR]->setText(""); } + items[SAT_COL_LATITUDE]->setData(Qt::DisplayRole, satState->m_latitude); + items[SAT_COL_LONGITUDE]->setData(Qt::DisplayRole, satState->m_longitude); items[SAT_COL_ALT]->setData(Qt::DisplayRole, (int)round(satState->m_altitude)); items[SAT_COL_RANGE]->setData(Qt::DisplayRole, (int)round(satState->m_range)); items[SAT_COL_RANGE_RATE]->setData(Qt::DisplayRole, QString::number(satState->m_rangeRate, 'f', 3)); @@ -1184,6 +1189,7 @@ void SatelliteTrackerGUI::on_satTable_cellDoubleClicked(int row, int column) QString sat = ui->satTable->item(row, SAT_COL_NAME)->text(); FeatureWebAPIUtils::mapFind(sat); + } // Columns in table reordered diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h index 215f3271c..8170886b8 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.h +++ b/plugins/feature/satellitetracker/satellitetrackergui.h @@ -93,6 +93,8 @@ private: SAT_COL_LOS, SAT_COL_MAX_EL, SAT_COL_DIR, + SAT_COL_LATITUDE, + SAT_COL_LONGITUDE, SAT_COL_ALT, SAT_COL_RANGE, SAT_COL_RANGE_RATE, diff --git a/plugins/feature/satellitetracker/satellitetrackergui.ui b/plugins/feature/satellitetracker/satellitetrackergui.ui index 17a923941..5b157c292 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.ui +++ b/plugins/feature/satellitetracker/satellitetrackergui.ui @@ -628,6 +628,16 @@ Direction of the next pass + + + Latitude (°) + + + + + Longitude (°) + + Alt (km) diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.cpp b/plugins/feature/satellitetracker/satellitetrackersettings.cpp index 843437a82..ddc28624a 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettings.cpp @@ -73,7 +73,9 @@ void SatelliteTrackerSettings::resetToDefaults() m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; m_chartsDarkTheme = true; - + m_replayEnabled = false; + m_useFileInputTime = true; + m_sendTimeToMap = true; for (int i = 0; i < SAT_COL_COLUMNS; i++) { m_columnIndexes[i] = i; @@ -121,10 +123,13 @@ QByteArray SatelliteTrackerSettings::serialize() const s.writeU32(34, m_reverseAPIFeatureSetIndex); s.writeU32(35, m_reverseAPIFeatureIndex); s.writeBool(36, m_chartsDarkTheme); - if (m_rollupState) { s.writeBlob(37, m_rollupState->serialize()); } + s.writeBool(38, m_replayEnabled); + s.writeString(39, m_replayStartDateTime.toString(Qt::ISODate)); + s.writeBool(40, m_useFileInputTime); + s.writeBool(41, m_sendTimeToMap); for (int i = 0; i < SAT_COL_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -204,12 +209,16 @@ bool SatelliteTrackerSettings::deserialize(const QByteArray& data) d.readU32(35, &utmp, 0); m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp; d.readBool(36, &m_chartsDarkTheme, true); - if (m_rollupState) { d.readBlob(37, &bytetmp); m_rollupState->deserialize(bytetmp); } + d.readBool(38, &m_replayEnabled, false); + d.readString(39, &strtmp); + m_replayStartDateTime = QDateTime::fromString(strtmp, Qt::ISODate); + d.readBool(40, &m_useFileInputTime, true); + d.readBool(41, &m_sendTimeToMap, true); for (int i = 0; i < SAT_COL_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.h b/plugins/feature/satellitetracker/satellitetrackersettings.h index 2c15a6482..8335c9869 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.h +++ b/plugins/feature/satellitetracker/satellitetrackersettings.h @@ -27,7 +27,7 @@ class Serializable; -#define SAT_COL_COLUMNS 16 +#define SAT_COL_COLUMNS 18 struct SatelliteTrackerSettings { @@ -76,6 +76,10 @@ struct SatelliteTrackerSettings QString m_losCommand; //!< Command/script to execute on LOS bool m_chartsDarkTheme; //!< Set dark theme for charts (effective for GUI only) QHash *> m_deviceSettings; //!< Settings for each device set for each satellite + bool m_replayEnabled; //!< Replay a pass in the past, by setting date and time to m_replayStartDateTime + QDateTime m_replayStartDateTime; //!< Time to start the replay at + bool m_useFileInputTime; //!< Get time from FileInput device + bool m_sendTimeToMap; //!< Send time to map when start pressed int m_columnIndexes[SAT_COL_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[SAT_COL_COLUMNS]; //!< Size of the coumns in the table diff --git a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp index 03744a465..161de1571 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp @@ -51,6 +51,10 @@ SatelliteTrackerSettingsDialog::SatelliteTrackerSettingsDialog(SatelliteTrackerS item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsEnabled); ui->tles->addItem(item); } + ui->replayEnabled->setChecked(settings->m_replayEnabled); + ui->replayDateTime->setDateTime(settings->m_replayStartDateTime); + ui->useFileInputTime->setChecked(settings->m_useFileInputTime); + ui->sendTimeToMap->setChecked(settings->m_sendTimeToMap); } SatelliteTrackerSettingsDialog::~SatelliteTrackerSettingsDialog() @@ -95,7 +99,12 @@ void SatelliteTrackerSettingsDialog::accept() m_settings->m_utc = ui->utc->isChecked(); m_settings->m_drawOnMap = ui->drawOnMap->isChecked(); m_settings->m_tles.clear(); - for (int i = 0; i < ui->tles->count(); i++) + for (int i = 0; i < ui->tles->count(); i++) { m_settings->m_tles.append(ui->tles->item(i)->text()); + } + m_settings->m_replayEnabled = ui->replayEnabled->isChecked(); + m_settings->m_replayStartDateTime = ui->replayDateTime->dateTime(); + m_settings->m_useFileInputTime = ui->useFileInputTime->isChecked(); + m_settings->m_sendTimeToMap = ui->sendTimeToMap->isChecked(); QDialog::accept(); } diff --git a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui index 08d13515b..b031215fd 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui +++ b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui @@ -12,7 +12,6 @@ - Liberation Sans 9 @@ -495,6 +494,91 @@ + + + Replay + + + + + + Enable replay + + + + + + + Enable replay of a pass in the past, by starting with the chosen date and time + + + + + + + + + + Get time from FileInput device + + + + + + + Get the time from the FileInput device + + + + + + + + + + Start date and time + + + + + + + Set date and time to the displayed value when Satellite Tracker's start button is pressed + + + + 2021 + 1 + 1 + + + + dd/MM/yyyy HH:mm:ss + + + true + + + + + + + Send time to map + + + + + + + Send time to Map feature when Satellite Tracker's start button is pressed + + + + + + + + diff --git a/plugins/feature/satellitetracker/satellitetrackersgp4.cpp b/plugins/feature/satellitetracker/satellitetrackersgp4.cpp index c9a37863f..709a4c561 100644 --- a/plugins/feature/satellitetracker/satellitetrackersgp4.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersgp4.cpp @@ -40,7 +40,8 @@ static DateTime qDateTimeToDateTime(QDateTime qdt) QDateTime utc = qdt.toUTC(); QDate date = utc.date(); QTime time = utc.time(); - DateTime dt(date.year(), date.month(), date.day(), time.hour(), time.minute(), time.second()); + DateTime dt; + dt.Initialise(date.year(), date.month(), date.day(), time.hour(), time.minute(), time.second(), time.msec() * 1000); return dt; } @@ -49,7 +50,8 @@ static DateTime qDateTimeToDateTime(QDateTime qdt) void getGroundTrack(QDateTime dateTime, const QString& tle0, const QString& tle1, const QString& tle2, int steps, bool forward, - QList& coordinates) + QList& coordinates, + QList& coordinateDateTimes) { Tle tle = Tle(tle0.toStdString(), tle1.toStdString(), tle2.toStdString()); SGP4 sgp4(tle); @@ -57,7 +59,13 @@ void getGroundTrack(QDateTime dateTime, double periodMins; double timeStep; - // Note map doesn't support paths wrapping around Earth + // For 3D map, we want to quantize to minutes, so we replace previous + // position data, rather than insert additional positions alongside the old + // which can result is the camera view jumping around + dateTime = QDateTime(dateTime.date(), QTime(dateTime.time().hour(), dateTime.time().minute())); + + // Note 2D map doesn't support paths wrapping around Earth several times + // So we just have a slight overlap here, with the future track being longer DateTime currentTime = qDateTimeToDateTime(dateTime); DateTime endTime; if (forward) @@ -73,7 +81,16 @@ void getGroundTrack(QDateTime dateTime, timeStep = -periodMins / (steps * 0.4); } - coordinates.clear(); + // Quantize time step to 30 seconds + timeStep *= 2.0; + if (timeStep > 0.0) { + timeStep = std::max(timeStep, 1.0); + } else if (timeStep < 0.0) { + timeStep = std::min(timeStep, -1.0); + } + timeStep = round(timeStep); + timeStep /= 2.0; + while ((forward && (currentTime < endTime)) || (!forward && (currentTime > endTime))) { // Calculate satellite position @@ -86,7 +103,11 @@ void getGroundTrack(QDateTime dateTime, Units::radiansToDegrees(geo.longitude), geo.altitude * 1000.0); coordinates.append(coord); - // Map is stretched at poles, so use finer steps + + QDateTime *coordDateTime = new QDateTime(dateTimeToQDateTime(currentTime)); + coordinateDateTimes.append(coordDateTime); + + // 2D map is stretched at poles, so use finer steps if (std::abs(Units::radiansToDegrees(geo.latitude)) >= 70) currentTime = currentTime.AddMinutes(timeStep/4); else @@ -485,9 +506,15 @@ void getSatelliteState(QDateTime dateTime, } qDeleteAll(satState->m_groundTrack); + satState->m_groundTrack.clear(); + qDeleteAll(satState->m_groundTrackDateTime); + satState->m_groundTrackDateTime.clear(); qDeleteAll(satState->m_predictedGroundTrack); - getGroundTrack(dateTime, tle0, tle1, tle2, groundTrackSteps, false, satState->m_groundTrack); - getGroundTrack(dateTime, tle0, tle1, tle2, groundTrackSteps, true, satState->m_predictedGroundTrack); + satState->m_predictedGroundTrack.clear(); + qDeleteAll(satState->m_predictedGroundTrackDateTime); + satState->m_predictedGroundTrackDateTime.clear(); + getGroundTrack(dateTime, tle0, tle1, tle2, groundTrackSteps, false, satState->m_groundTrack, satState->m_groundTrackDateTime); + getGroundTrack(dateTime, tle0, tle1, tle2, groundTrackSteps, true, satState->m_predictedGroundTrack, satState->m_predictedGroundTrackDateTime); } catch (SatelliteException& se) { diff --git a/plugins/feature/satellitetracker/satellitetrackersgp4.h b/plugins/feature/satellitetracker/satellitetrackersgp4.h index edd8eb090..26c42dbfc 100644 --- a/plugins/feature/satellitetracker/satellitetrackersgp4.h +++ b/plugins/feature/satellitetracker/satellitetrackersgp4.h @@ -47,7 +47,9 @@ struct SatelliteState { double m_period; QList m_passes; QList m_groundTrack; + QList m_groundTrackDateTime; QList m_predictedGroundTrack; + QList m_predictedGroundTrackDateTime; }; void getGroundTrack(QDateTime dateTime, diff --git a/plugins/feature/satellitetracker/satellitetrackerworker.cpp b/plugins/feature/satellitetracker/satellitetrackerworker.cpp index 8d122b706..fc625c72f 100644 --- a/plugins/feature/satellitetracker/satellitetrackerworker.cpp +++ b/plugins/feature/satellitetracker/satellitetrackerworker.cpp @@ -237,13 +237,17 @@ void SatelliteTrackerWorker::removeFromMap(QString id) MessagePipes& messagePipes = MainCore::instance()->getMessagePipes(); QList *mapMessageQueues = messagePipes.getMessageQueues(m_satelliteTracker, "mapitems"); if (mapMessageQueues) - sendToMap(mapMessageQueues, id, "", "", 0.0, 0.0, 0.0, 0.0, nullptr, nullptr); + sendToMap(mapMessageQueues, id, "", "", "", 0.0f, 0.0, 0.0, 0.0, 0.0, nullptr, nullptr, nullptr, nullptr); } void SatelliteTrackerWorker::sendToMap(QList *mapMessageQueues, - QString name, QString image, QString text, + QString name, QString image, QString model, QString text, + float labelOffset, double lat, double lon, double altitude, double rotation, - QList *track, QList *predictedTrack) + QList *track, + QList *trackDateTime, + QList *predictedTrack, + QList *predictedTrackDateTime) { QList::iterator it = mapMessageQueues->begin(); @@ -257,7 +261,11 @@ void SatelliteTrackerWorker::sendToMap(QList *mapMessageQueues, swgMapItem->setImage(new QString(image)); swgMapItem->setImageRotation(rotation); swgMapItem->setText(new QString(text)); - swgMapItem->setImageMinZoom(0); + swgMapItem->setModel(new QString(model)); + swgMapItem->setFixedPosition(false); + swgMapItem->setOrientation(0); + swgMapItem->setLabel(new QString(name)); + swgMapItem->setLabelAltitudeOffset(labelOffset); if (track != nullptr) { QList *mapTrack = new QList(); @@ -268,6 +276,7 @@ void SatelliteTrackerWorker::sendToMap(QList *mapMessageQueues, p->setLatitude(c->latitude()); p->setLongitude(c->longitude()); p->setAltitude(c->altitude()); + p->setDateTime(new QString(trackDateTime->at(i)->toString(Qt::ISODate))); mapTrack->append(p); } swgMapItem->setTrack(mapTrack); @@ -282,6 +291,7 @@ void SatelliteTrackerWorker::sendToMap(QList *mapMessageQueues, p->setLatitude(c->latitude()); p->setLongitude(c->longitude()); p->setAltitude(c->altitude()); + p->setDateTime(new QString(predictedTrackDateTime->at(i)->toString(Qt::ISODate))); mapTrack->append(p); } swgMapItem->setPredictedTrack(mapTrack); @@ -297,7 +307,7 @@ void SatelliteTrackerWorker::update() // Get date and time to calculate position at QDateTime qdt; if (m_settings.m_dateTime == "") - qdt = SatelliteTracker::currentDateTimeUtc(); + qdt = m_satelliteTracker->currentDateTimeUtc(); else if (m_settings.m_utc) qdt = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs); else @@ -333,6 +343,7 @@ void SatelliteTrackerWorker::update() // Do we have a new AOS? if ((satWorkerState->m_aos != satWorkerState->m_satState.m_passes[0]->m_aos) || (satWorkerState->m_los != satWorkerState->m_satState.m_passes[0]->m_los)) { + qDebug() << "SatelliteTrackerWorker: Current time: " << qdt.toString(Qt::ISODateWithMs); qDebug() << "SatelliteTrackerWorker: New AOS: " << name << " new: " << satWorkerState->m_satState.m_passes[0]->m_aos << " old: " << satWorkerState->m_aos; qDebug() << "SatelliteTrackerWorker: New LOS: " << name << " new: " << satWorkerState->m_satState.m_passes[0]->m_los << " old: " << satWorkerState->m_los; satWorkerState->m_aos = satWorkerState->m_satState.m_passes[0]->m_aos; @@ -356,14 +367,15 @@ void SatelliteTrackerWorker::update() if (satWorkerState->m_losTimer.isActive()) { qDebug() << "SatelliteTrackerWorker::update m_losTimer.remainingTime: " << satWorkerState->m_losTimer.remainingTime(); } - // We can detect a new AOS for a satellite, a little bit before the LOS has occured, presumably - // because the calculations aren't accurate to fractions of a second. Allow for 1s here - if (satWorkerState->m_losTimer.isActive() && (satWorkerState->m_losTimer.remainingTime() <= 1000)) + // We can detect a new AOS for a satellite, a little bit before the LOS has occured + // Allow for 5s here (1s doesn't appear to be enough in some cases) + if (satWorkerState->m_losTimer.isActive() && (satWorkerState->m_losTimer.remainingTime() <= 5000)) { satWorkerState->m_losTimer.stop(); // LOS hasn't been called yet - do so, before we reset timer los(satWorkerState); } + qDebug() << "SatelliteTrackerWorker:: Interval to LOS " << (satWorkerState->m_los.toMSecsSinceEpoch() - qdt.toMSecsSinceEpoch()); satWorkerState->m_losTimer.setInterval(satWorkerState->m_los.toMSecsSinceEpoch() - qdt.toMSecsSinceEpoch()); satWorkerState->m_losTimer.setSingleShot(true); satWorkerState->m_losTimer.start(); @@ -417,11 +429,29 @@ void SatelliteTrackerWorker::update() QList *mapMessageQueues = messagePipes.getMessageQueues(m_satelliteTracker, "mapitems"); if (mapMessageQueues) { + const QStringList cubeSats({"AISAT-1", "FOX-1B", "FOX-1C", "FOX-1D", "FOX-1E", "FUNCUBE-1", "NO-84"}); QString image; + QString model; + float labelOffset; + if (sat->m_name == "ISS") + { image = "qrc:///satellitetracker/satellitetracker/iss-32.png"; + model = "iss.glb"; + labelOffset = 15.0f; + } + else if (cubeSats.contains(sat->m_name)) + { + image = "qrc:///satellitetracker/satellitetracker/cubesat-32.png"; + model = "cubesat.glb"; + labelOffset = 0.7f; + } else + { image = "qrc:///satellitetracker/satellitetracker/satellite-32.png"; + model = "satellite.glb"; + labelOffset = 2.5f; + } QString text = QString("Name: %1\nAltitude: %2 km\nRange: %3 km\nRange rate: %4 km/s\nSpeed: %5 km/h\nPeriod: %6 mins") .arg(sat->m_name) @@ -456,10 +486,11 @@ void SatelliteTrackerWorker::update() .arg(QChar(0xb0)); } - sendToMap(mapMessageQueues, sat->m_name, image, text, + sendToMap(mapMessageQueues, sat->m_name, image, model, text, labelOffset, satWorkerState->m_satState.m_latitude, satWorkerState->m_satState.m_longitude, satWorkerState->m_satState.m_altitude * 1000.0, 0, - &satWorkerState->m_satState.m_groundTrack, &satWorkerState->m_satState.m_predictedGroundTrack); + &satWorkerState->m_satState.m_groundTrack, &satWorkerState->m_satState.m_groundTrackDateTime, + &satWorkerState->m_satState.m_predictedGroundTrack, &satWorkerState->m_satState.m_predictedGroundTrackDateTime); } } @@ -495,7 +526,7 @@ void SatelliteTrackerWorker::aos(SatWorkerState *satWorkerState) SatWorkerState *targetSatWorkerState = m_workerState.value(m_settings.m_target); int currentTargetIdx = m_settings.m_satellites.indexOf(m_settings.m_target); int newTargetIdx = m_settings.m_satellites.indexOf(satWorkerState->m_name); - if ((newTargetIdx < currentTargetIdx) || !targetSatWorkerState->hasAOS()) + if ((newTargetIdx < currentTargetIdx) || !targetSatWorkerState->hasAOS(m_satelliteTracker->currentDateTimeUtc())) { // Stop doppler correction for current target if (m_workerState.contains(m_settings.m_target)) @@ -646,7 +677,12 @@ void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name) // Send AOS message to channels/features SatWorkerState *satWorkerState = m_workerState.value(name); - ChannelWebAPIUtils::satelliteAOS(name, satWorkerState->m_satState.m_passes[0]->m_northToSouth); + SatNogsSatellite *sat = m_satellites.value(satWorkerState->m_name); + // APT needs current time, for current position of satellite, not start of pass which may be in the past + // if the satellite was already visible when Sat Tracker was started + ChannelWebAPIUtils::satelliteAOS(name, satWorkerState->m_satState.m_passes[0]->m_northToSouth, + sat->m_tle->toString(), + m_satelliteTracker->currentDateTimeUtc()); FeatureWebAPIUtils::satelliteAOS(name, satWorkerState->m_aos, satWorkerState->m_los); // Start Doppler correction, if needed @@ -677,6 +713,7 @@ void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name) } if (requiresDoppler) { + qDebug() << "SatelliteTrackerWorker::applyDeviceAOSSettings: requiresDoppler"; satWorkerState->m_dopplerTimer.setInterval(m_settings.m_dopplerPeriod * 1000); satWorkerState->m_dopplerTimer.start(); connect(&satWorkerState->m_dopplerTimer, &QTimer::timeout, [this, satWorkerState]() { @@ -705,7 +742,10 @@ void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name) { // Send AOS message to channels/features SatWorkerState *satWorkerState = m_workerState.value(name); - ChannelWebAPIUtils::satelliteAOS(name, satWorkerState->m_satState.m_passes[0]->m_northToSouth); + SatNogsSatellite *sat = m_satellites.value(satWorkerState->m_name); + ChannelWebAPIUtils::satelliteAOS(name, satWorkerState->m_satState.m_passes[0]->m_northToSouth, + sat->m_tle->toString(), + m_satelliteTracker->currentDateTimeUtc()); FeatureWebAPIUtils::satelliteAOS(name, satWorkerState->m_aos, satWorkerState->m_los); } @@ -750,7 +790,7 @@ void SatelliteTrackerWorker::doppler(SatWorkerState *satWorkerState) void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState) { - qDebug() << "SatelliteTrackerWorker::los " << satWorkerState->m_name; + qDebug() << "SatelliteTrackerWorker::los " << satWorkerState->m_name << " target: " << m_settings.m_target; // Indicate LOS to GUI if (getMessageQueueToGUI()) @@ -776,6 +816,10 @@ void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState) QProcess::startDetached(program, allArgs); } + // Send LOS message to channels/features + ChannelWebAPIUtils::satelliteLOS(satWorkerState->m_name); + FeatureWebAPIUtils::satelliteLOS(satWorkerState->m_name); + if (m_settings.m_deviceSettings.contains(satWorkerState->m_name)) { QList *m_deviceSettingsList = m_settings.m_deviceSettings.value(satWorkerState->m_name); @@ -791,10 +835,6 @@ void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState) } } - // Send LOS message to channels/features - ChannelWebAPIUtils::satelliteLOS(satWorkerState->m_name); - FeatureWebAPIUtils::satelliteLOS(satWorkerState->m_name); - // Stop acquisition for (int i = 0; i < m_deviceSettingsList->size(); i++) { @@ -834,7 +874,7 @@ void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState) if (m_workerState.contains(m_settings.m_satellites[i])) { SatWorkerState *newSatWorkerState = m_workerState.value(m_settings.m_satellites[i]); - if (newSatWorkerState->hasAOS()) + if (newSatWorkerState->hasAOS(m_satelliteTracker->currentDateTimeUtc())) { qDebug() << "SatelliteTrackerWorker::los - autoTarget setting " << m_settings.m_satellites[i]; m_settings.m_target = m_settings.m_satellites[i]; @@ -852,8 +892,7 @@ void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState) m_recalculatePasses = true; } -bool SatWorkerState::hasAOS() +bool SatWorkerState::hasAOS(const QDateTime& currentTime) { - QDateTime currentTime = SatelliteTracker::currentDateTimeUtc(); return (m_aos <= currentTime) && (m_los > currentTime); } diff --git a/plugins/feature/satellitetracker/satellitetrackerworker.h b/plugins/feature/satellitetracker/satellitetrackerworker.h index 8989f5e61..7f043ecc4 100644 --- a/plugins/feature/satellitetracker/satellitetrackerworker.h +++ b/plugins/feature/satellitetracker/satellitetrackerworker.h @@ -48,7 +48,7 @@ public: m_satState.m_name = name; } - bool hasAOS(); + bool hasAOS(const QDateTime& currentTime); protected: QString m_name; // Name of the satellite @@ -122,9 +122,13 @@ private: void applySettings(const SatelliteTrackerSettings& settings, bool force = false); MessageQueue *getMessageQueueToGUI() { return m_msgQueueToGUI; } void removeFromMap(QString id); - void sendToMap(QList *mapMessageQueues, QString id, QString image, QString text, + void sendToMap(QList *mapMessageQueues, QString id, QString image, QString model, QString text, + float labelOffset, double lat, double lon, double altitude, double rotation, - QList *track = nullptr, QList *predictedTrack = nullptr); + QList *track = nullptr, + QList *trackDateTime = nullptr, + QList *predictedTrack = nullptr, + QList *predictedTrackDateTime = nullptr); void applyDeviceAOSSettings(const QString& name); void startStopSinks(bool start); void calculateRotation(SatWorkerState *satWorkerState); diff --git a/plugins/feature/satellitetracker/satnogs.h b/plugins/feature/satellitetracker/satnogs.h index 3f5baae04..7d1cfc525 100644 --- a/plugins/feature/satellitetracker/satnogs.h +++ b/plugins/feature/satellitetracker/satnogs.h @@ -115,6 +115,11 @@ struct SatNogsTLE { m_tle2 = tle2; } + QString toString() const + { + return m_tle0 + "\n" + m_tle1 + "\n" + m_tle2 + "\n"; + } + static QList createList(QJsonArray array) { QList list;