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 "SatellitesModel.h" 0007 0008 #include "MarbleDebug.h" 0009 #include "MarbleDirs.h" 0010 #include "SatellitesMSCItem.h" 0011 #include "SatellitesTLEItem.h" 0012 0013 #include "MarbleClock.h" 0014 #include "MarbleColors.h" 0015 #include "GeoDataPlacemark.h" 0016 #include "GeoDataStyle.h" 0017 #include "GeoDataIconStyle.h" 0018 #include "GeoDataLabelStyle.h" 0019 #include "GeoDataLineStyle.h" 0020 0021 #include <planetarySats.h> 0022 #include <sgp4io.h> 0023 0024 #include <clocale> 0025 0026 namespace Marble { 0027 0028 SatellitesModel::SatellitesModel( GeoDataTreeModel *treeModel, 0029 const MarbleClock *clock ) 0030 : TrackerPluginModel( treeModel ), 0031 m_clock( clock ), 0032 m_currentColorIndex( 0 ) 0033 { 0034 setupColors(); 0035 connect(m_clock, SIGNAL(timeChanged()), this, SLOT(update())); 0036 } 0037 0038 void SatellitesModel::setupColors() 0039 { 0040 m_colorList.push_back( Oxygen::brickRed4 ); 0041 m_colorList.push_back( Oxygen::raspberryPink4 ); 0042 m_colorList.push_back( Oxygen::burgundyPurple4 ); 0043 m_colorList.push_back( Oxygen::grapeViolet4 ); 0044 m_colorList.push_back( Oxygen::skyBlue4 ); 0045 m_colorList.push_back( Oxygen::seaBlue4 ); 0046 m_colorList.push_back( Oxygen::emeraldGreen4 ); 0047 m_colorList.push_back( Oxygen::forestGreen4 ); 0048 m_colorList.push_back( Oxygen::sunYellow4 ); 0049 m_colorList.push_back( Oxygen::hotOrange4 ); 0050 m_colorList.push_back( Oxygen::aluminumGray4 ); 0051 m_colorList.push_back( Oxygen::woodBrown4 ); 0052 } 0053 0054 QColor SatellitesModel::nextColor() 0055 { 0056 if (m_colorList.isEmpty()) { 0057 return Oxygen::brickRed4; 0058 } 0059 if (m_currentColorIndex < m_colorList.size()) { 0060 m_currentColorIndex++; 0061 return m_colorList[m_currentColorIndex-1]; 0062 } else { 0063 m_currentColorIndex = 1; 0064 return m_colorList[0]; 0065 } 0066 return Oxygen::brickRed4; 0067 } 0068 0069 void SatellitesModel::loadSettings( const QHash<QString, QVariant> &settings ) 0070 { 0071 QStringList idList = settings[QStringLiteral("idList")].toStringList(); 0072 m_enabledIds = idList; 0073 0074 updateVisibility(); 0075 } 0076 0077 void SatellitesModel::setPlanet( const QString &planetId ) 0078 { 0079 if( m_lcPlanet != planetId ) { 0080 0081 mDebug() << "Planet changed from" << m_lcPlanet << "to" << planetId; 0082 m_lcPlanet = planetId; 0083 0084 updateVisibility(); 0085 } 0086 } 0087 0088 void SatellitesModel::updateVisibility() 0089 { 0090 beginUpdateItems(); 0091 0092 for( TrackerPluginItem *obj: items() ) { 0093 SatellitesMSCItem *oItem = dynamic_cast<SatellitesMSCItem*>(obj); 0094 if( oItem != nullptr ) { 0095 bool enabled = ( ( oItem->relatedBody().toLower() == m_lcPlanet ) && 0096 ( m_enabledIds.contains( oItem->id() ) ) ); 0097 oItem->setEnabled( enabled ); 0098 0099 if( enabled ) { 0100 oItem->update(); 0101 } 0102 } 0103 0104 SatellitesTLEItem *eItem = dynamic_cast<SatellitesTLEItem*>(obj); 0105 if( eItem != nullptr ) { 0106 // TLE satellites are always earth satellites 0107 bool enabled = (m_lcPlanet == QLatin1String("earth")); 0108 eItem->setEnabled( enabled ); 0109 0110 if( enabled ) { 0111 eItem->update(); 0112 } 0113 } 0114 } 0115 0116 endUpdateItems(); 0117 } 0118 0119 void SatellitesModel::parseFile( const QString &id, 0120 const QByteArray &data ) 0121 { 0122 // catalog files are comma separated while tle files 0123 // are not allowed to contain commas, so we use this 0124 // to distinguish between those two 0125 if( data.contains( ',' ) ) { 0126 parseCatalog( id, data ); 0127 } else { 0128 parseTLE( id, data ); 0129 } 0130 0131 emit fileParsed( id ); 0132 } 0133 0134 void SatellitesModel::parseCatalog( const QString &id, 0135 const QByteArray &data ) 0136 { 0137 // For details see: 0138 // https://techbase.kde.org/Projects/Marble/SatelliteCatalogFormat 0139 0140 mDebug() << "Reading satellite catalog from:" << id; 0141 0142 QTextStream ts(data); 0143 int index = 1; 0144 0145 beginUpdateItems(); 0146 0147 QString line = ts.readLine(); 0148 for( ; !line.isNull(); line = ts.readLine() ) { 0149 0150 if (line.trimmed().startsWith(QLatin1Char('#'))) { 0151 continue; 0152 } 0153 0154 QStringList elms = line.split(", "); 0155 0156 // check for '<' instead of '==' in order to allow fields to be added 0157 // to catalogs later without breaking the code 0158 if( elms.size() < 14 ) { 0159 mDebug() << "Skipping line:" << elms << "(" << line << ")"; 0160 continue; 0161 } 0162 0163 QString name( elms[0] ); 0164 QString category( elms[1] ); 0165 QString body( elms[2] ); 0166 QByteArray body8Bit = body.toLocal8Bit(); 0167 char *cbody = const_cast<char*>( body8Bit.constData() ); 0168 0169 mDebug() << "Loading" << category << name; 0170 0171 PlanetarySats *planSat = new PlanetarySats(); 0172 planSat->setPlanet( cbody ); 0173 0174 planSat->setStateVector( 0175 elms[7].toFloat() - 2400000.5, 0176 elms[8].toFloat(), elms[9].toFloat(), elms[10].toFloat(), 0177 elms[11].toFloat(), elms[12].toFloat(), elms[13].toFloat() ); 0178 0179 planSat->stateToKepler(); 0180 0181 QDateTime missionStart, missionEnd; 0182 if( elms[3].toUInt() > 0 ) { 0183 missionStart = QDateTime::fromTime_t( elms[3].toUInt() ); 0184 } 0185 if( elms[4].toUInt() > 0 ) { 0186 missionEnd = QDateTime::fromTime_t( elms[4].toUInt() ); 0187 } 0188 0189 SatellitesMSCItem *item = new SatellitesMSCItem( name, category, body, id, 0190 missionStart, missionEnd, 0191 index++, planSat, m_clock ); 0192 GeoDataStyle::Ptr style(new GeoDataStyle( *item->placemark()->style() )); 0193 style->lineStyle().setPenStyle( Qt::SolidLine ); 0194 style->lineStyle().setColor( nextColor() ); 0195 style->labelStyle().setGlow( true ); 0196 0197 // use special icon for moons 0198 if (category == QLatin1String("Moons")) { 0199 style->iconStyle().setIconPath(QStringLiteral(":/icons/moon.png")); 0200 } else { 0201 style->iconStyle().setIconPath(MarbleDirs::path(QStringLiteral("bitmaps/satellite.png"))); 0202 } 0203 0204 item->placemark()->setStyle( style ); 0205 0206 item->placemark()->setVisible( ( body.toLower() == m_lcPlanet ) ); 0207 addItem( item ); 0208 } 0209 0210 endUpdateItems(); 0211 } 0212 0213 void SatellitesModel::parseTLE( const QString &id, 0214 const QByteArray &data ) 0215 { 0216 mDebug() << "Reading satellite TLE data from:" << id; 0217 0218 QList<QByteArray> tleLines = data.split( '\n' ); 0219 // File format: One line of description, two lines of TLE, last line is empty 0220 if ( tleLines.size() % 3 != 1 ) { 0221 mDebug() << "Malformated satellite data file"; 0222 } 0223 0224 beginUpdateItems(); 0225 0226 //FIXME: terrible hack because twoline2rv uses sscanf 0227 setlocale( LC_NUMERIC, "C" ); 0228 0229 double startmfe, stopmfe, deltamin; 0230 elsetrec satrec; 0231 int i = 0; 0232 while ( i < tleLines.size() - 1 ) { 0233 QString satelliteName = QString( tleLines.at( i++ ) ).trimmed(); 0234 char line1[130]; 0235 char line2[130]; 0236 if( tleLines.at( i ).size() >= 79 || 0237 tleLines.at( i+1 ).size() >= 79 ) { 0238 mDebug() << "Invalid TLE data!"; 0239 return; 0240 } 0241 qstrcpy( line1, tleLines.at( i++ ).constData() ); 0242 qstrcpy( line2, tleLines.at( i++ ).constData() ); 0243 twoline2rv( line1, line2, 'c', 'd', 'i', wgs84, 0244 startmfe, stopmfe, deltamin, satrec ); 0245 if ( satrec.error != 0 ) { 0246 mDebug() << "Error: " << satrec.error; 0247 return; 0248 } 0249 0250 SatellitesTLEItem *item = new SatellitesTLEItem( satelliteName, satrec, m_clock ); 0251 GeoDataStyle::Ptr style(new GeoDataStyle( *item->placemark()->style() )); 0252 style->lineStyle().setPenStyle( Qt::SolidLine ); 0253 style->lineStyle().setColor( nextColor() ); 0254 style->labelStyle().setGlow( true ); 0255 style->iconStyle().setIconPath(MarbleDirs::path(QStringLiteral("bitmaps/satellite.png"))); 0256 item->placemark()->setStyle( style ); 0257 addItem( item ); 0258 } 0259 0260 //Reset to environment 0261 setlocale( LC_NUMERIC, "" ); 0262 0263 endUpdateItems(); 0264 } 0265 0266 } // namespace Marble 0267 0268 #include "moc_SatellitesModel.cpp"