mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-10-08 08:10:35 -04:00
MessageQueue::pop() transfers ownership of the popped message to the caller, but many handleInputMessages() implementations only deleted the message when handleMessage() returned true. When a message was not handled, ownership had already been transferred from the queue and the message was leaked. This was identified while investigating an ASAN leak in the audio subsystem. Update the applicable handleInputMessages() implementations to always delete messages after handleMessage() returns. Unhandled messages are also logged with the component and message identifier so unexpected message routing is visible during debugging. This change: - Ensures every message popped from a queue has its ownership resolved. - Prevents leaks when handleMessage() returns false. - Makes unhandled messages visible instead of silently discarding them. - Preserves the existing message handling logic and does not require components to handle message types that are legitimately irrelevant to them. The changes are intentionally mechanical and limited to the common message queue ownership and diagnostic pattern. Signed-off-by: Robin Getz <rgetz503@gmail.com>
1054 lines
48 KiB
C++
1054 lines
48 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
|
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
|
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
|
|
// Copyright (C) 2023 Daniele Forsi <iu5hkx@gmail.com> //
|
|
// //
|
|
// This program is free software; you can redistribute it and/or modify //
|
|
// it under the terms of the GNU General Public License as published by //
|
|
// the Free Software Foundation as version 3 of the License, or //
|
|
// (at your option) any later version. //
|
|
// //
|
|
// This program is distributed in the hope that it will be useful, //
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
// GNU General Public License V3 for more details. //
|
|
// //
|
|
// You should have received a copy of the GNU General Public License //
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <cmath>
|
|
|
|
#include <QDebug>
|
|
#include <QAbstractSocket>
|
|
#include <QTcpServer>
|
|
#include <QTcpSocket>
|
|
#include <QEventLoop>
|
|
#include <QTimer>
|
|
#include <QDateTime>
|
|
|
|
#include "SWGTargetAzimuthElevation.h"
|
|
#include "SWGMapItem.h"
|
|
|
|
#include "webapi/webapiadapterinterface.h"
|
|
|
|
#include "util/units.h"
|
|
#include "device/deviceset.h"
|
|
#include "device/deviceapi.h"
|
|
#include "channel/channelapi.h"
|
|
#include "channel/channelwebapiutils.h"
|
|
#include "feature/featurewebapiutils.h"
|
|
#include "maincore.h"
|
|
|
|
#include "satellitetracker.h"
|
|
#include "satellitetrackerworker.h"
|
|
#include "satellitetrackerreport.h"
|
|
#include "satellitetrackersgp4.h"
|
|
|
|
MESSAGE_CLASS_DEFINITION(SatelliteTrackerWorker::MsgConfigureSatelliteTrackerWorker, Message)
|
|
MESSAGE_CLASS_DEFINITION(SatelliteTrackerReport::MsgReportSat, Message)
|
|
MESSAGE_CLASS_DEFINITION(SatelliteTrackerReport::MsgReportAOS, Message)
|
|
MESSAGE_CLASS_DEFINITION(SatelliteTrackerReport::MsgReportLOS, Message)
|
|
MESSAGE_CLASS_DEFINITION(SatelliteTrackerReport::MsgReportTarget, Message)
|
|
|
|
SatelliteTrackerWorker::SatelliteTrackerWorker(SatelliteTracker* satelliteTracker, WebAPIAdapterInterface *webAPIAdapterInterface) :
|
|
m_satelliteTracker(satelliteTracker),
|
|
m_webAPIAdapterInterface(webAPIAdapterInterface),
|
|
m_msgQueueToFeature(nullptr),
|
|
m_msgQueueToGUI(nullptr),
|
|
m_pollTimer(this),
|
|
m_recalculatePasses(true),
|
|
m_flipRotation(false),
|
|
m_extendedAzRotation(false)
|
|
{
|
|
connect(&m_pollTimer, SIGNAL(timeout()), this, SLOT(update()));
|
|
}
|
|
|
|
SatelliteTrackerWorker::~SatelliteTrackerWorker()
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::~SatelliteTrackerWorker";
|
|
stopWork();
|
|
m_inputMessageQueue.clear();
|
|
// Remove satellites from Map
|
|
QHashIterator<QString, SatWorkerState *> itr(m_workerState);
|
|
while (itr.hasNext())
|
|
{
|
|
itr.next();
|
|
if (m_settings.m_drawOnMap) {
|
|
removeFromMap(itr.key());
|
|
}
|
|
}
|
|
qDeleteAll(m_workerState);
|
|
}
|
|
|
|
void SatelliteTrackerWorker::startWork()
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::startWork";
|
|
QMutexLocker mutexLocker(&m_mutex);
|
|
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
|
m_recalculatePasses = true;
|
|
|
|
m_pollTimer.start((int)round(m_settings.m_updatePeriod*1000.0));
|
|
// Resume doppler timers
|
|
QHashIterator<QString, SatWorkerState *> itr(m_workerState);
|
|
while (itr.hasNext())
|
|
{
|
|
itr.next();
|
|
SatWorkerState *satWorkerState = itr.value();
|
|
if (satWorkerState->m_dopplerTimer.interval() > 0)
|
|
satWorkerState->m_dopplerTimer.start();
|
|
}
|
|
|
|
// Handle any messages already on the queue
|
|
handleInputMessages();
|
|
}
|
|
|
|
void SatelliteTrackerWorker::stopWork()
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::stopWork";
|
|
QMutexLocker mutexLocker(&m_mutex);
|
|
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
|
m_pollTimer.stop();
|
|
// Stop doppler timers
|
|
QHashIterator<QString, SatWorkerState *> itr(m_workerState);
|
|
while (itr.hasNext())
|
|
{
|
|
itr.next();
|
|
itr.value()->m_dopplerTimer.stop();
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::handleInputMessages()
|
|
{
|
|
Message* message;
|
|
|
|
while ((message = m_inputMessageQueue.pop()))
|
|
{
|
|
if (!handleMessage(*message)) {
|
|
qDebug("%s: unhandled message: %s", Q_FUNC_INFO, message->getIdentifier());
|
|
}
|
|
delete message;
|
|
}
|
|
}
|
|
|
|
bool SatelliteTrackerWorker::handleMessage(const Message& message)
|
|
{
|
|
if (MsgConfigureSatelliteTrackerWorker::match(message))
|
|
{
|
|
QMutexLocker mutexLocker(&m_mutex);
|
|
MsgConfigureSatelliteTrackerWorker& cfg = (MsgConfigureSatelliteTrackerWorker&) message;
|
|
|
|
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
|
|
return true;
|
|
}
|
|
else if (SatelliteTracker::MsgSatData::match(message))
|
|
{
|
|
SatelliteTracker::MsgSatData& satData = (SatelliteTracker::MsgSatData&) message;
|
|
m_satellites = satData.getSatellites();
|
|
m_recalculatePasses = true;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::applySettings(const SatelliteTrackerSettings& settings, const QList<QString>& settingsKeys, bool force)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
|
|
|
|
if (settingsKeys.contains("target")
|
|
|| settingsKeys.contains("latitude")
|
|
|| settingsKeys.contains("longitude")
|
|
|| settingsKeys.contains("heightAboveSeaLevel")
|
|
|| settingsKeys.contains("dateTime")
|
|
|| settingsKeys.contains("utc")
|
|
|| settingsKeys.contains("groundTrackPoints")
|
|
|| settingsKeys.contains("minAOSElevation")
|
|
|| settingsKeys.contains("minPassElevation")
|
|
|| settingsKeys.contains("predictionPeriod")
|
|
|| settingsKeys.contains("passStartTime")
|
|
|| settingsKeys.contains("passFinishTime")
|
|
|| (!m_settings.m_drawOnMap && settings.m_drawOnMap)
|
|
|| force)
|
|
{
|
|
// Recalculate immediately
|
|
m_recalculatePasses = true;
|
|
QTimer::singleShot(1, this, &SatelliteTrackerWorker::update);
|
|
m_pollTimer.start((int)round(settings.m_updatePeriod*1000.0));
|
|
}
|
|
else if (settingsKeys.contains("updatePeriod") || force)
|
|
{
|
|
m_pollTimer.start((int)round(settings.m_updatePeriod*1000.0));
|
|
}
|
|
|
|
if (!settings.m_drawOnMap && m_settings.m_drawOnMap)
|
|
{
|
|
QHashIterator<QString, SatWorkerState *> itr(m_workerState);
|
|
while (itr.hasNext())
|
|
{
|
|
itr.next();
|
|
removeFromMap(itr.key());
|
|
}
|
|
}
|
|
|
|
// Remove satellites no longer needed
|
|
QMutableHashIterator<QString, SatWorkerState *> itr(m_workerState);
|
|
while (itr.hasNext())
|
|
{
|
|
itr.next();
|
|
if (settings.m_satellites.indexOf(itr.key()) == -1)
|
|
{
|
|
if (m_settings.m_drawOnMap) {
|
|
removeFromMap(itr.key());
|
|
}
|
|
delete itr.value();
|
|
itr.remove();
|
|
}
|
|
}
|
|
|
|
// Add new satellites
|
|
for (int i = 0; i < settings.m_satellites.size(); i++)
|
|
{
|
|
if (!m_workerState.contains(settings.m_satellites[i]))
|
|
{
|
|
SatWorkerState *satWorkerState = new SatWorkerState(settings.m_satellites[i]);
|
|
m_workerState.insert(settings.m_satellites[i], satWorkerState);
|
|
connect(&satWorkerState->m_aosTimer, &QTimer::timeout, [this, satWorkerState]() {
|
|
aos(satWorkerState);
|
|
});
|
|
connect(&satWorkerState->m_losTimer, &QTimer::timeout, [this, satWorkerState]() {
|
|
los(satWorkerState);
|
|
});
|
|
m_recalculatePasses = true;
|
|
}
|
|
}
|
|
|
|
if (settingsKeys.contains("target") && (settings.m_target != m_settings.m_target))
|
|
{
|
|
if (m_workerState.contains(m_settings.m_target))
|
|
{
|
|
SatWorkerState *satWorkerState = m_workerState.value(m_settings.m_target);
|
|
disableDoppler(satWorkerState);
|
|
}
|
|
if (m_workerState.contains(settings.m_target))
|
|
{
|
|
SatWorkerState *satWorkerState = m_workerState.value(settings.m_target);
|
|
if (satWorkerState->hasAOS(m_satelliteTracker->currentDateTimeUtc())) {
|
|
enableDoppler(satWorkerState);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (force) {
|
|
m_settings = settings;
|
|
} else {
|
|
m_settings.applySettings(settingsKeys, settings);
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::removeFromMap(QString id)
|
|
{
|
|
QList<ObjectPipe*> mapMessagePipes;
|
|
MainCore::instance()->getMessagePipes().getMessagePipes(m_satelliteTracker, "mapitems", mapMessagePipes);
|
|
|
|
if (mapMessagePipes.size() > 0) {
|
|
sendToMap(mapMessagePipes, id, "", "", "", 0.0f, 0.0, 0.0, 0.0, 0.0, nullptr, nullptr, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::sendToMap(
|
|
const QList<ObjectPipe*>& mapMessagePipes,
|
|
QString name,
|
|
QString image,
|
|
QString model,
|
|
QString text,
|
|
float labelOffset,
|
|
double lat,
|
|
double lon,
|
|
double altitude,
|
|
double rotation,
|
|
QList<QGeoCoordinate *> *track,
|
|
QList<QDateTime *> *trackDateTime,
|
|
QList<QGeoCoordinate *> *predictedTrack,
|
|
QList<QDateTime *> *predictedTrackDateTime
|
|
)
|
|
{
|
|
for (const auto& pipe : mapMessagePipes)
|
|
{
|
|
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
|
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
|
|
swgMapItem->setName(new QString(name));
|
|
swgMapItem->setLatitude(lat);
|
|
swgMapItem->setLongitude(lon);
|
|
swgMapItem->setAltitude(altitude);
|
|
swgMapItem->setImage(new QString(image));
|
|
swgMapItem->setImageRotation(rotation);
|
|
swgMapItem->setText(new QString(text));
|
|
swgMapItem->setModel(new QString(model));
|
|
swgMapItem->setFixedPosition(false);
|
|
swgMapItem->setOrientation(0);
|
|
swgMapItem->setLabel(new QString(name));
|
|
swgMapItem->setLabelAltitudeOffset(labelOffset);
|
|
if (track != nullptr)
|
|
{
|
|
QList<SWGSDRangel::SWGMapCoordinate *> *mapTrack = new QList<SWGSDRangel::SWGMapCoordinate *>();
|
|
for (int i = 0; i < track->size(); i++)
|
|
{
|
|
SWGSDRangel::SWGMapCoordinate* p = new SWGSDRangel::SWGMapCoordinate();
|
|
QGeoCoordinate *c = track->at(i);
|
|
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);
|
|
}
|
|
if (predictedTrack != nullptr)
|
|
{
|
|
QList<SWGSDRangel::SWGMapCoordinate *> *mapTrack = new QList<SWGSDRangel::SWGMapCoordinate *>();
|
|
for (int i = 0; i < predictedTrack->size(); i++)
|
|
{
|
|
SWGSDRangel::SWGMapCoordinate* p = new SWGSDRangel::SWGMapCoordinate();
|
|
QGeoCoordinate *c = predictedTrack->at(i);
|
|
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);
|
|
}
|
|
|
|
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_satelliteTracker, swgMapItem);
|
|
messageQueue->push(msg);
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::update()
|
|
{
|
|
// Get date and time to calculate position at
|
|
QDateTime qdt;
|
|
if (m_settings.m_dateTime == "")
|
|
qdt = m_satelliteTracker->currentDateTimeUtc();
|
|
else if (m_settings.m_utc)
|
|
qdt = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs);
|
|
else
|
|
qdt = QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs).toUTC();
|
|
|
|
bool timeReversed = m_lastUpdateDateTime > qdt;
|
|
|
|
QHashIterator<QString, SatWorkerState *> itr(m_workerState);
|
|
while (itr.hasNext())
|
|
{
|
|
itr.next();
|
|
SatWorkerState *satWorkerState = itr.value();
|
|
QString name = satWorkerState->m_name;
|
|
if (m_satellites.contains(name))
|
|
{
|
|
SatNogsSatellite *sat = m_satellites.value(name);
|
|
if (sat->m_tle != nullptr)
|
|
{
|
|
// Calculate position, AOS/LOS and other details for satellite
|
|
int noOfPasses;
|
|
bool recalcAsPastLOS = (satWorkerState->m_satState.m_passes.size() > 0) && (satWorkerState->m_satState.m_passes[0].m_los < qdt);
|
|
if (m_recalculatePasses || recalcAsPastLOS || timeReversed)
|
|
noOfPasses = (name == m_settings.m_target) ? 99 : 1;
|
|
else
|
|
noOfPasses = 0;
|
|
getSatelliteState(qdt, sat->m_tle->m_tle0, sat->m_tle->m_tle1, sat->m_tle->m_tle2,
|
|
m_settings.m_latitude, m_settings.m_longitude, m_settings.m_heightAboveSeaLevel/1000.0,
|
|
m_settings.m_predictionPeriod, m_settings.m_minAOSElevation, m_settings.m_minPassElevation,
|
|
m_settings.m_passStartTime, m_settings.m_passFinishTime, m_settings.m_utc,
|
|
noOfPasses, m_settings.m_groundTrackPoints, &satWorkerState->m_satState);
|
|
|
|
// Update AOS/LOS
|
|
if (satWorkerState->m_satState.m_passes.size() > 0)
|
|
{
|
|
// Only use timers if using real time
|
|
if (m_settings.m_dateTimeSelect == SatelliteTrackerSettings::NOW)
|
|
{
|
|
// Do we have a new pass?
|
|
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;
|
|
satWorkerState->m_los = satWorkerState->m_satState.m_passes[0].m_los;
|
|
satWorkerState->m_hasSignalledAOS = false;
|
|
if (satWorkerState->m_aos.isValid())
|
|
{
|
|
if (satWorkerState->m_aos > qdt)
|
|
{
|
|
satWorkerState->m_aosTimer.setInterval(satWorkerState->m_aos.toMSecsSinceEpoch() - qdt.toMSecsSinceEpoch());
|
|
satWorkerState->m_aosTimer.setSingleShot(true);
|
|
satWorkerState->m_aosTimer.start();
|
|
}
|
|
else if (qdt < satWorkerState->m_los)
|
|
aos(satWorkerState);
|
|
|
|
if (satWorkerState->m_los.isValid() && (m_settings.m_target == satWorkerState->m_name))
|
|
calculateRotation(satWorkerState);
|
|
}
|
|
if (satWorkerState->m_los.isValid() && (satWorkerState->m_los > qdt))
|
|
{
|
|
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 occurred
|
|
// 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();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Do we need to signal LOS?
|
|
if (satWorkerState->m_hasSignalledAOS && !satWorkerState->hasAOS(qdt))
|
|
{
|
|
los(satWorkerState);
|
|
satWorkerState->m_hasSignalledAOS = false;
|
|
}
|
|
// Do we have a new pass?
|
|
if ((satWorkerState->m_aos != satWorkerState->m_satState.m_passes[0].m_aos) || (satWorkerState->m_los != satWorkerState->m_satState.m_passes[0].m_los))
|
|
{
|
|
satWorkerState->m_aos = satWorkerState->m_satState.m_passes[0].m_aos;
|
|
satWorkerState->m_los = satWorkerState->m_satState.m_passes[0].m_los;
|
|
satWorkerState->m_hasSignalledAOS = false;
|
|
}
|
|
// Check if we need to signal AOS
|
|
if (!satWorkerState->m_hasSignalledAOS && satWorkerState->m_aos.isValid() && satWorkerState->hasAOS(qdt)) {
|
|
aos(satWorkerState);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
satWorkerState->m_aos = QDateTime();
|
|
satWorkerState->m_los = QDateTime();
|
|
satWorkerState->m_aosTimer.stop();
|
|
satWorkerState->m_losTimer.stop();
|
|
}
|
|
|
|
// Send Az/El of target to Rotator Controllers, if elevation above horizon
|
|
if ((name == m_settings.m_target) && (satWorkerState->m_satState.m_elevation >= 0))
|
|
{
|
|
double azimuth = satWorkerState->m_satState.m_azimuth + m_settings.m_azimuthOffset;
|
|
double elevation = satWorkerState->m_satState.m_elevation + m_settings.m_elevationOffset;
|
|
if (m_extendedAzRotation)
|
|
{
|
|
if (azimuth < 180.0)
|
|
azimuth += 360.0;
|
|
}
|
|
else if (m_flipRotation)
|
|
{
|
|
azimuth = std::fmod(azimuth + 180.0, 360.0);
|
|
elevation = 180.0 - elevation;
|
|
}
|
|
|
|
QList<ObjectPipe*> rotatorPipes;
|
|
MainCore::instance()->getMessagePipes().getMessagePipes(m_satelliteTracker, "target", rotatorPipes);
|
|
|
|
for (const auto& pipe : rotatorPipes)
|
|
{
|
|
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
|
SWGSDRangel::SWGTargetAzimuthElevation *swgTarget = new SWGSDRangel::SWGTargetAzimuthElevation();
|
|
swgTarget->setName(new QString(m_settings.m_target));
|
|
swgTarget->setAzimuth(azimuth);
|
|
swgTarget->setElevation(elevation);
|
|
messageQueue->push(MainCore::MsgTargetAzimuthElevation::create(m_satelliteTracker, swgTarget));
|
|
}
|
|
}
|
|
|
|
// Send to Map
|
|
if (m_settings.m_drawOnMap)
|
|
{
|
|
QList<ObjectPipe*> mapMessagePipes;
|
|
MainCore::instance()->getMessagePipes().getMessagePipes(m_satelliteTracker, "mapitems", mapMessagePipes);
|
|
|
|
if (mapMessagePipes.size() > 0)
|
|
{
|
|
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)
|
|
.arg((int)round(satWorkerState->m_satState.m_altitude))
|
|
.arg((int)round(satWorkerState->m_satState.m_range))
|
|
.arg(satWorkerState->m_satState.m_rangeRate, 0, 'f', 1)
|
|
.arg(Units::kmpsToIntegerKPH(satWorkerState->m_satState.m_speed))
|
|
.arg((int)round(satWorkerState->m_satState.m_period));
|
|
if (satWorkerState->m_satState.m_passes.size() > 0)
|
|
{
|
|
if ((qdt >= satWorkerState->m_satState.m_passes[0].m_aos) && (qdt <= satWorkerState->m_satState.m_passes[0].m_los))
|
|
text = text.append("\nSatellite is visible");
|
|
else
|
|
text = text.append("\nAOS in: %1 mins").arg((int)round((satWorkerState->m_satState.m_passes[0].m_aos.toSecsSinceEpoch() - qdt.toSecsSinceEpoch())/60.0));
|
|
QString aosDateTime;
|
|
QString losDateTime;
|
|
if (m_settings.m_utc)
|
|
{
|
|
aosDateTime = satWorkerState->m_satState.m_passes[0].m_aos.toString(m_settings.m_dateFormat + " hh:mm");
|
|
losDateTime = satWorkerState->m_satState.m_passes[0].m_los.toString(m_settings.m_dateFormat + " hh:mm");
|
|
}
|
|
else
|
|
{
|
|
aosDateTime = satWorkerState->m_satState.m_passes[0].m_aos.toLocalTime().toString(m_settings.m_dateFormat + " hh:mm");
|
|
losDateTime = satWorkerState->m_satState.m_passes[0].m_los.toLocalTime().toString(m_settings.m_dateFormat + " hh:mm");
|
|
}
|
|
text = QString("%1\nAOS: %2\nLOS: %3\nMax El: %4%5")
|
|
.arg(text)
|
|
.arg(aosDateTime)
|
|
.arg(losDateTime)
|
|
.arg((int)round(satWorkerState->m_satState.m_passes[0].m_maxElevation))
|
|
.arg(QChar(0xb0));
|
|
}
|
|
|
|
sendToMap(
|
|
mapMessagePipes,
|
|
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_groundTrackDateTime,
|
|
&satWorkerState->m_satState.m_predictedGroundTrack, &satWorkerState->m_satState.m_predictedGroundTrackDateTime
|
|
);
|
|
}
|
|
}
|
|
|
|
// Send to GUI
|
|
if (getMessageQueueToGUI())
|
|
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportSat::create(new SatelliteState(satWorkerState->m_satState)));
|
|
|
|
// Sent to Feature for Web report
|
|
if (m_msgQueueToFeature)
|
|
m_msgQueueToFeature->push(SatelliteTrackerReport::MsgReportSat::create(new SatelliteState(satWorkerState->m_satState)));
|
|
}
|
|
else
|
|
qDebug() << "SatelliteTrackerWorker::update: No TLE for " << sat->m_name << ". Can't compute position.";
|
|
}
|
|
}
|
|
m_lastUpdateDateTime = qdt;
|
|
m_recalculatePasses = false;
|
|
}
|
|
|
|
void SatelliteTrackerWorker::aos(SatWorkerState *satWorkerState)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::aos " << satWorkerState->m_name;
|
|
|
|
satWorkerState->m_hasSignalledAOS = true;
|
|
|
|
// Indicate AOS to GUI
|
|
if (getMessageQueueToGUI())
|
|
{
|
|
QString speech = substituteVariables(m_settings.m_aosSpeech, satWorkerState->m_name);
|
|
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportAOS::create(satWorkerState->m_name, speech));
|
|
}
|
|
|
|
// Update target
|
|
if (m_settings.m_autoTarget && (satWorkerState->m_name != m_settings.m_target))
|
|
{
|
|
// Only switch if higher priority (earlier in list) or other target not in AOS
|
|
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(m_satelliteTracker->currentDateTimeUtc()))
|
|
{
|
|
// Stop doppler correction for current target
|
|
if (m_workerState.contains(m_settings.m_target))
|
|
disableDoppler(m_workerState.value(m_settings.m_target));
|
|
|
|
qDebug() << "SatelliteTrackerWorker::aos - autoTarget setting " << satWorkerState->m_name;
|
|
m_settings.m_target = satWorkerState->m_name;
|
|
// Update GUI with new target
|
|
if (getMessageQueueToGUI())
|
|
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportTarget::create(satWorkerState->m_name));
|
|
}
|
|
}
|
|
|
|
// TODO: Detect if different device sets are used and support multiple sats simultaneously
|
|
if (m_settings.m_target == satWorkerState->m_name)
|
|
applyDeviceAOSSettings(satWorkerState->m_name);
|
|
}
|
|
|
|
// Determine if we need to flip rotator or use extended azimuth to avoid 360/0 discontinuity
|
|
void SatelliteTrackerWorker::calculateRotation(SatWorkerState *satWorkerState)
|
|
{
|
|
m_flipRotation = false;
|
|
m_extendedAzRotation = false;
|
|
if (satWorkerState->m_satState.m_passes.size() > 0)
|
|
{
|
|
SatNogsSatellite *sat = m_satellites.value(satWorkerState->m_name);
|
|
bool passes0 = getPassesThrough0Deg(sat->m_tle->m_tle0, sat->m_tle->m_tle1, sat->m_tle->m_tle2,
|
|
m_settings.m_latitude, m_settings.m_longitude, m_settings.m_heightAboveSeaLevel/1000.0,
|
|
satWorkerState->m_satState.m_passes[0].m_aos, satWorkerState->m_satState.m_passes[0].m_los);
|
|
if (passes0)
|
|
{
|
|
double aosAz = satWorkerState->m_satState.m_passes[0].m_aosAzimuth;
|
|
double losAz = satWorkerState->m_satState.m_passes[0].m_losAzimuth;
|
|
double minAz = std::min(aosAz, losAz);
|
|
if ((m_settings.m_rotatorMaxAzimuth - 360.0) > minAz)
|
|
m_extendedAzRotation = true;
|
|
else if (m_settings.m_rotatorMaxElevation == 180.0)
|
|
m_flipRotation = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
QString SatelliteTrackerWorker::substituteVariables(const QString &textIn, const QString &satelliteName)
|
|
{
|
|
SatWorkerState *satWorkerState = m_workerState.value(satelliteName);
|
|
if (!satWorkerState) {
|
|
return "";
|
|
}
|
|
|
|
int durationMins = (int)round((satWorkerState->m_los.toSecsSinceEpoch() - satWorkerState->m_aos.toSecsSinceEpoch())/60.0);
|
|
|
|
QString text = textIn;
|
|
text = text.replace("${name}", satelliteName);
|
|
text = text.replace("${duration}", QString::number(durationMins));
|
|
if (satWorkerState->m_satState.m_passes.size() > 0)
|
|
{
|
|
text = text.replace("${aos}", satWorkerState->m_satState.m_passes[0].m_aos.toString());
|
|
text = text.replace("${los}", satWorkerState->m_satState.m_passes[0].m_los.toString());
|
|
text = text.replace("${elevation}", QString::number(std::round(satWorkerState->m_satState.m_passes[0].m_maxElevation)));
|
|
text = text.replace("${aosAzimuth}", QString::number(std::round(satWorkerState->m_satState.m_passes[0].m_aosAzimuth)));
|
|
text = text.replace("${losAzimuth}", QString::number(std::round(satWorkerState->m_satState.m_passes[0].m_losAzimuth)));
|
|
text = text.replace("${northToSouth}", QString::number(satWorkerState->m_satState.m_passes[0].m_northToSouth));
|
|
text = text.replace("${latitude}", QString::number(satWorkerState->m_satState.m_latitude));
|
|
text = text.replace("${longitude}", QString::number(satWorkerState->m_satState.m_longitude));
|
|
text = text.replace("${altitude}", QString::number(satWorkerState->m_satState.m_altitude));
|
|
text = text.replace("${azimuth}", QString::number(std::round(satWorkerState->m_satState.m_azimuth)));
|
|
text = text.replace("${elevation}", QString::number(std::round(satWorkerState->m_satState.m_elevation)));
|
|
text = text.replace("${range}", QString::number(std::round(satWorkerState->m_satState.m_range)));
|
|
text = text.replace("${rangeRate}", QString::number(std::round(satWorkerState->m_satState.m_rangeRate)));
|
|
text = text.replace("${speed}", QString::number(std::round(satWorkerState->m_satState.m_speed)));
|
|
text = text.replace("${period}", QString::number(satWorkerState->m_satState.m_period));
|
|
}
|
|
return text;
|
|
}
|
|
|
|
void SatelliteTrackerWorker::executeCommand(const QString &command, const QString &satelliteName)
|
|
{
|
|
if (!command.isEmpty())
|
|
{
|
|
#if QT_CONFIG(process)
|
|
// Replace variables
|
|
QString cmd = substituteVariables(command, satelliteName);
|
|
QStringList allArgs = QProcess::splitCommand(cmd);
|
|
qDebug() << "SatelliteTrackerWorker::executeCommand: Executing: " << allArgs;
|
|
QString program = allArgs[0];
|
|
allArgs.pop_front();
|
|
QProcess::startDetached(program, allArgs);
|
|
#else
|
|
qWarning() << "SatelliteTrackerWorker::executeCommand: QProcess not supported. Can't run: " << command;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name)
|
|
{
|
|
// Execute global program/script
|
|
if (!m_settings.m_aosCommand.isEmpty()) {
|
|
executeCommand(m_settings.m_aosCommand, name);
|
|
}
|
|
|
|
// Update device set
|
|
if (m_settings.m_deviceSettings.contains(name))
|
|
{
|
|
QList<SatelliteTrackerSettings::SatelliteDeviceSettings *> *m_deviceSettingsList = m_settings.m_deviceSettings.value(name);
|
|
|
|
MainCore *mainCore = MainCore::instance();
|
|
|
|
// Load presets
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
if (!devSettings->m_presetGroup.isEmpty())
|
|
{
|
|
const MainSettings& mainSettings = mainCore->getSettings();
|
|
const std::vector<DeviceSet*>& deviceSets = mainCore->getDeviceSets();
|
|
|
|
if (devSettings->m_deviceSetIndex < (int)deviceSets.size())
|
|
{
|
|
const DeviceSet *deviceSet = deviceSets[devSettings->m_deviceSetIndex];
|
|
QString presetType;
|
|
if (deviceSet->m_deviceSourceEngine != nullptr) {
|
|
presetType = "R";
|
|
} else if (deviceSet->m_deviceSinkEngine != nullptr) {
|
|
presetType = "T";
|
|
} else if (deviceSet->m_deviceMIMOEngine != nullptr) {
|
|
presetType = "M";
|
|
}
|
|
|
|
const Preset* preset = mainSettings.getPreset(devSettings->m_presetGroup, devSettings->m_presetFrequency, devSettings->m_presetDescription, presetType);
|
|
|
|
if (preset != nullptr)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::aos: Loading preset " << preset->getDescription() << " to device set at " << devSettings->m_deviceSetIndex;
|
|
MainCore::MsgLoadPreset *msg = MainCore::MsgLoadPreset::create(preset, devSettings->m_deviceSetIndex);
|
|
mainCore->getMainMessageQueue()->push(msg);
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "SatelliteTrackerWorker::aos: Unable to get preset: " << devSettings->m_presetGroup << " " << devSettings->m_presetFrequency << " " << devSettings->m_presetDescription;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "SatelliteTrackerWorker::aos: device set at " << devSettings->m_deviceSetIndex << " does not exist";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wait a little bit for presets to load before performing other steps
|
|
QTimer::singleShot(1000, [this, name, m_deviceSettingsList]()
|
|
{
|
|
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
|
|
// Override frequency
|
|
if (devSettings->m_frequency != 0)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::aos: setting frequency to: " << devSettings->m_frequency;
|
|
ChannelWebAPIUtils::setCenterFrequency(devSettings->m_deviceSetIndex, devSettings->m_frequency);
|
|
}
|
|
|
|
// Execute per satellite program/script
|
|
if (!devSettings->m_aosCommand.isEmpty()) {
|
|
executeCommand(devSettings->m_aosCommand, name);
|
|
}
|
|
|
|
}
|
|
|
|
// Start acquisition - Need to use WebAPI, in order for GUI to correctly reflect being started
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
if (devSettings->m_startOnAOS)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::aos: starting acquisition";
|
|
ChannelWebAPIUtils::run(devSettings->m_deviceSetIndex);
|
|
}
|
|
}
|
|
|
|
// Send AOS message to channels/features
|
|
SatWorkerState *satWorkerState = m_workerState.value(name);
|
|
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
|
|
enableDoppler(satWorkerState);
|
|
|
|
// Start file sinks (need a little delay to ensure sample rate message has been handled in filerecord)
|
|
QTimer::singleShot(1000, [m_deviceSettingsList]()
|
|
{
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
|
|
if (devSettings->m_startStopFileSink)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::aos: starting file sinks";
|
|
ChannelWebAPIUtils::startStopFileSinks(devSettings->m_deviceSetIndex, true);
|
|
}
|
|
}
|
|
});
|
|
|
|
});
|
|
}
|
|
else
|
|
{
|
|
// Send AOS message to channels/features
|
|
SatWorkerState *satWorkerState = m_workerState.value(name);
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
void SatelliteTrackerWorker::enableDoppler(SatWorkerState *satWorkerState)
|
|
{
|
|
QList<SatelliteTrackerSettings::SatelliteDeviceSettings *> *m_deviceSettingsList = m_settings.m_deviceSettings.value(satWorkerState->m_name);
|
|
if (m_deviceSettingsList)
|
|
{
|
|
satWorkerState->m_doppler.clear();
|
|
bool requiresDoppler = false;
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
if (devSettings->m_doppler.size() > 0)
|
|
{
|
|
requiresDoppler = true;
|
|
satWorkerState->m_doppler.append(0);
|
|
}
|
|
}
|
|
if (requiresDoppler)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::applyDeviceAOSSettings: Enabling doppler for " << satWorkerState->m_name;
|
|
satWorkerState->m_dopplerTimer.setInterval(m_settings.m_dopplerPeriod * 1000);
|
|
satWorkerState->m_dopplerTimer.start();
|
|
disconnect(satWorkerState->m_connection);
|
|
satWorkerState->m_connection = connect(&satWorkerState->m_dopplerTimer, &QTimer::timeout, [this, satWorkerState]() {
|
|
doppler(satWorkerState);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::disableDoppler(SatWorkerState *satWorkerState)
|
|
{
|
|
// Stop Doppler timer, and set interval to 0, so we don't restart it in start()
|
|
satWorkerState->m_dopplerTimer.stop();
|
|
satWorkerState->m_dopplerTimer.setInterval(0);
|
|
disconnect(satWorkerState->m_connection);
|
|
// Remove doppler correction from any channel
|
|
if (satWorkerState->m_doppler.size() > 0)
|
|
{
|
|
QList<SatelliteTrackerSettings::SatelliteDeviceSettings *> *m_deviceSettingsList = m_settings.m_deviceSettings.value(satWorkerState->m_name);
|
|
if (m_deviceSettingsList)
|
|
{
|
|
int idx = 0;
|
|
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
if (devSettings->m_doppler.size() > 0)
|
|
{
|
|
for (int j = 0; j < devSettings->m_doppler.size(); j++)
|
|
{
|
|
int offset;
|
|
if (ChannelWebAPIUtils::getFrequencyOffset(devSettings->m_deviceSetIndex, devSettings->m_doppler[j], offset))
|
|
{
|
|
// Remove old doppler
|
|
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
|
|
ChannelAPI *channel = deviceSets[devSettings->m_deviceSetIndex]->getChannelAt(j);
|
|
int tx = false;
|
|
if (channel) {
|
|
tx = channel->getStreamType() == ChannelAPI::StreamSingleSource; // What if MIMO?
|
|
}
|
|
if (tx) {
|
|
offset -= satWorkerState->m_doppler[idx];
|
|
} else {
|
|
offset += satWorkerState->m_doppler[idx];
|
|
}
|
|
if (!ChannelWebAPIUtils::setFrequencyOffset(devSettings->m_deviceSetIndex, devSettings->m_doppler[j], offset))
|
|
qDebug() << "SatelliteTrackerWorker::disableDoppler: Failed to set frequency offset";
|
|
}
|
|
else
|
|
qDebug() << "SatelliteTrackerWorker::disableDoppler: Failed to get frequency offset";
|
|
}
|
|
satWorkerState->m_doppler[idx] = 0;
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::doppler(SatWorkerState *satWorkerState)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::doppler " << satWorkerState->m_name;
|
|
|
|
QList<SatelliteTrackerSettings::SatelliteDeviceSettings *> *m_deviceSettingsList = m_settings.m_deviceSettings.value(satWorkerState->m_name);
|
|
if (m_deviceSettingsList)
|
|
{
|
|
int idx = 0;
|
|
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
if (devSettings->m_doppler.size() > 0)
|
|
{
|
|
// Get center frequency for this device
|
|
double centerFrequency;
|
|
|
|
if (ChannelWebAPIUtils::getCenterFrequency(devSettings->m_deviceSetIndex, centerFrequency))
|
|
{
|
|
// Calculate frequency delta due to Doppler
|
|
double c = 299792458.0;
|
|
double deltaF = centerFrequency * satWorkerState->m_satState.m_rangeRate * 1000.0 / c;
|
|
int doppler = (int)round(deltaF);
|
|
|
|
for (int j = 0; j < devSettings->m_doppler.size(); j++)
|
|
{
|
|
int offset;
|
|
if (ChannelWebAPIUtils::getFrequencyOffset(devSettings->m_deviceSetIndex, devSettings->m_doppler[j], offset))
|
|
{
|
|
// Apply doppler - For receive, we subtract, transmit we add
|
|
std::vector<DeviceSet*>& deviceSets = MainCore::instance()->getDeviceSets();
|
|
ChannelAPI *channel = deviceSets[devSettings->m_deviceSetIndex]->getChannelAt(j);
|
|
int tx = false;
|
|
if (channel) {
|
|
tx = channel->getStreamType() == ChannelAPI::StreamSingleSource; // What if MIMO?
|
|
}
|
|
|
|
// Remove old doppler and apply new
|
|
int initOffset;
|
|
if (tx)
|
|
{
|
|
initOffset = offset - satWorkerState->m_doppler[idx];
|
|
offset = initOffset + doppler;
|
|
}
|
|
else
|
|
{
|
|
initOffset = offset + satWorkerState->m_doppler[idx];
|
|
offset = initOffset - doppler;
|
|
}
|
|
if (!ChannelWebAPIUtils::setFrequencyOffset(devSettings->m_deviceSetIndex, devSettings->m_doppler[j], offset))
|
|
qDebug() << "SatelliteTrackerWorker::doppler: Failed to set frequency offset";
|
|
}
|
|
else
|
|
qDebug() << "SatelliteTrackerWorker::doppler: Failed to get frequency offset";
|
|
}
|
|
|
|
satWorkerState->m_doppler[idx] = doppler;
|
|
}
|
|
else
|
|
qDebug() << "SatelliteTrackerWorker::doppler: couldn't get centre frequency for device at " << devSettings->m_deviceSetIndex;
|
|
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::los " << satWorkerState->m_name << " target: " << m_settings.m_target;
|
|
|
|
// Indicate LOS to GUI
|
|
if (getMessageQueueToGUI())
|
|
{
|
|
QString speech = substituteVariables(m_settings.m_losSpeech, satWorkerState->m_name);
|
|
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportLOS::create(satWorkerState->m_name, speech));
|
|
}
|
|
|
|
disableDoppler(satWorkerState);
|
|
|
|
if (m_settings.m_target == satWorkerState->m_name)
|
|
{
|
|
// Execute program/script
|
|
if (!m_settings.m_losCommand.isEmpty()) {
|
|
executeCommand(m_settings.m_losCommand, satWorkerState->m_name);
|
|
}
|
|
|
|
// 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<SatelliteTrackerSettings::SatelliteDeviceSettings *> *m_deviceSettingsList = m_settings.m_deviceSettings.value(satWorkerState->m_name);
|
|
|
|
// Stop file sinks
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
if (devSettings->m_startStopFileSink)
|
|
{
|
|
qDebug() << "SatelliteTrackerWorker::los: stopping file sinks";
|
|
ChannelWebAPIUtils::startStopFileSinks(devSettings->m_deviceSetIndex, false);
|
|
}
|
|
}
|
|
|
|
// Stop acquisition
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
|
|
if (devSettings->m_stopOnLOS) {
|
|
ChannelWebAPIUtils::stop(devSettings->m_deviceSetIndex);
|
|
}
|
|
}
|
|
|
|
// Execute per satellite program/script
|
|
// Do after stopping acquisition, so files are closed by file sink
|
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
|
{
|
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
|
if (!devSettings->m_losCommand.isEmpty()) {
|
|
executeCommand(devSettings->m_losCommand, satWorkerState->m_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Is another lower-priority satellite with AOS available to switch to?
|
|
if (m_settings.m_autoTarget)
|
|
{
|
|
for (int i = m_settings.m_satellites.indexOf(m_settings.m_target) + 1; i < m_settings.m_satellites.size(); i++)
|
|
{
|
|
if (m_workerState.contains(m_settings.m_satellites[i]))
|
|
{
|
|
SatWorkerState *newSatWorkerState = m_workerState.value(m_settings.m_satellites[i]);
|
|
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];
|
|
// Update GUI with new target
|
|
if (getMessageQueueToGUI())
|
|
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportTarget::create(m_settings.m_target));
|
|
// Apply device settings
|
|
applyDeviceAOSSettings(m_settings.m_target);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_recalculatePasses = true;
|
|
}
|
|
|
|
bool SatWorkerState::hasAOS(const QDateTime& currentTime)
|
|
{
|
|
return (m_aos <= currentTime) && (m_los > currentTime);
|
|
}
|