File indexing completed on 2024-04-28 03:50:24
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2011 Guillaume Martres <smarter@ubuntu.com> 0004 // 0005 0006 #include "SatellitesTLEItem.h" 0007 0008 #include "MarbleClock.h" 0009 #include "MarbleDebug.h" 0010 #include "MarbleGlobal.h" 0011 #include "GeoPainter.h" 0012 #include "GeoDataCoordinates.h" 0013 #include "GeoDataPlacemark.h" 0014 #include "GeoDataStyle.h" 0015 #include "GeoDataTrack.h" 0016 0017 #include <sgp4ext.h> 0018 0019 #include <QFile> 0020 #include <QDateTime> 0021 #include <QAction> 0022 #include <QColor> 0023 0024 #include <cmath> 0025 0026 namespace Marble { 0027 0028 SatellitesTLEItem::SatellitesTLEItem( const QString &name, 0029 elsetrec satrec, 0030 const MarbleClock *clock ) 0031 : TrackerPluginItem( name ), 0032 m_satrec( satrec ), 0033 m_track( new GeoDataTrack() ), 0034 m_clock( clock ) 0035 { 0036 double tumin, mu, xke, j2, j3, j4, j3oj2; 0037 double radiusearthkm; 0038 getgravconst( wgs84, tumin, mu, radiusearthkm, xke, j2, j3, j4, j3oj2 ); 0039 m_earthSemiMajorAxis = radiusearthkm; 0040 0041 setDescription(); 0042 0043 placemark()->setVisualCategory(GeoDataPlacemark::Satellite); 0044 placemark()->setZoomLevel( 0 ); 0045 placemark()->setGeometry( m_track ); 0046 0047 update(); 0048 } 0049 0050 void SatellitesTLEItem::setDescription() 0051 { 0052 QFile templateFile(QStringLiteral(":/marble/satellites/satellite.html")); 0053 if (!templateFile.open(QIODevice::ReadOnly)) { 0054 placemark()->setDescription(QObject::tr("No info available.")); 0055 return; 0056 } 0057 QString html = templateFile.readAll(); 0058 0059 html.replace("%name%", name()); 0060 html.replace("%noradId%", QString::number(m_satrec.satnum)); 0061 html.replace("%perigee%", QString::number(perigee(), 'f', 2)); 0062 html.replace("%apogee%", QString::number(apogee(), 'f', 2)); 0063 html.replace("%inclination%", QString::number(inclination(), 'f', 2)); 0064 html.replace("%period%", QString::number(period(), 'f', 2)); 0065 html.replace("%semiMajorAxis%", QString::number(semiMajorAxis(), 'f', 2)); 0066 0067 placemark()->setDescription( html ); 0068 } 0069 0070 void SatellitesTLEItem::update() 0071 { 0072 if( !isEnabled() ) { 0073 return; 0074 } 0075 0076 QDateTime startTime = m_clock->dateTime(); 0077 QDateTime endTime = startTime; 0078 if( isTrackVisible() ) { 0079 startTime = startTime.addSecs( -2 * 60 ); 0080 endTime = startTime.addSecs( period() ); 0081 } 0082 0083 m_track->removeBefore( startTime ); 0084 m_track->removeAfter( endTime ); 0085 0086 addPointAt( m_clock->dateTime() ); 0087 0088 // time interval between each point in the track, in seconds 0089 double step = period() / 100.0; 0090 0091 for ( double i = startTime.toTime_t(); i < endTime.toTime_t(); i += step ) { 0092 // No need to add points in this interval 0093 if ( i >= m_track->firstWhen().toTime_t() ) { 0094 i = m_track->lastWhen().toTime_t() + step; 0095 } 0096 0097 addPointAt( QDateTime::fromTime_t( i ) ); 0098 } 0099 } 0100 0101 void SatellitesTLEItem::addPointAt( const QDateTime &dateTime ) 0102 { 0103 // in minutes 0104 double timeSinceEpoch = (double)( dateTime.toTime_t() - 0105 timeAtEpoch().toTime_t() ) / 60.0; 0106 0107 double r[3], v[3]; 0108 sgp4( wgs84, m_satrec, timeSinceEpoch, r, v ); 0109 0110 GeoDataCoordinates coordinates = fromTEME( 0111 r[0], r[1], r[2], gmst( timeSinceEpoch ) ); 0112 if ( m_satrec.error != 0 ) { 0113 return; 0114 } 0115 0116 m_track->addPoint( dateTime, coordinates); 0117 } 0118 0119 QDateTime SatellitesTLEItem::timeAtEpoch() const 0120 { 0121 int year = m_satrec.epochyr + ( m_satrec.epochyr < 57 ? 2000 : 1900 ); 0122 0123 int month, day, hours, minutes; 0124 double seconds; 0125 days2mdhms( year, m_satrec.epochdays, month, day, hours , minutes, seconds ); 0126 0127 int ms = fmod(seconds * 1000.0, 1000.0); 0128 0129 return QDateTime( QDate( year, month, day ), 0130 QTime( hours, minutes, (int)seconds, ms ), 0131 Qt::UTC ); 0132 } 0133 0134 double SatellitesTLEItem::period() const 0135 { 0136 // no := mean motion (rad / min) 0137 return 60 * (2 * M_PI / m_satrec.no); 0138 } 0139 0140 double SatellitesTLEItem::apogee() const 0141 { 0142 return m_satrec.alta * m_earthSemiMajorAxis; 0143 } 0144 0145 double SatellitesTLEItem::perigee() const 0146 { 0147 return m_satrec.altp * m_earthSemiMajorAxis; 0148 } 0149 0150 double SatellitesTLEItem::semiMajorAxis() const 0151 { 0152 0153 return m_satrec.a * m_earthSemiMajorAxis; 0154 } 0155 0156 double SatellitesTLEItem::inclination() const 0157 { 0158 return m_satrec.inclo / M_PI * 180; 0159 } 0160 0161 GeoDataCoordinates SatellitesTLEItem::fromTEME( double x, 0162 double y, 0163 double z, 0164 double gmst ) const 0165 { 0166 double lon = atan2( y, x ); 0167 // Rotate the angle by gmst (the origin goes from the vernal equinox 0168 // point to the Greenwich Meridian) 0169 lon = GeoDataCoordinates::normalizeLon( fmod(lon - gmst, 2 * M_PI) ); 0170 0171 double lat = atan2( z, sqrt( x*x + y*y ) ); 0172 0173 //TODO: determine if this is worth the extra precision 0174 // Algorithm from https://celestrak.com/columns/v02n03/ 0175 //TODO: demonstrate it. 0176 double a = m_earthSemiMajorAxis; 0177 double planetRadius = sqrt( x*x + y*y ); 0178 double latp = lat; 0179 double C; 0180 for ( int i = 0; i < 3; i++ ) { 0181 C = 1 / sqrt( 1 - square( m_satrec.ecco * sin( latp ) ) ); 0182 lat = atan2( z + a * C * square( m_satrec.ecco ) * sin( latp ), planetRadius ); 0183 } 0184 0185 double alt = planetRadius / cos( lat ) - a * C; 0186 0187 lat = GeoDataCoordinates::normalizeLat( lat ); 0188 0189 return GeoDataCoordinates( lon, lat, alt * 1000 ); 0190 } 0191 0192 double SatellitesTLEItem::gmst( double minutesP ) const 0193 { 0194 // Earth rotation rate in rad/min, from sgp4io.cpp 0195 double rptim = 4.37526908801129966e-3; 0196 return fmod( m_satrec.gsto + rptim * minutesP, 2 * M_PI ); 0197 } 0198 0199 double SatellitesTLEItem::square( double x ) 0200 { 0201 return x * x; 0202 } 0203 0204 } // namespace Marble