2021-01-13 20:51:38 +00:00
///////////////////////////////////////////////////////////////////////////////////
2026-04-03 20:33:00 +01:00
// Copyright (C) 2021-2026 Jon Beniston, M7RCE <jon@beniston.com> //
2023-11-18 13:12:18 +01:00
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai> //
2021-01-13 20:51:38 +00:00
// //
// 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>
2026-04-03 20:33:00 +01:00
# include <QVector3D>
2021-01-13 20:51:38 +00:00
# include "SWGTargetAzimuthElevation.h"
# include "SWGMapItem.h"
2021-10-12 11:07:56 +01:00
# include "SWGStarTrackerTarget.h"
2024-02-14 13:20:33 +00:00
# include "SWGSkyMapTarget.h"
2021-01-13 20:51:38 +00:00
# include "webapi/webapiadapterinterface.h"
2023-05-23 22:32:26 +01:00
# include "channel/channelwebapiutils.h"
2021-01-13 20:51:38 +00:00
# include "util/units.h"
2026-04-03 20:33:00 +01:00
# include "util/profiler.h"
2021-01-13 20:51:38 +00:00
# include "maincore.h"
# include "startracker.h"
# include "startrackerworker.h"
# include "startrackerreport.h"
2026-04-03 20:33:00 +01:00
# include "spice.h"
# include "spiceephemerides.h"
2021-01-13 20:51:38 +00:00
MESSAGE_CLASS_DEFINITION ( StarTrackerWorker : : MsgConfigureStarTrackerWorker , Message )
MESSAGE_CLASS_DEFINITION ( StarTrackerReport : : MsgReportAzAl , Message )
MESSAGE_CLASS_DEFINITION ( StarTrackerReport : : MsgReportRADec , Message )
2021-10-12 11:07:56 +01:00
MESSAGE_CLASS_DEFINITION ( StarTrackerReport : : MsgReportGalactic , Message )
2026-04-03 20:33:00 +01:00
MESSAGE_CLASS_DEFINITION ( StarTrackerReport : : MsgReportAzElVsTime , Message )
MESSAGE_CLASS_DEFINITION ( StarTrackerReport : : MsgReportSolarSystemPositions , Message )
MESSAGE_CLASS_DEFINITION ( StarTrackerReport : : MsgReportJupiter , Message )
MESSAGE_CLASS_DEFINITION ( StarTrackerReport : : MsgReportJupiterData , Message )
static double linearInterpolate ( double y1 , double y2 , double mu )
{
return y1 + mu * ( y2 - y1 ) ;
}
static double cubicInterpolate ( double y0 , double y1 , double y2 , double y3 , double mu )
{
double a0 , a1 , a2 , a3 , mu2 ;
mu2 = mu * mu ;
a0 = y3 - y2 - y0 + y1 ;
a1 = y0 - y1 - a0 ;
a2 = y2 - y0 ;
a3 = y1 ;
return a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3 ;
}
2021-01-13 20:51:38 +00:00
StarTrackerWorker : : StarTrackerWorker ( StarTracker * starTracker , WebAPIAdapterInterface * webAPIAdapterInterface ) :
m_starTracker ( starTracker ) ,
m_webAPIAdapterInterface ( webAPIAdapterInterface ) ,
m_msgQueueToFeature ( nullptr ) ,
m_msgQueueToGUI ( nullptr ) ,
2021-11-25 09:28:59 +00:00
m_pollTimer ( this ) ,
2021-01-13 20:51:38 +00:00
m_tcpServer ( nullptr ) ,
2021-10-12 11:07:56 +01:00
m_clientConnection ( nullptr ) ,
2026-04-03 20:33:00 +01:00
m_solarFlux ( 0.0f ) ,
m_jplHorizons ( nullptr ) ,
m_requestedEphemeridesLatitude ( 0.0 ) ,
m_requestedEphemeridesLongitude ( 0.0 ) ,
m_chartLatitude ( 0.0 ) ,
m_chartLongitude ( 0.0 ) ,
m_chartL ( 0.0f ) ,
m_chartB ( 0.0f )
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
connect ( & m_pollTimer , & QTimer : : timeout , this , & StarTrackerWorker : : update ) ;
2021-01-13 20:51:38 +00:00
}
StarTrackerWorker : : ~ StarTrackerWorker ( )
{
2022-09-20 21:13:12 +01:00
stopWork ( ) ;
2021-01-13 20:51:38 +00:00
m_inputMessageQueue . clear ( ) ;
2026-04-03 20:33:00 +01:00
if ( m_jplHorizons )
{
disconnect ( m_jplHorizons , & JPLHorizons : : ephemeridesUpdated , this , & StarTrackerWorker : : horizonsEphemeridesUpdated ) ;
delete m_jplHorizons ;
}
2021-01-13 20:51:38 +00:00
}
2022-09-20 11:48:25 +01:00
void StarTrackerWorker : : startWork ( )
2021-01-13 20:51:38 +00:00
{
QMutexLocker mutexLocker ( & m_mutex ) ;
connect ( & m_inputMessageQueue , SIGNAL ( messageEnqueued ( ) ) , this , SLOT ( handleInputMessages ( ) ) ) ;
2021-11-25 09:28:59 +00:00
m_pollTimer . start ( ( int ) round ( m_settings . m_updatePeriod * 1000.0 ) ) ;
2022-09-20 11:48:25 +01:00
// Handle any messages already on the queue
handleInputMessages ( ) ;
2021-11-25 09:28:59 +00:00
}
2021-01-13 20:51:38 +00:00
void StarTrackerWorker : : stopWork ( )
{
QMutexLocker mutexLocker ( & m_mutex ) ;
2023-02-16 14:30:14 +00:00
m_pollTimer . stop ( ) ;
2021-01-13 20:51:38 +00:00
disconnect ( & m_inputMessageQueue , SIGNAL ( messageEnqueued ( ) ) , this , SLOT ( handleInputMessages ( ) ) ) ;
2023-02-16 14:30:14 +00:00
if ( m_settings . m_drawSunOnMap )
removeFromMap ( " Sun " ) ;
if ( m_settings . m_drawMoonOnMap )
removeFromMap ( " Moon " ) ;
if ( m_settings . m_drawStarOnMap & & ( m_settings . m_target ! = " Sun " ) & & ( m_settings . m_target ! = " Moon " ) )
removeFromMap ( " Star " ) ;
2021-01-13 20:51:38 +00:00
restartServer ( false , 0 ) ;
}
void StarTrackerWorker : : handleInputMessages ( )
{
Message * message ;
while ( ( message = m_inputMessageQueue . pop ( ) ) ! = nullptr )
{
if ( handleMessage ( * message ) ) {
delete message ;
}
}
}
bool StarTrackerWorker : : handleMessage ( const Message & cmd )
{
if ( MsgConfigureStarTrackerWorker : : match ( cmd ) )
{
QMutexLocker mutexLocker ( & m_mutex ) ;
MsgConfigureStarTrackerWorker & cfg = ( MsgConfigureStarTrackerWorker & ) cmd ;
2022-11-30 07:21:59 +01:00
applySettings ( cfg . getSettings ( ) , cfg . getSettingsKeys ( ) , cfg . getForce ( ) ) ;
2021-01-13 20:51:38 +00:00
return true ;
}
2021-10-12 11:07:56 +01:00
else if ( StarTracker : : MsgSetSolarFlux : : match ( cmd ) )
{
StarTracker : : MsgSetSolarFlux & msg = ( StarTracker : : MsgSetSolarFlux & ) cmd ;
m_solarFlux = msg . getFlux ( ) ;
return true ;
}
2021-01-13 20:51:38 +00:00
else
{
return false ;
}
}
2022-11-30 07:21:59 +01:00
void StarTrackerWorker : : applySettings ( const StarTrackerSettings & settings , const QList < QString > & settingsKeys , bool force )
2021-01-13 20:51:38 +00:00
{
2022-11-30 07:21:59 +01:00
qDebug ( ) < < " StarTrackerWorker::applySettings: " < < settings . getDebugString ( settingsKeys , force ) < < " force: " < < force ;
if ( settingsKeys . contains ( " ra " )
| | settingsKeys . contains ( " dec " )
| | settingsKeys . contains ( " latitude " )
| | settingsKeys . contains ( " longitude " )
| | settingsKeys . contains ( " target " )
| | settingsKeys . contains ( " dateTime " )
| | settingsKeys . contains ( " refraction " )
| | settingsKeys . contains ( " pressure " )
| | settingsKeys . contains ( " temperature " )
| | settingsKeys . contains ( " humidity " )
| | settingsKeys . contains ( " heightAboveSeaLevel " )
| | settingsKeys . contains ( " temperatureLapseRate " )
| | settingsKeys . contains ( " frequency " )
| | settingsKeys . contains ( " beamwidth " )
2023-05-23 22:32:26 +01:00
| | settingsKeys . contains ( " azimuth " )
| | settingsKeys . contains ( " elevation " )
2022-11-30 07:21:59 +01:00
| | settingsKeys . contains ( " l " )
| | settingsKeys . contains ( " b " )
2023-03-30 16:48:02 +01:00
| | settingsKeys . contains ( " azimuthOffset " )
| | settingsKeys . contains ( " elevationOffset " )
2026-04-03 20:33:00 +01:00
| | settingsKeys . contains ( " night " )
| | settingsKeys . contains ( " logScale " )
| | settingsKeys . contains ( " utc " )
| | settingsKeys . contains ( " chartSelect " )
| | settingsKeys . contains ( " chartSubSelect " )
2021-10-12 11:07:56 +01:00
| | force )
2021-01-13 20:51:38 +00:00
{
// Recalculate immediately
QTimer : : singleShot ( 1 , this , & StarTrackerWorker : : update ) ;
m_pollTimer . start ( ( int ) round ( settings . m_updatePeriod * 1000.0 ) ) ;
}
2022-11-30 07:21:59 +01:00
else if ( settingsKeys . contains ( " updatePeriod " ) | | force )
2021-01-13 20:51:38 +00:00
{
m_pollTimer . start ( ( int ) round ( settings . m_updatePeriod * 1000.0 ) ) ;
}
2022-11-30 07:21:59 +01:00
if ( settingsKeys . contains ( " drawSunOnMap " )
| | settingsKeys . contains ( " drawMoonOnMap " )
| | settingsKeys . contains ( " drawStarOnMap " )
2026-02-12 22:38:14 +01:00
| | settingsKeys . contains ( " target " ) )
2022-11-30 07:21:59 +01:00
{
if ( ! settings . m_drawSunOnMap & & m_settings . m_drawSunOnMap )
removeFromMap ( " Sun " ) ;
if ( ! settings . m_drawMoonOnMap & & m_settings . m_drawMoonOnMap )
removeFromMap ( " Moon " ) ;
if ( ( ! settings . m_drawStarOnMap & & m_settings . m_drawStarOnMap )
| | ( ( ( settings . m_target = = " Sun " ) | | ( settings . m_target = = " Moon " ) )
& & ( ( m_settings . m_target ! = " Sun " ) & & ( m_settings . m_target ! = " Moon " ) ) ) )
removeFromMap ( " Star " ) ;
}
if ( settingsKeys . contains ( " serverPort " ) | |
settingsKeys . contains ( " enableServer " ) | | force )
2021-01-13 20:51:38 +00:00
{
restartServer ( settings . m_enableServer , settings . m_serverPort ) ;
}
2026-04-03 20:33:00 +01:00
if ( ( settingsKeys . contains ( " targetSource " ) | | force ) & & ( settings . m_targetSource = = " Horizons " ) )
{
if ( ! m_jplHorizons )
{
m_jplHorizons = JPLHorizons : : create ( ) ;
if ( m_jplHorizons ) {
connect ( m_jplHorizons , & JPLHorizons : : ephemeridesUpdated , this , & StarTrackerWorker : : horizonsEphemeridesUpdated ) ;
}
}
}
2022-11-30 07:21:59 +01:00
if ( force ) {
m_settings = settings ;
} else {
m_settings . applySettings ( settingsKeys , settings ) ;
}
2021-01-13 20:51:38 +00:00
}
void StarTrackerWorker : : restartServer ( bool enabled , uint32_t port )
{
if ( m_tcpServer )
{
if ( m_clientConnection )
{
m_clientConnection - > close ( ) ;
delete m_clientConnection ;
m_clientConnection = nullptr ;
}
disconnect ( m_tcpServer , & QTcpServer : : newConnection , this , & StarTrackerWorker : : acceptConnection ) ;
m_tcpServer - > close ( ) ;
delete m_tcpServer ;
m_tcpServer = nullptr ;
}
if ( enabled )
{
qDebug ( ) < < " StarTrackerWorker::restartServer: server enabled on port " < < port ;
m_tcpServer = new QTcpServer ( this ) ;
if ( ! m_tcpServer - > listen ( QHostAddress : : Any , port ) ) {
qWarning ( " Star Tracker failed to listen on port %u. Check it is not already in use. " , port ) ;
} else {
connect ( m_tcpServer , & QTcpServer : : newConnection , this , & StarTrackerWorker : : acceptConnection ) ;
}
}
}
void StarTrackerWorker : : acceptConnection ( )
{
QMutexLocker mutexLocker ( & m_mutex ) ;
m_clientConnection = m_tcpServer - > nextPendingConnection ( ) ;
if ( ! m_clientConnection ) {
return ;
}
connect ( m_clientConnection , & QIODevice : : readyRead , this , & StarTrackerWorker : : readStellariumCommand ) ;
connect ( m_clientConnection , SIGNAL ( disconnected ( ) ) , this , SLOT ( disconnected ( ) ) ) ;
# if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect ( m_clientConnection , QOverload < QAbstractSocket : : SocketError > : : of ( & QAbstractSocket : : error ) , this , & StarTrackerWorker : : errorOccurred ) ;
# else
connect ( m_clientConnection , & QAbstractSocket : : errorOccurred , this , & StarTrackerWorker : : errorOccurred ) ;
# endif
qDebug ( ) < < " StarTrackerWorker::acceptConnection: client connected " ;
}
void StarTrackerWorker : : disconnected ( )
{
QMutexLocker mutexLocker ( & m_mutex ) ;
qDebug ( ) < < " StarTrackerWorker::disconnected " ;
m_clientConnection - > deleteLater ( ) ;
m_clientConnection = nullptr ;
/*if (m_msgQueueToFeature)
{
StarTrackerWorker : : MsgReportWorker * msg = StarTrackerWorker : : MsgReportWorker : : create ( " Disconnected " ) ;
m_msgQueueToFeature - > push ( msg ) ;
} */
}
void StarTrackerWorker : : errorOccurred ( QAbstractSocket : : SocketError socketError )
{
qDebug ( ) < < " StarTrackerWorker::errorOccurred: " < < socketError ;
/*if (m_msgQueueToFeature)
{
StarTrackerWorker : : MsgReportWorker * msg = StarTrackerWorker : : MsgReportWorker : : create ( m_socket . errorString ( ) + " " + socketError ) ;
m_msgQueueToFeature - > push ( msg ) ;
} */
}
// Get RA & Dec from Stellarium
// Protocol described here:
// http://svn.code.sf.net/p/stellarium/code/trunk/telescope_server/stellarium_telescope_protocol.txt
void StarTrackerWorker : : readStellariumCommand ( )
{
QMutexLocker mutexLocker ( & m_mutex ) ;
unsigned char buf [ 64 ] ;
qint64 len ;
len = m_clientConnection - > read ( ( char * ) buf , sizeof ( buf ) ) ;
if ( len ! = - 1 )
{
int msg_len ;
int msg_type ;
unsigned char * msg ;
// Extract length and message type
msg_len = buf [ 0 ] | ( buf [ 1 ] < < 8 ) ;
msg_type = buf [ 2 ] | ( buf [ 3 ] < < 8 ) ;
msg = & buf [ 4 ] ;
if ( msg_type = = 0 ) // MessageGoto
{
unsigned ra ;
int dec ;
if ( msg_len = = 20 )
{
// Skip time
msg + = 8 ;
// Extract RA LSB first
ra = msg [ 0 ] | ( msg [ 1 ] < < 8 ) | ( msg [ 2 ] < < 16 ) | ( msg [ 3 ] < < 24 ) ;
msg + = 4 ;
// Extract DEC LSB first
dec = msg [ 0 ] | ( msg [ 1 ] < < 8 ) | ( msg [ 2 ] < < 16 ) | ( msg [ 3 ] < < 24 ) ;
msg + = 4 ;
// Convert from integer to floating point
double raDeg = ra * ( 24.0 / 4294967296.0 ) ; // Convert to decimal hours
double decDeg = dec * ( 360.0 / 4294967296.0 ) ; // Convert to decimal degrees
// Set as current target
m_settings . m_ra = Units : : decimalHoursToHoursMinutesAndSeconds ( raDeg ) ;
m_settings . m_dec = Units : : decimalDegreesToDegreeMinutesAndSeconds ( decDeg ) ;
qDebug ( ) < < " StarTrackerWorker: New target from Stellarum: " < < m_settings . m_ra < < " " < < m_settings . m_dec ;
// Forward to GUI for display
2021-10-12 11:07:56 +01:00
if ( getMessageQueueToGUI ( ) ) {
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportRADec : : create ( raDeg , decDeg , " target " ) ) ;
}
2021-01-13 20:51:38 +00:00
}
else
{
qDebug ( ) < < " StarTrackerWorker: Unexpected number of bytes received ( " < < len < < " ) for message type: " < < msg_type ;
}
}
else
{
qDebug ( ) < < " StarTrackerWorker: Unsupported Stellarium message type: " < < msg_type ;
}
}
}
// Send our target to Stellarium (J2000 epoch)
void StarTrackerWorker : : writeStellariumTarget ( double ra , double dec )
{
QMutexLocker mutexLocker ( & m_mutex ) ;
if ( m_clientConnection ! = nullptr )
{
unsigned char buf [ 24 ] ;
// Length
buf [ 0 ] = sizeof ( buf ) ;
buf [ 1 ] = 0 ;
// Type (MessageCurrentPosition)
buf [ 2 ] = 0 ;
buf [ 3 ] = 0 ;
// Time (unused)
buf [ 4 ] = 0 ;
buf [ 5 ] = 0 ;
buf [ 6 ] = 0 ;
buf [ 7 ] = 0 ;
buf [ 8 ] = 0 ;
buf [ 9 ] = 0 ;
buf [ 10 ] = 0 ;
buf [ 11 ] = 0 ;
// RA
unsigned raInt = ra * ( 4294967296.0 / 24.0 ) ;
buf [ 12 ] = raInt & 0xff ;
buf [ 13 ] = ( raInt > > 8 ) & 0xff ;
buf [ 14 ] = ( raInt > > 16 ) & 0xff ;
buf [ 15 ] = ( raInt > > 24 ) & 0xff ;
// Dec
int decInt = dec * ( 4294967296.0 / 360.0 ) ;
buf [ 16 ] = decInt & 0xff ;
buf [ 17 ] = ( decInt > > 8 ) & 0xff ;
buf [ 18 ] = ( decInt > > 16 ) & 0xff ;
buf [ 19 ] = ( decInt > > 24 ) & 0xff ;
// Status (OK)
buf [ 20 ] = 0 ;
buf [ 21 ] = 0 ;
buf [ 22 ] = 0 ;
buf [ 23 ] = 0 ;
m_clientConnection - > write ( ( char * ) buf , sizeof ( buf ) ) ;
}
}
void StarTrackerWorker : : removeFromMap ( QString id )
{
2022-03-29 20:12:15 +02:00
QList < ObjectPipe * > mapMessagePipes ;
MainCore : : instance ( ) - > getMessagePipes ( ) . getMessagePipes ( m_starTracker , " mapitems " , mapMessagePipes ) ;
if ( mapMessagePipes . size ( ) > 0 ) {
sendToMap ( mapMessagePipes , id , " " , " " , 0.0 , 0.0 ) ;
2021-01-13 20:51:38 +00:00
}
}
2022-03-29 20:12:15 +02:00
void StarTrackerWorker : : sendToMap (
const QList < ObjectPipe * > & mapMessagePipes ,
QString name ,
QString image ,
QString text ,
double lat ,
double lon ,
double rotation
)
2021-01-13 20:51:38 +00:00
{
2022-03-29 20:12:15 +02:00
for ( const auto & pipe : mapMessagePipes )
2021-01-13 20:51:38 +00:00
{
2022-03-29 20:12:15 +02:00
MessageQueue * messageQueue = qobject_cast < MessageQueue * > ( pipe - > m_element ) ;
2021-01-13 20:51:38 +00:00
SWGSDRangel : : SWGMapItem * swgMapItem = new SWGSDRangel : : SWGMapItem ( ) ;
swgMapItem - > setName ( new QString ( name ) ) ;
swgMapItem - > setLatitude ( lat ) ;
swgMapItem - > setLongitude ( lon ) ;
swgMapItem - > setImage ( new QString ( image ) ) ;
swgMapItem - > setImageRotation ( rotation ) ;
swgMapItem - > setText ( new QString ( text ) ) ;
MainCore : : MsgMapItem * msg = MainCore : : MsgMapItem : : create ( m_starTracker , swgMapItem ) ;
2022-03-29 20:12:15 +02:00
messageQueue - > push ( msg ) ;
2021-01-13 20:51:38 +00:00
}
}
QString moonPhase ( double sunLongitude , double moonLongitude , double observationLatitude , double & rotation )
{
double difference = sunLongitude - moonLongitude ;
if ( difference < - 180.0 )
difference + = 360.0 ;
else if ( difference > 180.0 )
difference - = 360.0 ;
if ( difference > = 0.0 )
rotation = observationLatitude - 90.0 ;
else
rotation = 90.0 - observationLatitude ;
// These probably shouldn't be divided equally
if ( difference < - 157.5 )
return " full " ;
else if ( difference < - 112.5 )
return " waxing-gibbous " ;
else if ( difference < - 67.5 )
return " first-quarter " ;
else if ( difference < - 22.5 )
return " waxing-crescent " ;
else if ( difference < 22.5 )
return " new " ;
else if ( difference < 67.5 )
return " waning-crescent " ;
else if ( difference < 112.5 )
return " third-quarter " ;
else if ( difference < 157.5 )
return " waning-gibbous " ;
else if ( difference < 202.5 )
return " full " ;
else
return " full " ;
}
2026-04-03 20:33:00 +01:00
bool StarTrackerWorker : : getAzElFromSatelliteTracker ( double & azimuth , double & elevation )
{
unsigned int satelliteTrackerFeatureSetIndex , satelliteTrackerFeatureIndex ;
if ( MainCore : : getFeatureIndexFromId ( m_settings . m_target , satelliteTrackerFeatureSetIndex , satelliteTrackerFeatureIndex ) )
{
if ( ChannelWebAPIUtils : : getFeatureReportValue ( satelliteTrackerFeatureSetIndex , satelliteTrackerFeatureIndex , " targetAzimuth " , azimuth )
& & ChannelWebAPIUtils : : getFeatureReportValue ( satelliteTrackerFeatureSetIndex , satelliteTrackerFeatureIndex , " targetElevation " , elevation ) )
{
return true ;
}
else
{
qDebug ( ) < < " StarTrackerWorker::getAzElFromSatelliteTracker - Failed to get feature report value (targetAzimuth/targetElevation) from " < < m_settings . m_target ;
return false ;
}
}
else
{
qDebug ( ) < < " StarTrackerWorker::getAzElFromSatelliteTracker - Failed to parse feature name " < < m_settings . m_target ;
return false ;
}
}
bool StarTrackerWorker : : getRAFromSkyMap ( RADec & rdJ2000 )
{
double ra , dec ;
unsigned int featureSetIndex , featureIndex ;
if ( MainCore : : getFeatureIndexFromId ( m_settings . m_target , featureSetIndex , featureIndex ) )
{
if ( ChannelWebAPIUtils : : getFeatureReportValue ( featureSetIndex , featureIndex , " ra " , ra )
& & ChannelWebAPIUtils : : getFeatureReportValue ( featureSetIndex , featureIndex , " dec " , dec ) )
{
rdJ2000 . ra = ra ;
rdJ2000 . dec = dec ;
return true ;
}
else
{
qDebug ( ) < < " StarTrackerWorker::getRAFromSkyMap - Failed to get feature report value (ra/dec) from " < < m_settings . m_target ;
return false ;
}
}
else
{
qDebug ( ) < < " StarTrackerWorker::getRAFromSkyMap - Failed to parse feature name " < < m_settings . m_target ;
return false ;
}
}
bool StarTrackerWorker : : getRAFromHorizons ( const QDateTime & dateTime , RADec & rdJ2000 )
{
if ( ( m_requestedEphemeridesTarget ! = m_settings . m_target )
| | ( dateTime < m_requestedEphemeridesStartTime )
| | ( dateTime > m_requestedEphemeridesStopTime )
| | ( m_requestedEphemeridesLatitude ! = m_settings . m_latitude )
| | ( m_requestedEphemeridesLongitude ! = m_settings . m_longitude )
)
{
// Clear current data
m_ephemeridesTarget = " " ;
m_horizonsEphemerides = { } ;
// Request ephemerides
m_requestedEphemeridesTarget = m_settings . m_target ;
m_requestedEphemeridesStartTime = QDateTime ( dateTime . date ( ) , QTime ( 0 , 0 ) ) ;
m_requestedEphemeridesStopTime = QDateTime ( dateTime . date ( ) . addDays ( 1 ) , QTime ( 0 , 0 ) ) ;
m_requestedEphemeridesLatitude = m_settings . m_latitude ;
m_requestedEphemeridesLongitude = m_settings . m_longitude ;
m_jplHorizons - > getData ( m_requestedEphemeridesTarget , m_requestedEphemeridesStartTime , m_requestedEphemeridesStopTime , m_requestedEphemeridesLatitude , m_requestedEphemeridesLongitude , 0.0f ) ;
}
else if ( ( m_settings . m_target = = m_ephemeridesTarget )
& & ( ! m_horizonsEphemerides . isEmpty ( )
& & ( ( dateTime > = m_horizonsEphemerides [ 0 ] . m_dateTime )
| | ( dateTime < m_horizonsEphemerides [ m_horizonsEphemerides . size ( ) - 1 ] . m_dateTime )
)
)
)
{
// Find closest empheris
for ( int i = 0 ; i < m_horizonsEphemerides . size ( ) - 1 ; i + + )
{
if ( ( dateTime > = m_horizonsEphemerides [ i ] . m_dateTime ) & & ( dateTime < m_horizonsEphemerides [ i + 1 ] . m_dateTime ) )
{
// Interpolate
qint64 x = dateTime . toMSecsSinceEpoch ( ) ;
qint64 x1 = m_horizonsEphemerides [ i ] . m_dateTime . toMSecsSinceEpoch ( ) ;
qint64 x2 = m_horizonsEphemerides [ i + 1 ] . m_dateTime . toMSecsSinceEpoch ( ) ;
qint64 diff = x2 - x1 ;
double mu = ( x - x1 ) / ( double ) diff ;
if ( ( i = = 0 ) | | ( i > = m_horizonsEphemerides . size ( ) - 2 ) )
{
rdJ2000 . ra = linearInterpolate ( m_horizonsEphemerides [ i ] . m_ra , m_horizonsEphemerides [ i + 1 ] . m_ra , mu ) ;
rdJ2000 . dec = linearInterpolate ( m_horizonsEphemerides [ i ] . m_dec , m_horizonsEphemerides [ i + 1 ] . m_dec , mu ) ;
}
else
{
rdJ2000 . ra = cubicInterpolate ( m_horizonsEphemerides [ i - 1 ] . m_ra , m_horizonsEphemerides [ i ] . m_ra , m_horizonsEphemerides [ i + 1 ] . m_ra , m_horizonsEphemerides [ i + 2 ] . m_ra , mu ) ;
rdJ2000 . dec = cubicInterpolate ( m_horizonsEphemerides [ i - 1 ] . m_dec , m_horizonsEphemerides [ i ] . m_dec , m_horizonsEphemerides [ i + 1 ] . m_dec , m_horizonsEphemerides [ i + 2 ] . m_dec , mu ) ;
}
return true ;
}
}
}
return false ;
}
2021-01-13 20:51:38 +00:00
void StarTrackerWorker : : update ( )
{
AzAlt aa , sunAA , moonAA ;
2026-04-03 20:33:00 +01:00
RADec rdJnow , sunRD , moonRD ;
bool rdJnowValid = false ;
2021-10-12 11:07:56 +01:00
double l , b ;
bool lbTarget = false ;
2026-04-03 20:33:00 +01:00
bool horizonsTarget = false ;
2021-01-13 20:51:38 +00:00
QDateTime dt ;
2026-04-03 20:33:00 +01:00
const QStringList raDecTargets = {
" Custom RA/Dec " , " PSR B0329+54 " , " PSR B0833-45 " , " Sagittarius A " , " Cassiopeia A " , " Cygnus A " , " Taurus A (M1) " , " Virgo A (M87) "
} ;
const QStringList lbTargets = {
" Custom l/b " , " S7 " , " S8 " , " S9 "
} ;
PROFILER_START ( ) ;
spiceLock ( SpiceEphemerides : : getEphemeridesFiles ( m_settings . m_spiceEphemerides ) ) ;
2021-01-13 20:51:38 +00:00
// Get date and time to calculate position at
2026-04-03 20:33:00 +01:00
dt = m_settings . getDateTime ( ) ;
2021-01-13 20:51:38 +00:00
// Calculate position
2021-10-12 11:07:56 +01:00
if ( ( m_settings . m_target = = " Sun " ) | | m_settings . m_drawSunOnMap | | m_settings . m_drawSunOnSkyTempChart )
{
2021-01-13 20:51:38 +00:00
Astronomy : : sunPosition ( sunAA , sunRD , m_settings . m_latitude , m_settings . m_longitude , dt ) ;
2021-10-12 11:07:56 +01:00
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportRADec : : create ( sunRD . ra , sunRD . dec , " sun " ) ) ;
}
if ( ( m_settings . m_target = = " Moon " ) | | m_settings . m_drawMoonOnMap | | m_settings . m_drawMoonOnSkyTempChart )
{
2021-01-13 20:51:38 +00:00
Astronomy : : moonPosition ( moonAA , moonRD , m_settings . m_latitude , m_settings . m_longitude , dt ) ;
2021-10-12 11:07:56 +01:00
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportRADec : : create ( moonRD . ra , moonRD . dec , " moon " ) ) ;
}
2021-01-13 20:51:38 +00:00
2026-04-03 20:33:00 +01:00
if ( ( m_settings . m_target = = " Sun " ) & & ( m_settings . m_targetSource = = " SDRangel " ) )
2023-05-23 22:32:26 +01:00
{
2026-04-03 20:33:00 +01:00
rdJnow = sunRD ;
rdJnowValid = true ;
2021-10-12 11:07:56 +01:00
aa = sunAA ;
2026-04-03 20:33:00 +01:00
RADec rdJ2000 = Astronomy : : precess ( rdJnow , Astronomy : : julianDate ( dt ) , Astronomy : : jd_j2000 ( ) ) ;
Astronomy : : equatorialToGalactic ( rdJ2000 . ra , rdJ2000 . dec , l , b ) ;
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
else if ( ( m_settings . m_target = = " Moon " ) & & ( m_settings . m_targetSource = = " SDRangel " ) )
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
rdJnow = moonRD ;
rdJnowValid = true ;
2021-10-12 11:07:56 +01:00
aa = moonAA ;
2026-04-03 20:33:00 +01:00
RADec rdJ2000 = Astronomy : : precess ( rdJnow , Astronomy : : julianDate ( dt ) , Astronomy : : jd_j2000 ( ) ) ;
Astronomy : : equatorialToGalactic ( rdJ2000 . ra , rdJ2000 . dec , l , b ) ;
}
else if ( m_settings . m_target . contains ( " SatelliteTracker " ) )
{
// Get Az/El from Satellite Tracker
double azimuth , elevation ;
if ( getAzElFromSatelliteTracker ( azimuth , elevation ) )
{
m_settings . m_el = elevation ;
m_settings . m_az = azimuth ;
// Convert Alt/Az to RA/Dec and l/b
aa . alt = m_settings . m_el ;
aa . az = m_settings . m_az ;
rdJnow = Astronomy : : azAltToRaDec ( aa , m_settings . m_latitude , m_settings . m_longitude , dt ) ;
rdJnowValid = true ;
RADec rdJ2000 = Astronomy : : precess ( rdJnow , Astronomy : : julianDate ( dt ) , Astronomy : : jd_j2000 ( ) ) ;
Astronomy : : equatorialToGalactic ( rdJ2000 . ra , rdJ2000 . dec , l , b ) ;
}
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
else if ( m_settings . m_target = = " Custom Az/El " )
2021-05-24 12:34:38 +01:00
{
2026-04-03 20:33:00 +01:00
// Convert Alt/Az to RA/Dec and l/b
2021-05-24 12:34:38 +01:00
aa . alt = m_settings . m_el ;
aa . az = m_settings . m_az ;
2026-04-03 20:33:00 +01:00
rdJnow = Astronomy : : azAltToRaDec ( aa , m_settings . m_latitude , m_settings . m_longitude , dt ) ;
rdJnowValid = true ;
RADec rdJ2000 = Astronomy : : precess ( rdJnow , Astronomy : : julianDate ( dt ) , Astronomy : : jd_j2000 ( ) ) ;
Astronomy : : equatorialToGalactic ( rdJ2000 . ra , rdJ2000 . dec , l , b ) ;
}
else if ( lbTargets . contains ( m_settings . m_target ) )
2021-10-12 11:07:56 +01:00
{
// Convert l/b to RA/Dec, then Alt/Az
l = m_settings . m_l ;
b = m_settings . m_b ;
2026-04-03 20:33:00 +01:00
RADec rdJ2000 ;
Astronomy : : galacticToEquatorial ( l , b , rdJ2000 . ra , rdJ2000 . dec ) ;
aa = Astronomy : : raDecToAzAlt ( rdJ2000 , m_settings . m_latitude , m_settings . m_longitude , dt , true ) ;
2021-10-12 11:07:56 +01:00
lbTarget = true ;
2026-04-03 20:33:00 +01:00
rdJnow = Astronomy : : precess ( rdJ2000 , Astronomy : : jd_j2000 ( ) , Astronomy : : julianDate ( dt ) ) ;
rdJnowValid = true ;
2021-05-24 12:34:38 +01:00
}
2026-04-03 20:33:00 +01:00
else if ( m_settings . m_targetSource = = " SPICE " )
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
// Calculate Alt/Az and RA/Dec with SPICE, then convert to l/b
double et ;
2021-01-13 20:51:38 +00:00
2026-04-03 20:33:00 +01:00
if ( dateTimeToET ( dt , et ) )
{
getAzElFromSPICE ( m_settings . m_target , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , aa . az , aa . alt ) ;
RADec rdJ2000 ;
if ( getRADecFromSPICE ( m_settings . m_target , et , rdJ2000 . ra , rdJ2000 . dec ) )
{
rdJnow = Astronomy : : precess ( rdJ2000 , Astronomy : : jd_j2000 ( ) , Astronomy : : julianDate ( dt ) ) ;
rdJnowValid = true ;
Astronomy : : equatorialToGalactic ( rdJ2000 . ra , rdJ2000 . dec , l , b ) ;
}
2021-10-12 11:07:56 +01:00
}
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
else
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
RADec rdJ2000 ;
if ( m_settings . m_target . contains ( " SkyMap " ) )
{
// Get RA/Dec from Sky Map
if ( getRAFromSkyMap ( rdJ2000 ) )
{
m_settings . m_ra = QString : : number ( rdJ2000 . ra , ' f ' , 10 ) ; ;
m_settings . m_dec = QString : : number ( rdJ2000 . dec , ' f ' , 10 ) ; ;
rdJnow = Astronomy : : precess ( rdJ2000 , Astronomy : : jd_j2000 ( ) , Astronomy : : julianDate ( dt ) ) ;
rdJnowValid = true ;
}
}
else if ( ( m_settings . m_targetSource = = " Horizons " ) & & ! raDecTargets . contains ( m_settings . m_target ) )
{
// Get RA/Dec from Horizons
if ( getRAFromHorizons ( dt , rdJ2000 ) )
{
m_settings . m_ra = QString : : number ( rdJ2000 . ra , ' f ' , 10 ) ; ;
m_settings . m_dec = QString : : number ( rdJ2000 . dec , ' f ' , 10 ) ; ;
rdJnow = Astronomy : : precess ( rdJ2000 , Astronomy : : jd_j2000 ( ) , Astronomy : : julianDate ( dt ) ) ;
rdJnowValid = true ;
horizonsTarget = true ;
}
}
else if ( ( m_settings . m_target = = " Custom RA/Dec " ) & & m_settings . m_jnow )
{
rdJnow . ra = Units : : raToDecimal ( m_settings . m_ra ) ;
rdJnow . dec = Units : : decToDecimal ( m_settings . m_dec ) ;
rdJnowValid = true ;
rdJ2000 = Astronomy : : precess ( rdJnow , Astronomy : : julianDate ( dt ) , Astronomy : : jd_j2000 ( ) ) ;
}
else
{
rdJ2000 . ra = Units : : raToDecimal ( m_settings . m_ra ) ;
rdJ2000 . dec = Units : : decToDecimal ( m_settings . m_dec ) ;
rdJnowValid = true ;
rdJnow = Astronomy : : precess ( rdJ2000 , Astronomy : : jd_j2000 ( ) , Astronomy : : julianDate ( dt ) ) ;
}
if ( rdJnowValid )
{
aa = Astronomy : : raDecToAzAlt ( rdJnow , m_settings . m_latitude , m_settings . m_longitude , dt , false ) ;
Astronomy : : equatorialToGalactic ( rdJ2000 . ra , rdJ2000 . dec , l , b ) ;
2021-10-12 11:07:56 +01:00
}
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
if ( rdJnowValid )
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
// Precess to J2000
RADec rdJ2000 = Astronomy : : precess ( rdJnow , Astronomy : : julianDate ( dt ) , Astronomy : : jd_j2000 ( ) ) ;
// Send to Stellarium
writeStellariumTarget ( rdJ2000 . ra , rdJ2000 . dec ) ;
// Send to GUI
if ( ( m_settings . m_target = = " Sun " ) | | ( m_settings . m_target = = " Moon " ) | | ( m_settings . m_target = = " Custom Az/El " )
| | m_settings . m_target . contains ( " SatelliteTracker " ) | | m_settings . m_target . contains ( " SkyMap " )
| | lbTarget | | horizonsTarget | | ( m_settings . m_targetSource = = " SPICE " ) )
{
if ( getMessageQueueToGUI ( ) )
{
if ( m_settings . m_jnow ) {
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportRADec : : create ( rdJnow . ra , rdJnow . dec , " target " ) ) ;
} else {
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportRADec : : create ( rdJ2000 . ra , rdJ2000 . dec , " target " ) ) ;
}
}
2021-05-24 12:34:38 +01:00
}
2026-04-03 20:33:00 +01:00
// Adjust for refraction
if ( m_settings . m_refraction = = " Positional Astronomy Library " )
{
aa . alt + = Astronomy : : refractionPAL ( aa . alt , m_settings . m_pressure , m_settings . m_temperature , m_settings . m_humidity ,
m_settings . m_frequency , m_settings . m_latitude , m_settings . m_heightAboveSeaLevel ,
m_settings . m_temperatureLapseRate ) ;
if ( aa . alt > 90.0 ) {
aa . alt = 90.0f ;
}
}
else if ( m_settings . m_refraction = = " Saemundsson " )
{
aa . alt + = Astronomy : : refractionSaemundsson ( aa . alt , m_settings . m_pressure , m_settings . m_temperature ) ;
if ( aa . alt > 90.0 ) {
aa . alt = 90.0f ;
}
2021-10-12 11:07:56 +01:00
}
2021-01-13 20:51:38 +00:00
2026-04-03 20:33:00 +01:00
// Add user-adjustment
aa . alt + = m_settings . m_elevationOffset ;
aa . az + = m_settings . m_azimuthOffset ;
2021-01-13 20:51:38 +00:00
2026-04-03 20:33:00 +01:00
// Send to GUI
if ( getMessageQueueToGUI ( ) )
2022-03-28 20:12:25 +02:00
{
2026-04-03 20:33:00 +01:00
// MsgReportRADec sent in updateRaDec()
if ( m_settings . m_target ! = " Custom Az/El " ) {
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportAzAl : : create ( aa . az , aa . alt ) ) ;
}
if ( ! lbTarget ) {
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportGalactic : : create ( l , b ) ) ;
}
2021-10-12 11:07:56 +01:00
}
2026-04-03 20:33:00 +01:00
// Send Az/El to Rotator Controllers
// Unless we're receiving settings to display from a Radio Astronomy plugins
if ( ! m_settings . m_link )
{
QList < ObjectPipe * > rotatorPipes ;
MainCore : : instance ( ) - > getMessagePipes ( ) . getMessagePipes ( m_starTracker , " target " , rotatorPipes ) ;
2021-10-12 11:07:56 +01:00
2026-04-03 20:33:00 +01:00
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 ( aa . az ) ;
swgTarget - > setElevation ( aa . alt ) ;
messageQueue - > push ( MainCore : : MsgTargetAzimuthElevation : : create ( m_starTracker , swgTarget ) ) ;
}
}
// Send Az/El, RA/Dec and Galactic b/l to Radio Astronomy plugins
// Unless we're receiving settings to display from a Radio Astronomy plugins
if ( ! m_settings . m_link )
2022-03-27 08:01:02 +02:00
{
2026-04-03 20:33:00 +01:00
QList < ObjectPipe * > starTrackerPipes ;
MainCore : : instance ( ) - > getMessagePipes ( ) . getMessagePipes ( m_starTracker , " startracker.target " , starTrackerPipes ) ;
for ( const auto & pipe : starTrackerPipes )
{
MessageQueue * messageQueue = qobject_cast < MessageQueue * > ( pipe - > m_element ) ;
SWGSDRangel : : SWGStarTrackerTarget * swgTarget = new SWGSDRangel : : SWGStarTrackerTarget ( ) ;
swgTarget - > setName ( new QString ( m_settings . m_target ) ) ;
swgTarget - > setAzimuth ( aa . az ) ;
swgTarget - > setElevation ( aa . alt ) ;
swgTarget - > setRa ( rdJnow . ra ) ;
swgTarget - > setDec ( rdJnow . dec ) ;
swgTarget - > setB ( b ) ;
swgTarget - > setL ( l ) ;
swgTarget - > setSolarFlux ( m_solarFlux ) ;
swgTarget - > setAirTemperature ( m_settings . m_temperature ) ;
double temp ;
m_starTracker - > calcSkyTemperature ( m_settings . m_frequency , m_settings . m_beamwidth , rdJnow . ra , rdJnow . dec , temp ) ;
swgTarget - > setSkyTemperature ( temp ) ;
swgTarget - > setHpbw ( m_settings . m_beamwidth ) ;
// Calculate velocities
double vRot = Astronomy : : earthRotationVelocity ( rdJnow , m_settings . m_latitude , m_settings . m_longitude , dt ) ;
swgTarget - > setEarthRotationVelocity ( vRot ) ;
double vOrbit = Astronomy : : earthOrbitVelocityBCRS ( rdJnow , dt ) ;
swgTarget - > setEarthOrbitVelocityBcrs ( vOrbit ) ;
double vLSRK = Astronomy : : sunVelocityLSRK ( rdJnow ) ;
swgTarget - > setSunVelocityLsr ( vLSRK ) ;
messageQueue - > push ( MainCore : : MsgStarTrackerTarget : : create ( m_starTracker , swgTarget ) ) ;
}
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
// Send RA/Dec, position, beamwidth and date to Sky Map
QList < ObjectPipe * > skyMapPipes ;
MainCore : : instance ( ) - > getMessagePipes ( ) . getMessagePipes ( m_starTracker , " skymap.target " , skyMapPipes ) ;
for ( const auto & pipe : skyMapPipes )
2024-02-14 13:20:33 +00:00
{
2026-04-03 20:33:00 +01:00
MessageQueue * messageQueue = qobject_cast < MessageQueue * > ( pipe - > m_element ) ;
SWGSDRangel : : SWGSkyMapTarget * swgTarget = new SWGSDRangel : : SWGSkyMapTarget ( ) ;
RADec rdJ2000 = Astronomy : : precess ( rdJnow , Astronomy : : julianDate ( dt ) , Astronomy : : jd_j2000 ( ) ) ;
2024-02-14 13:20:33 +00:00
swgTarget - > setRa ( rdJ2000 . ra ) ;
swgTarget - > setDec ( rdJ2000 . dec ) ;
2026-04-03 20:33:00 +01:00
swgTarget - > setLatitude ( m_settings . m_latitude ) ;
swgTarget - > setLongitude ( m_settings . m_longitude ) ;
swgTarget - > setAltitude ( m_settings . m_heightAboveSeaLevel ) ;
swgTarget - > setDateTime ( new QString ( dt . toString ( Qt : : ISODateWithMs ) ) ) ;
swgTarget - > setHpbw ( m_settings . m_beamwidth ) ;
messageQueue - > push ( MainCore : : MsgSkyMapTarget : : create ( m_starTracker , swgTarget ) ) ;
}
// Send to Map
if ( m_settings . m_drawSunOnMap | | m_settings . m_drawMoonOnMap | | m_settings . m_drawStarOnMap )
{
QList < ObjectPipe * > mapMessagePipes ;
MainCore : : instance ( ) - > getMessagePipes ( ) . getMessagePipes ( m_starTracker , " mapitems " , mapMessagePipes ) ;
if ( mapMessagePipes . size ( ) > 0 )
{
// Different between GMST(Lst at Greenwich) and RA
double lst = Astronomy : : localSiderealTime ( dt , 0.0 ) ;
double sunLongitude ;
double sunLatitude ;
if ( m_settings . m_drawSunOnMap | | m_settings . m_drawMoonOnMap )
{
sunLongitude = Astronomy : : lstAndRAToLongitude ( lst , sunRD . ra ) ;
sunLatitude = sunRD . dec ;
sendToMap ( mapMessagePipes , " Sun " , " qrc:///startracker/startracker/sun-40.png " , " Sun " , sunLatitude , sunLongitude ) ;
}
if ( m_settings . m_drawMoonOnMap )
{
double moonLongitude = Astronomy : : lstAndRAToLongitude ( lst , moonRD . ra ) ;
double moonLatitude = moonRD . dec ;
double moonRotation ;
QString phase = moonPhase ( sunLongitude , moonLongitude , m_settings . m_latitude , moonRotation ) ;
sendToMap ( mapMessagePipes , " Moon " , QString ( " qrc:///startracker/startracker/moon-%1-32 " ) . arg ( phase ) , " Moon " ,
moonLatitude , moonLongitude , moonRotation ) ;
}
if ( ( m_settings . m_drawStarOnMap ) & & ( m_settings . m_target ! = " Sun " ) & & ( m_settings . m_target ! = " Moon " ) )
{
double starLongitude = Astronomy : : lstAndRAToLongitude ( lst , rdJnow . ra ) ;
double starLatitude = rdJnow . dec ;
QString text = m_settings . m_target . startsWith ( " Custom " ) ? " Star " : m_settings . m_target ;
sendToMap ( mapMessagePipes , " Star " , " qrc:///startracker/startracker/pulsar-32.png " , text , starLatitude , starLongitude ) ;
}
}
}
if ( m_settings . m_chartSelect = = StarTrackerSettings : : CHART_SOLAR_SYSTEM ) {
calculateSolarSystemPositions ( dt ) ;
}
if ( m_settings . m_chartSelect = = StarTrackerSettings : : CHART_JUPITER ) {
calculateJupiterParameters ( dt ) ;
}
// Calculate az/el of target over the current day for plotting on chart
// Can't do this for satellite tracker, as we only have current az/el
// Values from SkyMap map not be valid if object is moving (E.g. planets), as ra/dec is for one point in time
// Start at midnight or noon
QDateTime lt = dt . toLocalTime ( ) ;
if ( m_settings . m_night )
{
if ( lt . time ( ) < QTime ( 12 , 0 ) ) {
dt = QDateTime ( lt . date ( ) . addDays ( - 1 ) , QTime ( 12 , 0 ) ) ;
} else {
dt = QDateTime ( lt . date ( ) , QTime ( 12 , 0 ) ) ;
}
2024-02-14 13:20:33 +00:00
}
else
{
2026-04-03 20:33:00 +01:00
dt = QDateTime ( lt . date ( ) , QTime ( 0 , 0 ) ) ;
}
//dt.setTimeZone(QTimeZone::LocalTime());
if ( ( m_settings . m_target ! = m_chartTarget )
| | ( m_settings . m_latitude ! = m_chartLatitude ) | | ( m_settings . m_longitude ! = m_chartLongitude )
| | ( dt ! = m_chartStartTime )
| | ( m_settings . m_target . startsWith ( " Custom " ) )
| | ( ( raDecTargets . contains ( m_settings . m_target ) | | m_settings . m_target . contains ( " SkyMap " ) ) // When switching these targets, we get separate settings updates for RA and Dec, so need to redraw
& & ( ( m_settings . m_ra ! = m_chartRA ) | | ( m_settings . m_dec ! = m_chartDec ) ) // We don't want to redraw when RA/Dec for Sun/Moon changes though, as that happens continuously
)
| | ( lbTargets . contains ( m_settings . m_target )
& & ( ( m_settings . m_l ! = m_chartL ) | | ( m_settings . m_b ! = m_chartB ) )
)
)
{
m_chartTarget = m_settings . m_target ;
m_chartLatitude = m_settings . m_latitude ;
m_chartLongitude = m_settings . m_longitude ;
m_chartStartTime = dt ;
m_chartRA = m_settings . m_ra ;
m_chartDec = m_settings . m_dec ;
m_chartL = m_settings . m_l ;
m_chartB = m_settings . m_b ;
if ( ! m_settings . m_target . contains ( " SatelliteTracker " ) )
{
int timestep = 5 * 60 ; // 5 mins
QList < double > azimuths ;
QList < double > elevations ;
QList < QDateTime > dateTimes ;
for ( int step = 0 ; step < = 24 * 60 * 60 / timestep ; step + + )
{
AzAlt aa ;
RADec rdJnow , rdJ2000 ;
// Calculate elevation of desired object
if ( ( m_settings . m_target = = " Sun " ) & & ( m_settings . m_targetSource = = " SDRangel " ) )
{
Astronomy : : sunPosition ( aa , rdJnow , m_settings . m_latitude , m_settings . m_longitude , dt ) ;
}
else if ( ( m_settings . m_target = = " Moon " ) & & ( m_settings . m_targetSource = = " SDRangel " ) )
{
Astronomy : : moonPosition ( aa , rdJnow , m_settings . m_latitude , m_settings . m_longitude , dt ) ;
}
else if ( m_settings . m_target = = " Custom Az/El " )
{
aa . alt = m_settings . m_el ;
aa . az = m_settings . m_az ;
}
else if ( lbTargets . contains ( m_settings . m_target ) )
{
Astronomy : : galacticToEquatorial ( m_settings . m_l , m_settings . m_b , rdJ2000 . ra , rdJ2000 . dec ) ;
aa = Astronomy : : raDecToAzAlt ( rdJ2000 , m_settings . m_latitude , m_settings . m_longitude , dt , true ) ;
}
else if ( m_settings . m_targetSource = = " SPICE " )
{
double et ;
if ( dateTimeToET ( dt , et ) ) {
getAzElFromSPICE ( m_settings . m_target , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , aa . az , aa . alt ) ;
}
}
else
{
if ( m_settings . m_target . contains ( " SkyMap " ) )
{
getRAFromSkyMap ( rdJ2000 ) ;
}
else if ( ( m_settings . m_targetSource = = " Horizons " ) & & ! raDecTargets . contains ( m_settings . m_target ) )
{
getRAFromHorizons ( dt , rdJ2000 ) ;
}
else if ( ( m_settings . m_target = = " Custom RA/Dec " ) & & m_settings . m_jnow )
{
rdJnow . ra = Units : : raToDecimal ( m_settings . m_ra ) ;
rdJnow . dec = Units : : decToDecimal ( m_settings . m_dec ) ;
rdJ2000 = Astronomy : : precess ( rdJnow , Astronomy : : julianDate ( dt ) , Astronomy : : jd_j2000 ( ) ) ;
}
else
{
rdJ2000 . ra = Units : : raToDecimal ( m_settings . m_ra ) ;
rdJ2000 . dec = Units : : decToDecimal ( m_settings . m_dec ) ;
}
aa = Astronomy : : raDecToAzAlt ( rdJ2000 , m_settings . m_latitude , m_settings . m_longitude , dt , ! m_settings . m_jnow ) ;
}
azimuths . append ( aa . az ) ;
elevations . append ( aa . alt ) ;
dateTimes . append ( dt ) ;
dt = dt . addSecs ( timestep ) ; // addSecs accounts for daylight savings jumps
}
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportAzElVsTime : : create ( m_settings . m_target , azimuths , elevations , dateTimes ) ) ;
}
2024-02-14 13:20:33 +00:00
}
}
2026-04-03 20:33:00 +01:00
spiceUnlock ( ) ;
PROFILER_STOP ( " StarTrackerWorker " ) ;
}
void StarTrackerWorker : : calculateSolarSystemPositions ( const QDateTime & dateTime )
{
QStringList bodyNames ;
QList < QVector3D > bodyPositions ;
2026-04-07 18:21:28 +01:00
QList < QList < QPointF > > orbits ;
static const QStringList planets = {
" MERCURY " , " VENUS " , " EARTH " , " MARS " , " JUPITER " , " SATURN " , " URANUS " , " NEPTUNE " ,
" MERCURY BARYCENTER " , " VENUS BARYCENTER " , " EARTH BARYCENTER " , " MARS BARYCENTER " , " JUPITER BARYCENTER " , " SATURN BARYCENTER " , " URANUS BARYCENTER " , " NEPTUNE BARYCENTER "
} ;
2026-04-03 20:33:00 +01:00
double et ;
if ( ! dateTimeToET ( dateTime , et ) ) {
return ;
}
for ( const auto & body : m_settings . m_solarSystemBodies )
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
QVector3D positionKm ;
2022-03-28 20:12:25 +02:00
2026-04-07 18:21:28 +01:00
if ( spicePosition ( body , et , positionKm ) )
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
QVector3D position ( positionKm [ 0 ] , positionKm [ 1 ] , positionKm [ 2 ] ) ;
2021-01-13 20:51:38 +00:00
2026-04-03 20:33:00 +01:00
bodyNames . append ( body ) ;
bodyPositions . append ( position ) ;
2026-04-07 18:21:28 +01:00
QList < QPointF > orbit ;
if ( planets . contains ( body ) ) {
spiceOrbit ( body , et , orbit ) ;
}
orbits . append ( orbit ) ;
2026-04-03 20:33:00 +01:00
}
}
if ( getMessageQueueToGUI ( ) ) {
2026-04-07 18:21:28 +01:00
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportSolarSystemPositions : : create ( dateTime , bodyNames , bodyPositions , orbits ) ) ;
2026-04-03 20:33:00 +01:00
}
}
void StarTrackerWorker : : calculateJupiterParameters ( const QDateTime & dateTime )
{
double cml ;
double ioPhase ;
double ganymedePhase ;
double et ;
double az , el ;
bool jupiterVisibleNow = false ;
QDateTime dt ;
bool done ;
QString moon = m_settings . m_chartSubSelect = = 0 ? " IO " : " GANYMEDE " ;
double moonPhase ;
QList < StarTrackerReport : : JupiterData > jupiterData ;
QList < StarTrackerReport : : JupiterMoonData > moonData ;
PROFILER_START ( ) ;
if ( dateTimeToET ( dateTime , et ) )
{
// Calculate Alt/El of Jupiter
if ( getAzElFromSPICE ( " JUPITER " , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , az , el ) )
{
// Calculate Jupiter CML and phase of Moons
2026-04-07 18:21:28 +01:00
if ( spiceJupiterMoonPhase ( " IO " , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , cml , ioPhase ) )
2021-01-13 20:51:38 +00:00
{
2026-04-07 18:21:28 +01:00
if ( spiceJupiterMoonPhase ( " GANYMEDE " , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , cml , ganymedePhase ) )
2026-04-03 20:33:00 +01:00
{
if ( getMessageQueueToGUI ( ) ) {
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportJupiter : : create ( dateTime , az , el , cml , ioPhase , ganymedePhase ) ) ;
}
}
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
jupiterVisibleNow = el > 0.0 ;
}
}
if ( jupiterVisibleNow )
{
// Step backwards in time
dt = dateTime ;
dt . setTime ( QTime ( dateTime . time ( ) . hour ( ) , 0 ) ) ;
done = false ;
while ( ! done )
{
if ( dateTimeToET ( dt , et ) )
{
if ( getAzElFromSPICE ( " JUPITER " , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , az , el ) )
{
if ( el > 0.0 )
{
2026-04-07 18:21:28 +01:00
if ( spiceJupiterMoonPhase ( moon , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , cml , moonPhase ) )
2026-04-03 20:33:00 +01:00
{
StarTrackerReport : : JupiterData jd = { dt , az , el } ;
StarTrackerReport : : JupiterMoonData md = { cml , moonPhase } ;
jupiterData . push_front ( jd ) ;
moonData . push_front ( md ) ;
}
}
else
{
done = true ;
}
}
}
dt = dt . addSecs ( - 60 * 60 ) ;
}
// Step forwards in time
dt = dateTime ;
dt . setTime ( QTime ( dateTime . time ( ) . hour ( ) , 0 ) ) ;
done = false ;
while ( ! done )
{
dt = dt . addSecs ( 60 * 60 ) ;
if ( dateTimeToET ( dt , et ) )
{
if ( getAzElFromSPICE ( " JUPITER " , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , az , el ) )
{
if ( el > 0.0 )
{
2026-04-07 18:21:28 +01:00
if ( spiceJupiterMoonPhase ( moon , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , cml , moonPhase ) )
2026-04-03 20:33:00 +01:00
{
StarTrackerReport : : JupiterData jd = { dt , az , el } ;
StarTrackerReport : : JupiterMoonData id = { cml , moonPhase } ;
jupiterData . push_back ( jd ) ;
moonData . push_back ( id ) ;
}
}
else
{
done = true ;
}
}
}
}
}
else
{
// Step forwards in time until Jupiter is visible
dt = dateTime ;
dt . setTime ( QTime ( dateTime . time ( ) . hour ( ) , 0 ) ) ;
done = false ;
while ( ! done )
{
dt = dt . addSecs ( 60 * 60 ) ;
if ( dateTimeToET ( dt , et ) )
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
if ( getAzElFromSPICE ( " JUPITER " , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , az , el ) )
{
if ( el > 0.0 ) {
done = true ;
}
}
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
}
// Continue stepping forward until no longer visible
done = false ;
while ( ! done )
{
if ( dateTimeToET ( dt , et ) )
2021-01-13 20:51:38 +00:00
{
2026-04-03 20:33:00 +01:00
if ( getAzElFromSPICE ( " JUPITER " , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , az , el ) )
{
if ( el > 0.0 )
{
2026-04-07 18:21:28 +01:00
if ( spiceJupiterMoonPhase ( moon , et , m_settings . m_latitude , m_settings . m_longitude , 0.0 , cml , moonPhase ) )
2026-04-03 20:33:00 +01:00
{
StarTrackerReport : : JupiterData jd = { dt , az , el } ;
StarTrackerReport : : JupiterMoonData id = { cml , moonPhase } ;
jupiterData . push_back ( jd ) ;
moonData . push_back ( id ) ;
}
}
else
{
done = true ;
}
}
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
dt = dt . addSecs ( 60 * 60 ) ;
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
}
if ( getMessageQueueToGUI ( ) ) {
getMessageQueueToGUI ( ) - > push ( StarTrackerReport : : MsgReportJupiterData : : create ( jupiterData , moonData ) ) ;
2021-01-13 20:51:38 +00:00
}
2026-04-03 20:33:00 +01:00
PROFILER_STOP ( " calculateJupiterParameters " ) ;
}
void StarTrackerWorker : : horizonsEphemeridesUpdated ( const QString & target , const QList < JPLHorizons : : Ephemeris > & ephemerides )
{
m_ephemeridesTarget = target ;
m_horizonsEphemerides = ephemerides ;
2021-01-13 20:51:38 +00:00
}