File indexing completed on 2024-04-14 14:11:30
0001 /* 0002 SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "skyobject.h" 0008 0009 #include "geolocation.h" 0010 #include "ksnumbers.h" 0011 #include "kspaths.h" 0012 #ifdef KSTARS_LITE 0013 #include "skymaplite.h" 0014 #else 0015 #include "kspopupmenu.h" 0016 #include "skymap.h" 0017 #endif 0018 #include "kstarsdata.h" 0019 #include "Options.h" 0020 #include "starobject.h" 0021 #include "skycomponents/skylabeler.h" 0022 0023 0024 const SkyObject::UID SkyObject::invalidUID = ~0; 0025 const SkyObject::UID SkyObject::UID_STAR = 0; 0026 const SkyObject::UID SkyObject::UID_GALAXY = 1; 0027 const SkyObject::UID SkyObject::UID_DEEPSKY = 2; 0028 const SkyObject::UID SkyObject::UID_SOLARSYS = 3; 0029 0030 SkyObject::SkyObject(int t, dms r, dms d, float m, const QString &n, const QString &n2, const QString &lname) 0031 : SkyPoint(r, d) 0032 { 0033 setType(t); 0034 sortMagnitude = m; 0035 setName(n); 0036 setName2(n2); 0037 setLongName(lname); 0038 } 0039 0040 SkyObject::SkyObject(int t, double r, double d, float m, const QString &n, const QString &n2, const QString &lname) 0041 : SkyPoint(r, d) 0042 { 0043 setType(t); 0044 sortMagnitude = m; 0045 setName(n); 0046 setName2(n2); 0047 setLongName(lname); 0048 } 0049 0050 SkyObject *SkyObject::clone() const 0051 { 0052 Q_ASSERT(typeid(this) == typeid(static_cast<const SkyObject *>(this))); // Ensure we are not slicing a derived class 0053 return new SkyObject(*this); 0054 } 0055 0056 void SkyObject::showPopupMenu(KSPopupMenu *pmenu, const QPoint &pos) 0057 { 0058 #if defined(KSTARS_LITE) 0059 Q_UNUSED(pos) 0060 Q_UNUSED(pmenu); 0061 #else 0062 initPopupMenu(pmenu); 0063 pmenu->popup(pos); 0064 #endif 0065 } 0066 0067 void SkyObject::initPopupMenu(KSPopupMenu *pmenu) 0068 { 0069 #ifdef KSTARS_LITE 0070 Q_UNUSED(pmenu) 0071 #else 0072 pmenu->createEmptyMenu(this); 0073 #endif 0074 } 0075 0076 void SkyObject::setLongName(const QString &longname) 0077 { 0078 if (longname.isEmpty()) 0079 { 0080 if (hasName()) 0081 LongName = name(); 0082 else if (hasName2()) 0083 LongName = name2(); 0084 else 0085 LongName.clear(); 0086 } 0087 else 0088 { 0089 LongName = longname; 0090 } 0091 } 0092 0093 QTime SkyObject::riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact) const 0094 { 0095 // If this object does not rise or set, return an invalid time 0096 SkyPoint p = recomputeCoords(dt, geo); 0097 if (p.checkCircumpolar(geo->lat())) 0098 return QTime(); 0099 0100 //First of all, if the object is below the horizon at date/time dt, adjust the time 0101 //to bring it above the horizon 0102 KStarsDateTime dt2 = dt; 0103 dms lst(geo->GSTtoLST(dt.gst())); 0104 p.EquatorialToHorizontal(&lst, geo->lat()); 0105 if (p.alt().Degrees() < 0.0) 0106 { 0107 if (p.az().Degrees() < 180.0) //object has not risen yet 0108 { 0109 dt2 = dt.addSecs(12. * 3600.); // Move forward 12 hours, to a time when it has already risen 0110 } 0111 else //object has already set 0112 { 0113 dt2 = dt.addSecs(-12. * 3600.); // Move backward 12 hours, to a time when it has not yet set 0114 } 0115 } 0116 // The addition / subtraction of 12 hours ensures that we always 0117 // compute the _closest_ rise time and the _closest_ set time to 0118 // the current time. 0119 0120 QTime rstUt = riseSetTimeUT(dt2, geo, rst, exact); 0121 if (!rstUt.isValid()) 0122 return QTime(); 0123 0124 return geo->UTtoLT(KStarsDateTime(dt2.date(), rstUt)).time(); 0125 } 0126 0127 QTime SkyObject::riseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, bool riseT, bool exact) const 0128 { 0129 // First trial to calculate UT 0130 QTime UT = auxRiseSetTimeUT(dt, geo, &ra(), &dec(), riseT); 0131 0132 // We iterate once more using the calculated UT to compute again 0133 // the ra and dec for that time and hence the rise/set time. 0134 // Also, adjust the date by +/- 1 day, if necessary 0135 0136 // By adding this +/- 1 day, we are double-checking that the 0137 // reported rise-time is the _already_ (last) risen time, and that 0138 // the reported set-time is the _future_ (next) set time 0139 // 0140 // However, issues with this are taken care of in 0141 // SkyObject::riseSetTime() 0142 0143 KStarsDateTime dt0 = dt; 0144 dt0.setTime(UT); 0145 if (riseT && dt0 > dt) 0146 { 0147 dt0 = dt0.addDays(-1); 0148 } 0149 else if (!riseT && dt0 < dt) 0150 { 0151 dt0 = dt0.addDays(1); 0152 } 0153 0154 SkyPoint sp = recomputeCoords(dt0, geo); 0155 UT = auxRiseSetTimeUT(dt0, geo, &sp.ra(), &sp.dec(), riseT); 0156 0157 if (exact) 0158 { 0159 // We iterate a second time (For the Moon the second iteration changes 0160 // aprox. 1.5 arcmin the coordinates). 0161 dt0.setTime(UT); 0162 sp = recomputeCoords(dt0, geo); 0163 UT = auxRiseSetTimeUT(dt0, geo, &sp.ra(), &sp.dec(), riseT); 0164 } 0165 0166 return UT; 0167 } 0168 0169 QTime SkyObject::auxRiseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, const dms *righta, const dms *decl, 0170 bool riseT) const 0171 { 0172 dms LST = auxRiseSetTimeLST(geo->lat(), righta, decl, riseT); 0173 return dt.GSTtoUT(geo->LSTtoGST(LST)); 0174 } 0175 0176 dms SkyObject::auxRiseSetTimeLST(const dms *gLat, const dms *righta, const dms *decl, bool riseT) const 0177 { 0178 dms h0 = elevationCorrection(); 0179 double H = approxHourAngle(&h0, gLat, decl); 0180 dms LST; 0181 0182 if (riseT) 0183 LST.setH(24.0 + righta->Hours() - H / 15.0); 0184 else 0185 LST.setH(righta->Hours() + H / 15.0); 0186 0187 return LST.reduce(); 0188 } 0189 0190 dms SkyObject::riseSetTimeAz(const KStarsDateTime &dt, const GeoLocation *geo, bool riseT) const 0191 { 0192 dms Azimuth; 0193 double AltRad, AzRad; 0194 double sindec, cosdec, sinlat, coslat, sinHA, cosHA; 0195 double sinAlt, cosAlt; 0196 0197 QTime UT = riseSetTimeUT(dt, geo, riseT); 0198 KStarsDateTime dt0 = dt; 0199 dt0.setTime(UT); 0200 SkyPoint sp = recomputeCoords(dt0, geo); 0201 0202 dms LST = auxRiseSetTimeLST(geo->lat(), &sp.ra0(), &sp.dec0(), riseT); 0203 dms HourAngle = dms(LST.Degrees() - sp.ra0().Degrees()); 0204 0205 geo->lat()->SinCos(sinlat, coslat); 0206 dec().SinCos(sindec, cosdec); 0207 HourAngle.SinCos(sinHA, cosHA); 0208 0209 sinAlt = sindec * sinlat + cosdec * coslat * cosHA; 0210 AltRad = asin(sinAlt); 0211 cosAlt = cos(AltRad); 0212 0213 AzRad = acos((sindec - sinlat * sinAlt) / (coslat * cosAlt)); 0214 if (sinHA > 0.0) 0215 AzRad = 2.0 * dms::PI - AzRad; // resolve acos() ambiguity 0216 Azimuth.setRadians(AzRad); 0217 0218 return Azimuth; 0219 } 0220 0221 QTime SkyObject::transitTimeUT(const KStarsDateTime &dt, const GeoLocation *geo) const 0222 { 0223 dms LST = geo->GSTtoLST(dt.gst()); 0224 0225 //dSec is the number of seconds until the object transits. 0226 dms HourAngle = dms(LST.Degrees() - ra().Degrees()); 0227 int dSec = static_cast<int>(-3600. * HourAngle.Degrees() / 15.0); 0228 0229 //dt0 is the first guess at the transit time. 0230 KStarsDateTime dt0 = dt.addSecs(dSec); 0231 //recompute object's position at UT0 and then find transit time of this refined position 0232 SkyPoint sp = recomputeCoords(dt0, geo); 0233 HourAngle = dms(LST.Degrees() - sp.ra().Degrees()); 0234 dSec = static_cast<int>(-3600. * HourAngle.Degrees() / 15.0); 0235 0236 return dt.addSecs(dSec).time(); 0237 } 0238 0239 QTime SkyObject::transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const 0240 { 0241 return geo->UTtoLT(KStarsDateTime(dt.date(), transitTimeUT(dt, geo))).time(); 0242 } 0243 0244 dms SkyObject::transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const 0245 { 0246 KStarsDateTime dt0 = dt; 0247 dt0.setTime(transitTimeUT(dt, geo)); 0248 SkyPoint sp = recomputeCoords(dt0, geo); 0249 0250 double delta = 90 - geo->lat()->Degrees() + sp.dec().Degrees(); 0251 if (delta > 90) 0252 delta = 180 - delta; 0253 return dms(delta); 0254 } 0255 0256 double SkyObject::approxHourAngle(const dms *h0, const dms *gLat, const dms *dec) const 0257 { 0258 double sh0 = sin(h0->radians()); 0259 double r = (sh0 - sin(gLat->radians()) * sin(dec->radians())) / (cos(gLat->radians()) * cos(dec->radians())); 0260 0261 double H = acos(r) / dms::DegToRad; 0262 0263 return H; 0264 } 0265 0266 dms SkyObject::elevationCorrection(void) const 0267 { 0268 /* The atmospheric refraction at the horizon shifts altitude by 0269 * - 34 arcmin = 0.5667 degrees. This value changes if the observer 0270 * is above the horizon, or if the weather conditions change much. 0271 * 0272 * For the sun we have to add half the angular sie of the body, since 0273 * the sunset is the time the upper limb of the sun disappears below 0274 * the horizon, and dawn, when the upper part of the limb appears 0275 * over the horizon. The angular size of the sun = angular size of the 0276 * moon = 31' 59''. 0277 * 0278 * So for the sun the correction is = -34 - 16 = 50 arcmin = -0.8333 0279 * 0280 * This same correction should be applied to the moon however parallax 0281 * is important here. Meeus states that the correction should be 0282 * 0.7275 P - 34 arcmin, where P is the moon's horizontal parallax. 0283 * He proposes a mean value of 0.125 degrees if no great accuracy 0284 * is needed. 0285 */ 0286 0287 if (name() == i18n("Sun") || name() == i18n("Moon") || name() == i18n("Earth Shadow")) 0288 return dms(-0.8333); 0289 // else if ( name() == "Moon" ) 0290 // return dms(0.125); 0291 else // All sources point-like. 0292 return dms(-0.5667); 0293 } 0294 0295 SkyPoint SkyObject::recomputeCoords(const KStarsDateTime &dt, const GeoLocation *geo) const 0296 { 0297 // Create a clone 0298 SkyObject *c = this->clone(); 0299 0300 // compute coords of the copy for new time jd 0301 KSNumbers num(dt.djd()); 0302 0303 // Note: isSolarSystem() below should give the same result on this 0304 // and c. The only very minor reason to prefer this is so that we 0305 // have an additional layer of warnings about subclasses of 0306 // KSPlanetBase that do not implement SkyObject::clone() due to 0307 // the passing of lat and LST 0308 0309 if (isSolarSystem() && geo) 0310 { 0311 CachingDms LST = geo->GSTtoLST(dt.gst()); 0312 c->updateCoords(&num, true, geo->lat(), &LST); 0313 } 0314 else 0315 { 0316 c->updateCoords(&num); 0317 } 0318 0319 // Transfer the coordinates into a SkyPoint 0320 SkyPoint p = *c; 0321 0322 // Delete the clone 0323 delete c; 0324 0325 // Return the SkyPoint 0326 return p; 0327 } 0328 0329 SkyPoint SkyObject::recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const 0330 { 0331 Q_ASSERT(geo); 0332 SkyPoint ret = recomputeCoords(dt, geo); 0333 CachingDms LST = geo->GSTtoLST(dt.gst()); 0334 ret.EquatorialToHorizontal(&LST, geo->lat()); 0335 return ret; 0336 } 0337 0338 QString SkyObject::typeName(int t) 0339 { 0340 switch (t) 0341 { 0342 case STAR: 0343 return i18n("Star"); 0344 case CATALOG_STAR: 0345 return i18n("Catalog Star"); 0346 case PLANET: 0347 return i18n("Planet"); 0348 case OPEN_CLUSTER: 0349 return i18n("Open Cluster"); 0350 case GLOBULAR_CLUSTER: 0351 return i18n("Globular Cluster"); 0352 case GASEOUS_NEBULA: 0353 return i18n("Gaseous Nebula"); 0354 case PLANETARY_NEBULA: 0355 return i18n("Planetary Nebula"); 0356 case SUPERNOVA_REMNANT: 0357 return i18n("Supernova Remnant"); 0358 case GALAXY: 0359 return i18n("Galaxy"); 0360 case COMET: 0361 return i18n("Comet"); 0362 case ASTEROID: 0363 return i18n("Asteroid"); 0364 case CONSTELLATION: 0365 return i18n("Constellation"); 0366 case MOON: 0367 return i18n("Moon"); 0368 case GALAXY_CLUSTER: 0369 return i18n("Galaxy Cluster"); 0370 case SATELLITE: 0371 return i18n("Satellite"); 0372 case SUPERNOVA: 0373 return i18n("Supernova"); 0374 case RADIO_SOURCE: 0375 return i18n("Radio Source"); 0376 case ASTERISM: 0377 return i18n("Asterism"); 0378 case DARK_NEBULA: 0379 return i18n("Dark Nebula"); 0380 case QUASAR: 0381 return i18n("Quasar"); 0382 case MULT_STAR: 0383 return i18n("Multiple Star"); 0384 default: 0385 return i18n("Unknown Type"); 0386 } 0387 } 0388 0389 QString SkyObject::typeName() const 0390 { 0391 return typeName(Type); 0392 } 0393 0394 QString SkyObject::messageFromTitle(const QString &imageTitle) const 0395 { 0396 QString message = imageTitle; 0397 0398 //HST Image 0399 if (imageTitle == i18n("Show HST Image") || imageTitle.contains("HST")) 0400 { 0401 message = i18n("%1: Hubble Space Telescope, operated by STScI for NASA [public domain]", longname()); 0402 0403 //Spitzer Image 0404 } 0405 else if (imageTitle.contains(i18n("Show Spitzer Image"))) 0406 { 0407 message = i18n("%1: Spitzer Space Telescope, courtesy NASA/JPL-Caltech [public domain]", longname()); 0408 0409 //SEDS Image 0410 } 0411 else if (imageTitle == i18n("Show SEDS Image")) 0412 { 0413 message = i18n("%1: SEDS, http://www.seds.org [free for non-commercial use]", longname()); 0414 0415 //Kitt Peak AOP Image 0416 } 0417 else if (imageTitle == i18n("Show KPNO AOP Image")) 0418 { 0419 message = i18n("%1: Advanced Observing Program at Kitt Peak National Observatory [free for non-commercial use; " 0420 "no physical reproductions]", 0421 longname()); 0422 0423 //NOAO Image 0424 } 0425 else if (imageTitle.contains(i18n("Show NOAO Image"))) 0426 { 0427 message = 0428 i18n("%1: National Optical Astronomy Observatories and AURA [free for non-commercial use]", longname()); 0429 0430 //VLT Image 0431 } 0432 else if (imageTitle.contains("VLT")) 0433 { 0434 message = i18n("%1: Very Large Telescope, operated by the European Southern Observatory [free for " 0435 "non-commercial use; no reproductions]", 0436 longname()); 0437 0438 //All others 0439 } 0440 else if (imageTitle.startsWith(i18n("Show"))) 0441 { 0442 message = imageTitle.mid(imageTitle.indexOf(" ") + 1); //eat first word, "Show" 0443 message = longname() + ": " + message; 0444 } 0445 0446 return message; 0447 } 0448 0449 QString SkyObject::labelString() const 0450 { 0451 return translatedName(); 0452 } 0453 0454 double SkyObject::labelOffset() const 0455 { 0456 return SkyLabeler::ZoomOffset(); 0457 } 0458 0459 SkyObject::UID SkyObject::getUID() const 0460 { 0461 return invalidUID; 0462 }