File indexing completed on 2024-04-14 03:43:10
0001 /* 0002 SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "ksplanet.h" 0008 0009 #include "ksnumbers.h" 0010 #include "ksutils.h" 0011 #include "ksfilereader.h" 0012 0013 #include <cmath> 0014 #include <typeinfo> 0015 0016 #include "kstars_debug.h" 0017 0018 // Qt version calming 0019 #include <qtskipemptyparts.h> 0020 0021 KSPlanet::OrbitDataManager KSPlanet::odm; 0022 0023 KSPlanet::OrbitDataManager::OrbitDataManager() 0024 { 0025 //EMPTY 0026 } 0027 0028 bool KSPlanet::OrbitDataManager::readOrbitData(const QString &fname, QVector<OrbitData> *vector) 0029 { 0030 QFile f; 0031 0032 if (KSUtils::openDataFile(f, fname)) 0033 { 0034 KSFileReader fileReader(f); // close file is included 0035 QStringList fields; 0036 while (fileReader.hasMoreLines()) 0037 { 0038 fields = fileReader.readLine().split(' ', Qt::SkipEmptyParts); 0039 0040 if (fields.size() == 3) 0041 { 0042 double A = fields[0].toDouble(); 0043 double B = fields[1].toDouble(); 0044 double C = fields[2].toDouble(); 0045 vector->append(OrbitData(A, B, C)); 0046 } 0047 } 0048 } 0049 else 0050 { 0051 return false; 0052 } 0053 return true; 0054 } 0055 0056 bool KSPlanet::OrbitDataManager::loadData(KSPlanet::OrbitDataColl &odc, const QString &n) 0057 { 0058 QString fname, snum; 0059 QFile f; 0060 int nCount = 0; 0061 QString nl = n.toLower(); 0062 0063 if (hash.contains(nl)) 0064 { 0065 odc = hash[nl]; 0066 return true; //orbit data already loaded 0067 } 0068 0069 //Create a new OrbitDataColl 0070 OrbitDataColl ret; 0071 0072 //Ecliptic Longitude 0073 for (int i = 0; i < 6; ++i) 0074 { 0075 snum.setNum(i); 0076 fname = nl + ".L" + snum + ".vsop"; 0077 if (readOrbitData(fname, &ret.Lon[i])) 0078 nCount++; 0079 } 0080 0081 if (nCount == 0) 0082 return false; 0083 0084 //Ecliptic Latitude 0085 for (int i = 0; i < 6; ++i) 0086 { 0087 snum.setNum(i); 0088 fname = nl + ".B" + snum + ".vsop"; 0089 if (readOrbitData(fname, &ret.Lat[i])) 0090 nCount++; 0091 } 0092 0093 if (nCount == 0) 0094 return false; 0095 0096 //Heliocentric Distance 0097 for (int i = 0; i < 6; ++i) 0098 { 0099 snum.setNum(i); 0100 fname = nl + ".R" + snum + ".vsop"; 0101 if (readOrbitData(fname, &ret.Dst[i])) 0102 nCount++; 0103 } 0104 0105 if (nCount == 0) 0106 return false; 0107 0108 hash[nl] = ret; 0109 odc = hash[nl]; 0110 0111 return true; 0112 } 0113 0114 KSPlanet::KSPlanet(const QString &s, const QString &imfile, const QColor &c, double pSize) 0115 : KSPlanetBase(s, imfile, c, pSize) 0116 { 0117 } 0118 0119 KSPlanet::KSPlanet(int n) : KSPlanetBase() 0120 { 0121 switch (n) 0122 { 0123 case MERCURY: 0124 KSPlanetBase::init(i18n("Mercury"), "mercury", KSPlanetBase::planetColor[KSPlanetBase::MERCURY], 4879.4); 0125 break; 0126 case VENUS: 0127 KSPlanetBase::init(i18n("Venus"), "venus", KSPlanetBase::planetColor[KSPlanetBase::VENUS], 12103.6); 0128 break; 0129 case MARS: 0130 KSPlanetBase::init(i18n("Mars"), "mars", KSPlanetBase::planetColor[KSPlanetBase::MARS], 6792.4); 0131 break; 0132 case JUPITER: 0133 KSPlanetBase::init(i18n("Jupiter"), "jupiter", KSPlanetBase::planetColor[KSPlanetBase::JUPITER], 142984.); 0134 break; 0135 case SATURN: 0136 KSPlanetBase::init(i18n("Saturn"), "saturn", KSPlanetBase::planetColor[KSPlanetBase::SATURN], 120536.); 0137 break; 0138 case URANUS: 0139 KSPlanetBase::init(i18n("Uranus"), "uranus", KSPlanetBase::planetColor[KSPlanetBase::URANUS], 51118.); 0140 break; 0141 case NEPTUNE: 0142 KSPlanetBase::init(i18n("Neptune"), "neptune", KSPlanetBase::planetColor[KSPlanetBase::NEPTUNE], 49572.); 0143 break; 0144 default: 0145 qDebug() << Q_FUNC_INFO << "Error: Illegal identifier in KSPlanet constructor: " << n; 0146 break; 0147 } 0148 } 0149 0150 KSPlanet *KSPlanet::clone() const 0151 { 0152 Q_ASSERT(typeid(this) == typeid(static_cast<const KSPlanet *>(this))); // Ensure we are not slicing a derived class 0153 return new KSPlanet(*this); 0154 } 0155 0156 // TODO: Get rid of this dirty hack post KDE 4.2 release 0157 QString KSPlanet::untranslatedName() const 0158 { 0159 if (name() == i18n("Mercury")) 0160 return "Mercury"; 0161 else if (name() == i18n("Venus")) 0162 return "Venus"; 0163 else if (name() == i18n("Mars")) 0164 return "Mars"; 0165 else if (name() == i18n("Jupiter")) 0166 return "Jupiter"; 0167 else if (name() == i18n("Saturn")) 0168 return "Saturn"; 0169 else if (name() == i18n("Uranus")) 0170 return "Uranus"; 0171 else if (name() == i18n("Neptune")) 0172 return "Neptune"; 0173 else if (name() == i18n("Earth")) 0174 return "Earth"; 0175 else if (name() == i18n("Earth Shadow")) 0176 return "Earth Shadow"; 0177 else if (name() == i18n("Moon")) 0178 return "Moon"; 0179 else if (name() == i18n("Sun")) 0180 return "Sun"; 0181 else 0182 return name(); 0183 } 0184 0185 //we don't need the reference to the ODC, so just give it a junk variable 0186 bool KSPlanet::loadData() 0187 { 0188 OrbitDataColl odc; 0189 return odm.loadData(odc, untranslatedName()); 0190 } 0191 0192 void KSPlanet::calcEcliptic(double Tau, EclipticPosition &epret) const 0193 { 0194 double sum[6]; 0195 OrbitDataColl odc; 0196 double Tpow[6]; 0197 0198 Tpow[0] = 1.0; 0199 for (int i = 1; i < 6; ++i) 0200 { 0201 Tpow[i] = Tpow[i - 1] * Tau; 0202 } 0203 0204 if (!odm.loadData(odc, untranslatedName())) 0205 { 0206 epret.longitude = dms(0.0); 0207 epret.latitude = dms(0.0); 0208 epret.radius = 0.0; 0209 qCWarning(KSTARS) << "Could not get data for name:" << name() << "(" << untranslatedName() << ")"; 0210 return; 0211 } 0212 0213 //Ecliptic Longitude 0214 for (int i = 0; i < 6; ++i) 0215 { 0216 sum[i] = 0.0; 0217 for (int j = 0; j < odc.Lon[i].size(); ++j) 0218 { 0219 sum[i] += odc.Lon[i][j].A * cos(odc.Lon[i][j].B + odc.Lon[i][j].C * Tau); 0220 /* 0221 qDebug() << Q_FUNC_INFO << "sum[" << i <<"] =" << sum[i] << 0222 " A = " << odc.Lon[i][j].A << " B = " << odc.Lon[i][j].B << 0223 " C = " << odc.Lon[i][j].C; 0224 */ 0225 } 0226 sum[i] *= Tpow[i]; 0227 //qDebug() << Q_FUNC_INFO << name() << " : sum[" << i << "] = " << sum[i]; 0228 } 0229 0230 epret.longitude.setRadians(sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]); 0231 epret.longitude.setD(epret.longitude.reduce().Degrees()); 0232 0233 //Compute Ecliptic Latitude 0234 for (uint i = 0; i < 6; ++i) 0235 { 0236 sum[i] = 0.0; 0237 for (int j = 0; j < odc.Lat[i].size(); ++j) 0238 { 0239 sum[i] += odc.Lat[i][j].A * cos(odc.Lat[i][j].B + odc.Lat[i][j].C * Tau); 0240 } 0241 sum[i] *= Tpow[i]; 0242 } 0243 0244 epret.latitude.setRadians(sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]); 0245 0246 //Compute Heliocentric Distance 0247 for (uint i = 0; i < 6; ++i) 0248 { 0249 sum[i] = 0.0; 0250 for (int j = 0; j < odc.Dst[i].size(); ++j) 0251 { 0252 sum[i] += odc.Dst[i][j].A * cos(odc.Dst[i][j].B + odc.Dst[i][j].C * Tau); 0253 } 0254 sum[i] *= Tpow[i]; 0255 } 0256 0257 epret.radius = sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]; 0258 0259 /* 0260 qDebug() << Q_FUNC_INFO << name() << " pre: Lat = " << epret.latitude.toDMSString() << " Long = " << 0261 epret.longitude.toDMSString() << " Dist = " << epret.radius; 0262 */ 0263 } 0264 0265 bool KSPlanet::findGeocentricPosition(const KSNumbers *num, const KSPlanetBase *Earth) 0266 { 0267 if (Earth != nullptr) 0268 { 0269 double sinL, sinL0, sinB, sinB0; 0270 double cosL, cosL0, cosB, cosB0; 0271 double x = 0.0, y = 0.0, z = 0.0; 0272 0273 double olddst = -1000; 0274 double dst = 0; 0275 0276 EclipticPosition trialpos; 0277 0278 double jm = num->julianMillenia(); 0279 0280 Earth->ecLong().SinCos(sinL0, cosL0); 0281 Earth->ecLat().SinCos(sinB0, cosB0); 0282 0283 double eX = Earth->rsun() * cosB0 * cosL0; 0284 double eY = Earth->rsun() * cosB0 * sinL0; 0285 double eZ = Earth->rsun() * sinB0; 0286 0287 bool once = true; 0288 while (fabs(dst - olddst) > .001) 0289 { 0290 calcEcliptic(jm, trialpos); 0291 0292 // We store the heliocentric ecliptic coordinates the first time they are computed. 0293 if (once) 0294 { 0295 helEcPos = trialpos; 0296 once = false; 0297 } 0298 0299 olddst = dst; 0300 0301 trialpos.longitude.SinCos(sinL, cosL); 0302 trialpos.latitude.SinCos(sinB, cosB); 0303 0304 x = trialpos.radius * cosB * cosL - eX; 0305 y = trialpos.radius * cosB * sinL - eY; 0306 z = trialpos.radius * sinB - eZ; 0307 0308 //distance from Earth 0309 dst = sqrt(x * x + y * y + z * z); 0310 0311 //The light-travel time delay, in millenia 0312 //0.0057755183 is the inverse speed of light, 0313 //in days/AU 0314 double delay = (.0057755183 * dst) / 365250.0; 0315 0316 jm = num->julianMillenia() - delay; 0317 } 0318 0319 ep.longitude.setRadians(atan2(y, x)); 0320 ep.longitude.reduce(); 0321 ep.latitude.setRadians(atan2(z, sqrt(x * x + y * y))); 0322 setRsun(trialpos.radius); 0323 setRearth(dst); 0324 0325 EclipticToEquatorial(num->obliquity()); 0326 0327 setRA0(ra()); 0328 setDec0(dec()); 0329 apparentCoord(J2000, lastPrecessJD); 0330 0331 //nutate(num); 0332 //aberrate(num); 0333 } 0334 else 0335 { 0336 calcEcliptic(num->julianMillenia(), ep); 0337 helEcPos = ep; 0338 } 0339 0340 //determine the position angle 0341 findPA(num); 0342 0343 return true; 0344 } 0345 0346 void KSPlanet::findMagnitude(const KSNumbers *num) 0347 { 0348 double cosDec, sinDec; 0349 dec().SinCos(cosDec, sinDec); 0350 0351 /* Computation of the visual magnitude (V band) of the planet. 0352 * Algorithm provided by Pere Planesas (Observatorio Astronomico Nacional) 0353 * It has some similarity to J. Meeus algorithm in Astronomical Algorithms, Chapter 40. 0354 * */ 0355 0356 // Initialized to the faintest magnitude observable with the HST 0357 float magnitude = 30; 0358 0359 double param = 5 * log10(rsun() * rearth()); 0360 double phase = this->phase().Degrees(); 0361 double f1 = phase / 100.; 0362 0363 if (name() == i18n("Mercury")) 0364 { 0365 if (phase > 150.) 0366 f1 = 1.5; 0367 magnitude = -0.36 + param + 3.8 * f1 - 2.73 * f1 * f1 + 2 * f1 * f1 * f1; 0368 } 0369 else if (name() == i18n("Venus")) 0370 { 0371 magnitude = -4.29 + param + 0.09 * f1 + 2.39 * f1 * f1 - 0.65 * f1 * f1 * f1; 0372 } 0373 else if (name() == i18n("Mars")) 0374 { 0375 magnitude = -1.52 + param + 0.016 * phase; 0376 } 0377 else if (name() == i18n("Jupiter")) 0378 { 0379 magnitude = -9.25 + param + 0.005 * phase; 0380 } 0381 else if (name() == i18n("Saturn")) 0382 { 0383 double T = num->julianCenturies(); 0384 double a0 = (40.66 - 4.695 * T) * dms::PI / 180.; 0385 double d0 = (83.52 + 0.403 * T) * dms::PI / 180.; 0386 double sinx = -cos(d0) * cosDec * cos(a0 - ra().radians()); 0387 sinx = fabs(sinx - sin(d0) * sinDec); 0388 double rings = -2.6 * sinx + 1.25 * sinx * sinx; 0389 magnitude = -8.88 + param + 0.044 * phase + rings; 0390 } 0391 else if (name() == i18n("Uranus")) 0392 { 0393 magnitude = -7.19 + param + 0.0028 * phase; 0394 } 0395 else if (name() == i18n("Neptune")) 0396 { 0397 magnitude = -6.87 + param; 0398 } 0399 setMag(magnitude); 0400 } 0401 0402 SkyObject::UID KSPlanet::getUID() const 0403 { 0404 SkyObject::UID n; 0405 if (name() == i18n("Mercury")) 0406 { 0407 n = 1; 0408 } 0409 else if (name() == i18n("Venus")) 0410 { 0411 n = 2; 0412 } 0413 else if (name() == i18n("Earth")) 0414 { 0415 n = 3; 0416 } 0417 else if (name() == i18n("Mars")) 0418 { 0419 n = 4; 0420 } 0421 else if (name() == i18n("Jupiter")) 0422 { 0423 n = 5; 0424 } 0425 else if (name() == i18n("Saturn")) 0426 { 0427 n = 6; 0428 } 0429 else if (name() == i18n("Uranus")) 0430 { 0431 n = 7; 0432 } 0433 else if (name() == i18n("Neptune")) 0434 { 0435 n = 8; 0436 } 0437 else 0438 { 0439 return SkyObject::invalidUID; 0440 } 0441 return solarsysUID(UID_SOL_BIGOBJ) | n; 0442 }