1 /* 2 * Stellarium 3 * Copyright (C) 2002 Fabien Chereau 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 2 8 * of the License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. 18 */ 19 20 #ifndef PLANET_HPP 21 #define PLANET_HPP 22 23 #include "StelObject.hpp" 24 #include "StelProjector.hpp" 25 #include "StelPropertyMgr.hpp" 26 #include "VecMath.hpp" 27 #include "GeomMath.hpp" 28 #include "StelFader.hpp" 29 #include "StelTextureTypes.hpp" 30 #include "StelProjectorType.hpp" 31 #include "RotationElements.hpp" 32 33 #include <QCache> 34 #include <QString> 35 36 // The callback type for the external position computation function 37 // arguments are JDE, position[3], velocity[3]. 38 // The last variable is the userData pointer, which is Q_NULLPTR for Planets, but used in derived classes. E.g. points to the KeplerOrbit for Comets. 39 typedef void (*posFuncType)(double, double*, double*, void*); 40 41 typedef void (OsculatingFunctType)(double jde0,double jde,double xyz[3], double xyzdot[3]); 42 43 // epoch J2000: 12 UT on 1 Jan 2000 44 #define J2000 2451545.0 45 #define ORBIT_SEGMENTS 360 46 47 class Orbit; 48 class KeplerOrbit; 49 class StelFont; 50 class StelPainter; 51 class StelTranslator; 52 class StelOBJ; 53 class StelOpenGLArray; 54 class HipsSurvey; 55 template <class T> class QFuture; 56 class QOpenGLBuffer; 57 class QOpenGLFunctions; 58 class QOpenGLShaderProgram; 59 class QOpenGLTexture; 60 #ifdef DEBUG_SHADOWMAP 61 class QOpenGLFramebufferObject; 62 #endif 63 64 typedef QSharedPointer<class HipsSurvey> HipsSurveyP; 65 66 // Class to manage rings for planets like Saturn 67 class Ring 68 { 69 public: 70 Ring(float radiusMin, float radiusMax,const QString &texname); getSize(void) const71 double getSize(void) const {return static_cast<double>(radiusMax);} 72 const float radiusMin; 73 const float radiusMax; 74 StelTextureSP tex; 75 }; 76 77 78 class Planet : public StelObject 79 { 80 public: 81 static const QString PLANET_TYPE; 82 friend class SolarSystem; 83 84 //! numeric typecodes for the type descriptions in ssystem.ini 85 // Until 0.13 QStrings were used for types. 86 // Enums are slightly faster than string comparisons in time-critical comparisons. 87 // If other types are introduced, add here and the string in init(). 88 // TBD for 0.19 or later: Preferably convert this into a bitfield and allow several bits set: 89 // Cubewanos, SDO, OCO, Sednoids are Asteroids, Pluto is a Plutino and DwarfPlanet, Ceres is Asteroid and DwarfPlanet etc.! 90 // Maybe even add queries like Planet::isAsteroid() { return (planetType & Planet::isAsteroid);} 91 enum PlanetType 92 { 93 isStar, // ssystem.ini: type="star" 94 isPlanet, // ssystem.ini: type="planet" 95 isMoon, // ssystem.ini: type="moon" 96 isObserver, // ssystem.ini: type="observer" 97 isArtificial, // Used in transitions from planet to planet. 98 isAsteroid, // ssystem.ini: type="asteroid". all types >= isAsteroid are "Minor Bodies". 99 // Put other things (spacecraft etc) before isAsteroid. 100 isPlutino, // ssystem.ini: type="plutino" 101 isComet, // ssystem.ini: type="comet" 102 isDwarfPlanet, // ssystem.ini: type="dwarf planet" 103 isCubewano, // ssystem.ini: type="cubewano" 104 isSDO, // ssystem.ini: type="scattered disc object" 105 isOCO, // ssystem.ini: type="oco" 106 isSednoid, // ssystem.ini: type="sednoid" 107 isInterstellar, // ssystem.ini: type="interstellar object" 108 isUNDEFINED // ssystem.ini: type=<anything else>. THIS IS ONLY IN CASE OF ERROR! 109 }; 110 Q_ENUM(PlanetType) 111 112 enum PlanetOrbitColorStyle 113 { 114 ocsOneColor, // One color for all orbits 115 ocsGroups, // Separate colors for each group of Solar system bodies 116 ocsMajorPlanets // Separate colors for each of major planets of Solar system 117 }; 118 Q_ENUM(PlanetOrbitColorStyle) 119 120 enum ApparentMagnitudeAlgorithm 121 { 122 Mueller_1893, // G. Mueller, based on visual observations 1877-91. [Explanatory Supplement to the Astronomical Almanac, 1961] 123 AstronomicalAlmanac_1984, // Astronomical Almanac 1984 and later. These give V (instrumental) magnitudes (allegedly from D.L. Harris, but this is wrong!) 124 ExplanatorySupplement_1992, // Algorithm provided by Pere Planesas (Observatorio Astronomico Nacional) (Was called "Planesas") 125 ExplanatorySupplement_2013, // Explanatory Supplement to the Astronomical Almanac, 3rd edition 2013 126 MallamaHilton_2018, // A. Mallama, J. L. Hilton: Computing apparent planetary magnitudes for the Astronomical Almanac. Astron.&Computing 25 (2018) 10-24 127 UndefinedAlgorithm, 128 Generic // Visual magnitude based on phase angle and albedo. The formula source for this is totally unknown! 129 }; 130 Q_ENUM(ApparentMagnitudeAlgorithm) 131 132 //! enums to indicate for which purpose we check positional quality. 133 //! Objects on KeplerOrbits may be too far from their epoch to provide useful data. 134 enum PositionQuality 135 { 136 Position, // Good enough for positions. 137 OrbitPlotting // Good enough for orbitplotting? 138 }; 139 Q_ENUM(PositionQuality) 140 141 public: 142 Planet(const QString& englishName, 143 double equatorialRadius, 144 double oblateness, 145 Vec3f halocolor, 146 float albedo, 147 float roughness, 148 const QString& texMapName, 149 const QString& normalMapName, 150 const QString& objModelName, 151 posFuncType _coordFunc, 152 Orbit *anOrbitPtr, 153 OsculatingFunctType *osculatingFunc, 154 bool closeOrbit, 155 bool hidden, 156 bool hasAtmosphere, 157 bool hasHalo, 158 const QString &pTypeStr); 159 160 virtual ~Planet() Q_DECL_OVERRIDE; 161 162 //! Initializes static vars. Must be called before creating first planet. 163 // Currently ensured by SolarSystem::init() 164 static void init(); 165 166 /////////////////////////////////////////////////////////////////////////// 167 // Methods inherited from StelObject 168 //! Get a string with data about the Planet. 169 //! Planets support the following InfoStringGroup flags: 170 //! - Name 171 //! - Magnitude 172 //! - RaDec 173 //! - AltAzi 174 //! - Distance 175 //! - Size 176 //! - PlainText 177 //! - Extra: Heliocentric Ecliptical Coordinates & Observer-planetocentric Ecliptical Coordinates, Phase, illumination, phase angle & elongation from the Sun 178 //! @note subclasses should prefer to override only the component infostrings getInfoString...(), not this method! 179 //! @param core the StelCore object 180 //! @param flags a set of InfoStringGroup items to include in the return value. 181 //! @return a QString containing an HMTL encoded description of the Planet. 182 virtual QString getInfoString(const StelCore *core, const InfoStringGroup& flags) const Q_DECL_OVERRIDE; 183 //! In addition to the entries from StelObject::getInfoMap(), Planet objects provide 184 //! - distance 185 //! - phase (result of getPhase) 186 //! - illumination (=100*phase) 187 //! - phase-angle 188 //! - phase-angle-dms (formatted string) 189 //! - phase-angle-deg (formatted string) 190 //! - elongation 191 //! - elongation-dms (formatted string) 192 //! - elongation-deg (formatted string) 193 //! - type (object type description) 194 //! - velocity (formatted string) 195 //! - heliocentric-velocity (formatted string) 196 //! - scale 197 //! - eclipse-obscuration (for Sun only) 198 //! - eclipse-magnitude (for Sun only) 199 //! - central_l (on Earth only; degrees) 200 //! - central_b (on Earth only; degrees) 201 //! - pa_axis (on Earth only; degrees) 202 //! - subsolar_l (on Earth only; degrees) 203 //! - subsolar_b (on Earth only; degrees) 204 //! - libration_l (on Earth for Moon only; degrees) 205 //! - libration_b (on Earth for Moon only; degrees) 206 //! - colongitude (on Earth for Moon only; degrees) 207 //! - penumbral-eclipse-magnitude (on Earth for Moon only) 208 //! - umbral-eclipse-magnitude (on Earth for Moon only) 209 virtual QVariantMap getInfoMap(const StelCore *core) const Q_DECL_OVERRIDE; 210 virtual double getCloseViewFov(const StelCore* core) const Q_DECL_OVERRIDE; 211 virtual double getSatellitesFov(const StelCore* core) const Q_DECL_OVERRIDE; 212 virtual double getParentSatellitesFov(const StelCore* core) const Q_DECL_OVERRIDE; 213 virtual float getVMagnitude(const StelCore* core) const Q_DECL_OVERRIDE; 214 virtual float getSelectPriority(const StelCore* core) const Q_DECL_OVERRIDE; 215 virtual Vec3f getInfoColor(void) const Q_DECL_OVERRIDE; getType(void) const216 virtual QString getType(void) const Q_DECL_OVERRIDE {return PLANET_TYPE;} getID(void) const217 virtual QString getID(void) const Q_DECL_OVERRIDE { return englishName; } 218 //! A Planet's own eclipticPos is in VSOP87 ref. frame (practically equal to ecliptic of J2000 for us) coordinates relative to the parent body (sun, planet). 219 //! To get J2000 equatorial coordinates, we require heliocentric ecliptical positions (adding up parent positions) of observer and Planet. 220 //! Then we use the matrix rotation multiplication with an existing matrix in StelCore to orient from eclipticalJ2000 to equatorialJ2000. 221 //! The end result is a non-normalized 3D vector which allows retrieving distances etc. 222 //! The positional computation is called by SolarSystem. If the core's aberration setting is active, the J2000 position will then include it. 223 virtual Vec3d getJ2000EquatorialPos(const StelCore *core) const Q_DECL_OVERRIDE; 224 virtual QString getEnglishName(void) const Q_DECL_OVERRIDE; 225 virtual QString getNameI18n(void) const Q_DECL_OVERRIDE; getNativeName(void) const226 QString getNativeName(void) const { return nativeName; } getNativeNameI18n(void) const227 QString getNativeNameI18n(void) const { return nativeNameMeaningI18n; } getCommonEnglishName(void) const228 QString getCommonEnglishName(void) const {return englishName;} getCommonNameI18n(void) const229 QString getCommonNameI18n(void) const {return nameI18;} 230 //! Get angular semidiameter, degrees. If planet display is artificially enlarged (e.g. Moon upscale), value will also be increased. 231 virtual double getAngularSize(const StelCore* core) const Q_DECL_OVERRIDE; hasAtmosphere(void)232 virtual bool hasAtmosphere(void) {return atmosphere;} hasHalo(void)233 virtual bool hasHalo(void) {return halo;} 234 //! Returns whether planet positions are valid and useful for the current simulation time. 235 //! E.g. outdated orbital elements for Kepler orbits (beyond their orbit_good .ini file entries) 236 //! may lead to invalid positions which should better not be used. 237 //! @param purpose signal whether result should be good enough for observation of just for plotting orbit data. 238 //! For observation, date should be within the orbit_good value, or within 1 year from epoch of the orbital elements. 239 //! @note for major planets and moons this method will always return true 240 bool hasValidPositionalData(const double JDE, const PositionQuality purpose) const; 241 //! Returns JDE dates of presumably valid data for positional calculation or acceptable range for graphics. 242 //! For the major planets and moons, this is always (std::numeric_limits<double>::min(), std::numeric_limits<double>::max()) 243 //! For planets with Keplerian orbits, this is [epoch-orbit_good, epoch+orbit_good] or, 244 //! if purpose=Position, [epoch-min(orbit_good, 365), epoch+min(orbit_good, 365)]. 245 //! This should help to detect and avoid using outdated orbital elements. 246 Vec2d getValidPositionalDataRange(const PositionQuality purpose) const; getAxisRotation(void)247 float getAxisRotation(void) { return axisRotation;} //! return axisRotation last computed in computeTransMatrix(). [degrees] 248 249 /////////////////////////////////////////////////////////////////////////// 250 // Methods of SolarSystem object 251 //! Translate planet name using the passed translator 252 virtual void translateName(const StelTranslator &trans); 253 254 // Draw the Planet 255 // GZ Made that virtual to allow comets having their own draw(). 256 virtual void draw(StelCore* core, float maxMagLabels, const QFont& planetNameFont); 257 258 /////////////////////////////////////////////////////////////////////////// 259 // Methods specific to Planet 260 //! Get the equator radius of the planet in AU. 261 //! @return the equator radius of the planet in astronomical units. getEquatorialRadius(void) const262 double getEquatorialRadius(void) const {return equatorialRadius;} 263 //! Get the value (1-f) for oblateness f=polarRadius/equatorialRadius. getOneMinusOblateness(void) const264 double getOneMinusOblateness(void) const {return oneMinusOblateness;} 265 //! Get the polar radius of the planet in AU. 266 //! @return the polar radius of the planet in astronomical units. getPolarRadius(void) const267 double getPolarRadius(void) const {return equatorialRadius*oneMinusOblateness;} 268 //! Get duration of sidereal day (earth days, may come from rot_periode or orbit_period (for moons) from ssystem_*.ini) getSiderealDay(void) const269 double getSiderealDay(void) const { if (re.W1!=0.) return 360.0/re.W1; else return static_cast<double>(re.period);} // I assume the more modern values are better. 270 //! Get duration of sidereal year 271 // must be virtual for Comets. getSiderealPeriod(void) const272 virtual double getSiderealPeriod(void) const { return siderealPeriod; } 273 //! set duration of sidereal year. Also sets deltaOrbitJDE and may set closeOrbit for Planet objects which have KeplerOrbits. 274 //! siderealPeriod [earth days] orbital duration. 275 void setSiderealPeriod(const double siderealPeriod); 276 //! Get duration of mean solar day, in earth days. 277 double getMeanSolarDay(void) const; 278 //! Get albedo getAlbedo(void) const279 double getAlbedo(void) const { return static_cast<double>(albedo); } 280 getTextMapName() const281 const QString& getTextMapName() const {return texMapName;} getPlanetTypeString() const282 const QString getPlanetTypeString() const {return pTypeMap.value(pType);} getPlanetType() const283 PlanetType getPlanetType() const {return pType;} getOrbit() const284 Orbit* getOrbit() const {return orbitPtr;} 285 setNativeName(QString planet)286 void setNativeName(QString planet) { nativeName = planet; } setNativeNameMeaning(QString planet)287 void setNativeNameMeaning(QString planet) { nativeNameMeaning = planet; } 288 289 //! set the IAU moon number (designation of the moon), if any. 290 void setIAUMoonNumber(QString designation); 291 292 //! Return the absolute magnitude (read from file ssystem.ini) getAbsoluteMagnitude() const293 float getAbsoluteMagnitude() const {return absoluteMagnitude;} 294 //! Return the mean opposition magnitude, defined as V(1,0)+5log10(a(a-1)) 295 //! A return value of 100 signals invalid result. 296 float getMeanOppositionMagnitude() const; 297 // getApparentMagnitudeAlgorithm()298 static ApparentMagnitudeAlgorithm getApparentMagnitudeAlgorithm() { return vMagAlgorithm; } getApparentMagnitudeAlgorithmString()299 static const QString getApparentMagnitudeAlgorithmString() { return vMagAlgorithmMap.value(vMagAlgorithm); } 300 static void setApparentMagnitudeAlgorithm(QString algorithm); setApparentMagnitudeAlgorithm(ApparentMagnitudeAlgorithm algorithm)301 static void setApparentMagnitudeAlgorithm(ApparentMagnitudeAlgorithm algorithm){ vMagAlgorithm=algorithm; } 302 303 //! Compute the axial z rotation (daily rotation around the polar axis) [degrees] to use from equatorial to hour angle based coordinates. 304 //! On Earth, sidereal time on the other hand is the angle along the planet equator from RA0 to the meridian, i.e. hour angle of the first point of Aries. 305 //! For Earth (of course) it is sidereal time at Greenwich. 306 //! V0.21+ update: 307 //! For planets and Moons, in this context this is the rotation angle W of the Prime meridian from the ascending node of the planet equator on the ICRF equator. 308 //! The usual WGCCRE model is W=W0+d*W1. Some planets/moons have more complicated rotations though, these are also handled in here. 309 //! The planet objects with old-style data are computed like in earlier versions of Stellarium. Their computational model is however questionable. 310 //! For general applicability we need both time flavours: 311 //! @param JD is JD(UT) for Earth 312 //! @param JDE is used for other locations 313 double getSiderealTime(double JD, double JDE) const; 314 315 //! return a rotation matrix from the planet's equatorial coordinate frame to the VSOP87 (ecliptical J2000) frame. 316 //! For planets/moons with WGCCRE rotation elements, this is just a single matrix. 317 //! For objects with traditional elements, this builds up the matrix from the parents. 318 Mat4d getRotEquatorialToVsop87(void) const; 319 //! set a rotation matrix from the planet's equatorial coordinate frame to the VSOP87 (ecliptical J2000) frame. 320 //! For planets/moons with WGCCRE rotation elements, this just sets this matrix. 321 //! For objects with traditional elements, this builds up the matrix from the parents. 322 void setRotEquatorialToVsop87(const Mat4d &m); 323 getRotationElements(void) const324 const RotationElements &getRotationElements(void) const {return re;} 325 326 //! Set the rotational elements. 327 //! Given two data models, we must support both: the traditional elements relative to the parent object: 328 //! name: English name of the object. A corrective function may be attached which depends on the name. 329 //! _period: duration of sidereal rotation [Julian days] 330 //! _offset: [angle at _epoch. ] 331 //! _epoch: [JDE] 332 //! _obliquity [rad] 333 //! _ascendingNode of equator on ecliptic[rad] 334 //! The more modern way to specify these elements are relative to the ICRF: 335 //! ra_pole=_ra0 + T*_ra1. ra_pole and de_pole must be computed more than for initialisation for J2000 336 //! de_pole=_de0 + T*_de1. ra and de values to be stored in [rad] 337 //! _w0, _w1 to be given in degrees! 338 //! If _ra0 is not zero, we understand WGCCRE data ra0, ra1, de0, de1, w0, w1 are used. 339 void setRotationElements(const QString name, const double _period, const double _offset, const double _epoch, 340 const double _obliquity, const double _ascendingNode, 341 const double _ra0, const double _ra1, 342 const double _de0, const double _de1, 343 const double _w0, const double _w1); 344 345 //! Note: The only place where this is used is to build up orbits for planet moons w.r.t. the parent planet orientation. getRotAscendingNode(void) const346 double getRotAscendingNode(void) const {return re.ascendingNode; } 347 //! return angle between axis and normal of ecliptic plane (or, for a moon, equatorial/reference plane defined by parent). 348 //! For Earth, this is the angle between axis and normal to current ecliptic of date, i.e. the ecliptic obliquity of date JDE. 349 //! Note: The only place where this is not used for Earth is to build up orbits for planet moons w.r.t. the parent planet orientation. 350 double getRotObliquity(double JDE) const; 351 352 //! Compute the position and orbital velocity in the parent Planet coordinate system 353 //! You can add the aberrationPush value according to Edot*lightTime in Explanatory Supplement (2013) formula 7.55. 354 virtual void computePosition(const double dateJDE, const Vec3d &aberrationPush); 355 //! Compute the position and orbital velocity in the parent Planet coordinate system, and return them in eclPosition and eclVelocity 356 //! These may be preferred when we want to avoid setting the actual position (e.g., RTS computation) 357 virtual void computePosition(const double dateJDE, Vec3d &eclPosition, Vec3d &eclVelocity) const; 358 359 360 //! Compute the transformation matrix from the local Planet coordinate to the parent Planet coordinate. 361 //! This requires both flavours of JD in cases involving Earth. 362 void computeTransMatrix(double JD, double JDE); 363 364 //! Retrieve planetocentric rectangular coordinates of a location on the ellipsoid surface, or with altitude altMetres above the ellipsoid surface. 365 //! Meeus, Astr. Alg. 2nd ed, Ch.11. 366 //! @param longDeg longitude of location, degrees. (currently unused. Set to 0.) 367 //! @param latDeg planetographic latitude, degrees. 368 //! @param altMetres altitude above ellipsoid surface (metres) 369 //! @return [rhoCosPhiPrime*a, rhoSinPhiPrime*a, phiPrime, rho*a] where a=equatorial radius 370 Vec4d getRectangularCoordinates(const double longDeg, const double latDeg, const double altMetres=0.) const; 371 372 //! Get the phase angle (radians) for an observer at pos obsPos in heliocentric coordinates (in AU) 373 double getPhaseAngle(const Vec3d& obsPos) const; 374 //! Get the elongation angle (radians) for an observer at pos obsPos in heliocentric coordinates (in AU) 375 double getElongation(const Vec3d& obsPos) const; 376 //! Get the angular radius (degrees) of the planet spheroid (i.e. without the rings) 377 double getSpheroidAngularSize(const StelCore* core) const; 378 //! Get the planet phase (illuminated fraction of the planet disk, [0=dark..1=full]) for an observer at pos obsPos in heliocentric coordinates (in AU) 379 float getPhase(const Vec3d& obsPos) const; 380 //! Get the position angle of the illuminated limb of a planet 381 //! The result depends on the arguments' coordinate system which must be identical. 382 //! E.g. if both are equatorial for equinox of date or J2000, the angle is zero when the bright limb is towards the north of the disk. 383 //! An angle of 90° indicates a bright limb on the eastern limb, like an old moon. 384 //! Source: Meeus, Astr.Algorithms (2nd ed.), 48.5. 385 static float getPAsun(const Vec3d &sunPos, const Vec3d &objPos); 386 387 //! Get planetographic coordinates of subsolar and sub-observer points. 388 //! Only meaningful for earth-bound observers. 389 //! Source: Explanatory Supplement 2013, 10.4.1 390 //! @param jupiterGraphical Jupiter requires special treatment because its LII coordinate system does not 391 //! stay in sync with the texture. (GRS is moving). Set this to true to return the 392 //! incorrect, graphics-only longitude. 393 //! first[0] = 10.26 phi_e [rad] Planetocentric latitude of sub-earth point 394 //! first[1] = 10.26 phi'_e [rad] Planetographic latitude of sub-earth point 395 //! first[2] = 10.26 lambda'_e [rad] Planetographic longitude of sub-earth point (0..2pi) 396 //! first[3] = 10.29 P_n [rad] Position angle of axis north pole in equatorial coordinates of date 397 //! second[0] = 10.26 phi_s [rad] Planetocentric latitude of sub-solar point 398 //! second[1] = 10.26 phi'_s [rad] Planetographic latitude of sub-solar point 399 //! second[2] = 10.26 lambda'_s [rad] Planetographic longitude of sub-solar point (0..2pi) 400 //! Note: For the Moon, it is more common to give Libration angles, where L=-lambda'_e, B=phi'_e. 401 //! Note: For Jupiter, this returns central meridian in L_II. 402 //! Note: For Saturn, this returns central meridian in L_III (rotation of magnetic field). 403 QPair<Vec4d, Vec3d> getSubSolarObserverPoints(const StelCore *core, bool jupiterGraphical=false) const; 404 405 //! returns if planet has retrograde rotation isRotatingRetrograde() const406 bool isRotatingRetrograde() const { return re.W1<0.; } 407 408 //! Get the Planet position in the parent Planet ecliptic coordinate in AU 409 Vec3d getEclipticPos(double dateJDE) const; 410 //! Get the last computed Planet position in the parent Planet ecliptic coordinate in AU getEclipticPos() const411 Vec3d getEclipticPos() const {return getEclipticPos(lastJDE);} 412 413 //! Return the heliocentric ecliptical position getHeliocentricEclipticPos() const414 Vec3d getHeliocentricEclipticPos() const {return getHeliocentricPos(eclipticPos);} 415 Vec3d getHeliocentricEclipticPos(double dateJDE) const; 416 417 //! Return the heliocentric transformation for local (parentocentric) coordinate 418 //! @arg p planetocentric rectangular ecliptical coordinate (J2000) 419 //! @return heliocentric rectangular ecliptical coordinates (J2000) 420 Vec3d getHeliocentricPos(Vec3d p) const; 421 //! Propagate the heliocentric coordinates to parentocentric coordinates 422 //! @arg pos heliocentric rectangular ecliptical coordinate (J2000) 423 void setHeliocentricEclipticPos(const Vec3d &pos); 424 425 //! Get the planet velocity around the parent planet in ecliptical coordinates in AU/d getEclipticVelocity() const426 Vec3d getEclipticVelocity() const {return eclipticVelocity;} 427 428 //! Get the planet's heliocentric velocity in the solar system in ecliptical coordinates in AU/d. Required for aberration! 429 Vec3d getHeliocentricEclipticVelocity() const; 430 431 //! Compute and return the distance to the given position in heliocentric ecliptical (J2000) coordinates (in AU) 432 //! Preserves result for later retrieval by getDistance() 433 //! As side effect, improve fps by juggling update frequency (deltaJDE) for asteroids and other minor bodies. They must be fast if close to observer, but can be slow if further away. 434 double computeDistance(const Vec3d& obsHelioPos); 435 //! Return the last computed distance to the given position in heliocentric ecliptical (J2000) coordinates (in AU) getDistance(void) const436 double getDistance(void) const {return distance;} 437 setRings(Ring * r)438 void setRings(Ring* r) {rings = r;} 439 setSphereScale(double s)440 void setSphereScale(double s) { if(!fuzzyEquals(s, sphereScale)) { sphereScale = s; if(objModel) objModel->needsRescale=true; } } getSphereScale() const441 double getSphereScale() const { return sphereScale; } 442 getParent(void) const443 const QSharedPointer<Planet> getParent(void) const {return parent;} 444 setLabelColor(const Vec3f & lc)445 static void setLabelColor(const Vec3f& lc) {labelColor = lc;} getLabelColor(void)446 static const Vec3f& getLabelColor(void) {return labelColor;} 447 448 // update displayed elements. @param deltaTime: ms (since last call) 449 virtual void update(int deltaTime); 450 setFlagHints(bool b)451 void setFlagHints(bool b){hintFader = b;} getFlagHints(void) const452 bool getFlagHints(void) const {return hintFader;} 453 setFlagLabels(bool b)454 void setFlagLabels(bool b){flagLabels = b;} getFlagLabels(void) const455 bool getFlagLabels(void) const {return flagLabels;} 456 457 bool flagNativeName; setFlagNativeName(bool b)458 void setFlagNativeName(bool b) { flagNativeName = b; } getFlagNativeName(void) const459 bool getFlagNativeName(void) const { return flagNativeName; } 460 461 /////////////////////////////////////////////////////////////////////////// 462 ///// Orbit related code 463 // Should move to an OrbitPath class which works on a SolarSystemObject, not a Planet setFlagOrbits(bool b)464 void setFlagOrbits(bool b){orbitFader = b;} getFlagOrbits(void) const465 bool getFlagOrbits(void) const {return orbitFader;} 466 LinearFader orbitFader; 467 // draw orbital path of Planet 468 void drawOrbit(const StelCore*); 469 Vec3d orbit[ORBIT_SEGMENTS+1]; // store heliocentric coordinates for drawing the orbit 470 double deltaJDE; // time difference between positional updates. 471 double deltaOrbitJDE; 472 bool closeOrbit; // whether to connect the beginning of the orbit line to 473 // the end: good for elliptical orbits, bad for parabolic 474 // and hyperbolic orbits 475 476 static Vec3f orbitColor; setOrbitColor(const Vec3f & oc)477 static void setOrbitColor(const Vec3f& oc) {orbitColor = oc;} getOrbitColor()478 static const Vec3f& getOrbitColor() {return orbitColor;} 479 480 static Vec3f orbitMajorPlanetsColor; setMajorPlanetOrbitColor(const Vec3f & oc)481 static void setMajorPlanetOrbitColor(const Vec3f& oc) { orbitMajorPlanetsColor = oc;} getMajorPlanetOrbitColor()482 static const Vec3f& getMajorPlanetOrbitColor() {return orbitMajorPlanetsColor;} 483 484 static Vec3f orbitMoonsColor; setMoonOrbitColor(const Vec3f & oc)485 static void setMoonOrbitColor(const Vec3f& oc) { orbitMoonsColor = oc;} getMoonOrbitColor()486 static const Vec3f& getMoonOrbitColor() {return orbitMoonsColor;} 487 488 static Vec3f orbitMinorPlanetsColor; setMinorPlanetOrbitColor(const Vec3f & oc)489 static void setMinorPlanetOrbitColor(const Vec3f& oc) { orbitMinorPlanetsColor = oc;} getMinorPlanetOrbitColor()490 static const Vec3f& getMinorPlanetOrbitColor() {return orbitMinorPlanetsColor;} 491 492 static Vec3f orbitDwarfPlanetsColor; setDwarfPlanetOrbitColor(const Vec3f & oc)493 static void setDwarfPlanetOrbitColor(const Vec3f& oc) { orbitDwarfPlanetsColor = oc;} getDwarfPlanetOrbitColor()494 static const Vec3f& getDwarfPlanetOrbitColor() {return orbitDwarfPlanetsColor;} 495 496 static Vec3f orbitCubewanosColor; setCubewanoOrbitColor(const Vec3f & oc)497 static void setCubewanoOrbitColor(const Vec3f& oc) { orbitCubewanosColor = oc;} getCubewanoOrbitColor()498 static const Vec3f& getCubewanoOrbitColor() {return orbitCubewanosColor;} 499 500 static Vec3f orbitPlutinosColor; setPlutinoOrbitColor(const Vec3f & oc)501 static void setPlutinoOrbitColor(const Vec3f& oc) { orbitPlutinosColor = oc;} getPlutinoOrbitColor()502 static const Vec3f& getPlutinoOrbitColor() {return orbitPlutinosColor;} 503 504 static Vec3f orbitScatteredDiscObjectsColor; setScatteredDiscObjectOrbitColor(const Vec3f & oc)505 static void setScatteredDiscObjectOrbitColor(const Vec3f& oc) { orbitScatteredDiscObjectsColor = oc;} getScatteredDiscObjectOrbitColor()506 static const Vec3f& getScatteredDiscObjectOrbitColor() {return orbitScatteredDiscObjectsColor;} 507 508 static Vec3f orbitOortCloudObjectsColor; setOortCloudObjectOrbitColor(const Vec3f & oc)509 static void setOortCloudObjectOrbitColor(const Vec3f& oc) { orbitOortCloudObjectsColor = oc;} getOortCloudObjectOrbitColor()510 static const Vec3f& getOortCloudObjectOrbitColor() {return orbitOortCloudObjectsColor;} 511 512 static Vec3f orbitCometsColor; setCometOrbitColor(const Vec3f & oc)513 static void setCometOrbitColor(const Vec3f& oc) { orbitCometsColor = oc;} getCometOrbitColor()514 static const Vec3f& getCometOrbitColor() {return orbitCometsColor;} 515 516 static Vec3f orbitSednoidsColor; setSednoidOrbitColor(const Vec3f & oc)517 static void setSednoidOrbitColor(const Vec3f& oc) { orbitSednoidsColor = oc;} getSednoidOrbitColor()518 static const Vec3f& getSednoidOrbitColor() {return orbitSednoidsColor;} 519 520 static Vec3f orbitInterstellarColor; setInterstellarOrbitColor(const Vec3f & oc)521 static void setInterstellarOrbitColor(const Vec3f& oc) { orbitInterstellarColor = oc;} getInterstellarOrbitColor()522 static const Vec3f& getInterstellarOrbitColor() {return orbitInterstellarColor;} 523 524 static Vec3f orbitMercuryColor; setMercuryOrbitColor(const Vec3f & oc)525 static void setMercuryOrbitColor(const Vec3f& oc) { orbitMercuryColor = oc;} getMercuryOrbitColor()526 static const Vec3f& getMercuryOrbitColor() {return orbitMercuryColor;} 527 528 static Vec3f orbitVenusColor; setVenusOrbitColor(const Vec3f & oc)529 static void setVenusOrbitColor(const Vec3f& oc) { orbitVenusColor = oc;} getVenusOrbitColor()530 static const Vec3f& getVenusOrbitColor() {return orbitVenusColor;} 531 532 static Vec3f orbitEarthColor; setEarthOrbitColor(const Vec3f & oc)533 static void setEarthOrbitColor(const Vec3f& oc) { orbitEarthColor = oc;} getEarthOrbitColor()534 static const Vec3f& getEarthOrbitColor() {return orbitEarthColor;} 535 536 static Vec3f orbitMarsColor; setMarsOrbitColor(const Vec3f & oc)537 static void setMarsOrbitColor(const Vec3f& oc) { orbitMarsColor = oc;} getMarsOrbitColor()538 static const Vec3f& getMarsOrbitColor() {return orbitMarsColor;} 539 540 static Vec3f orbitJupiterColor; setJupiterOrbitColor(const Vec3f & oc)541 static void setJupiterOrbitColor(const Vec3f& oc) { orbitJupiterColor = oc;} getJupiterOrbitColor()542 static const Vec3f& getJupiterOrbitColor() {return orbitJupiterColor;} 543 544 static Vec3f orbitSaturnColor; setSaturnOrbitColor(const Vec3f & oc)545 static void setSaturnOrbitColor(const Vec3f& oc) { orbitSaturnColor = oc;} getSaturnOrbitColor()546 static const Vec3f& getSaturnOrbitColor() {return orbitSaturnColor;} 547 548 static Vec3f orbitUranusColor; setUranusOrbitColor(const Vec3f & oc)549 static void setUranusOrbitColor(const Vec3f& oc) { orbitUranusColor = oc;} getUranusOrbitColor()550 static const Vec3f& getUranusOrbitColor() {return orbitUranusColor;} 551 552 static Vec3f orbitNeptuneColor; setNeptuneOrbitColor(const Vec3f & oc)553 static void setNeptuneOrbitColor(const Vec3f& oc) { orbitNeptuneColor = oc;} getNeptuneOrbitColor()554 static const Vec3f& getNeptuneOrbitColor() {return orbitNeptuneColor;} 555 556 static PlanetOrbitColorStyle orbitColorStyle; 557 558 //! Return the list of planets which project some shadow on this planet 559 QVector<const Planet*> getCandidatesForShadow() const; 560 getAberrationPush() const561 Vec3d getAberrationPush() const {return aberrationPush; } 562 563 //! Compute times of nearest rise, transit and set for a solar system object for current location. 564 //! @param core the currently active StelCore object 565 //! @param altitude (optional; default=0) altitude of the object, degrees. 566 //! Setting this to -6. for the Sun will find begin and end for civil twilight. 567 //! @return Vec4d - time of rise, transit and set closest to current time; JD. 568 //! @note The fourth element flags particular conditions: 569 //! * +100. for circumpolar objects. Rise and set give lower culmination times. 570 //! * -100. for objects never rising. Rise and set give transit times. 571 //! * -1000. is used as "invalid" value. The result should then not be used. 572 //! @note This is based on Meeus, Astronomical Algorithms (2nd ed.), but deviates in details. 573 //! @note Limitation for efficiency: If this is a planet moon from another planet, we compute RTS for the parent planet instead! 574 virtual Vec4d getRTSTime(const StelCore* core, const double altitude=0.) const Q_DECL_OVERRIDE; 575 576 void resetTextures(); 577 void replaceTexture(const QString& texName); 578 579 protected: 580 // These components for getInfoString() can be overridden in subclasses 581 virtual QString getInfoStringName(const StelCore *core, const InfoStringGroup& flags) const; 582 virtual QString getInfoStringAbsoluteMagnitude(const StelCore *core, const InfoStringGroup& flags) const; 583 //! Any flag=Extra information to be displayed after the magnitude strings 584 virtual QString getInfoStringExtraMag(const StelCore *core, const InfoStringGroup& flags) const; 585 //! Any flag=Size information to be displayed 586 virtual QString getInfoStringSize(const StelCore *core, const InfoStringGroup& flags) const; 587 //! Return elongation and phase angle when flags=Elongation 588 virtual QString getInfoStringEloPhase(const StelCore *core, const InfoStringGroup& flags, const bool withIllum) const; 589 //! Return sidereal and synodic periods when flags=Extra 590 virtual QString getInfoStringPeriods(const StelCore *core, const InfoStringGroup& flags) const; 591 //! Any flag=Extra information to be displayed at the end 592 virtual QString getInfoStringExtra(const StelCore *core, const InfoStringGroup& flags) const; 593 594 protected: 595 struct PlanetOBJModel 596 { 597 PlanetOBJModel(); 598 ~PlanetOBJModel(); 599 600 //! Loads the data from the StelOBJ into the StelOpenGLArray 601 bool loadGL(); 602 603 void performScaling(double scale); 604 605 //! The BBox of the original model before any transformations 606 AABBox bbox; 607 //! Contains the original positions in model space in km, they need scaling and projection 608 QVector<Vec3f> posArray; 609 //! True when the positions need to be rescaled before drawing 610 bool needsRescale; 611 //! Contains the scaled positions (sphere scale in AU), need StelProjector transformation for display 612 QVector<Vec3f> scaledArray; 613 //! Used to store the projected array data, avoids re-allocation each frame 614 QVector<Vec3f> projectedPosArray; 615 //! An OpenGL buffer for the projected positions 616 QOpenGLBuffer* projPosBuffer; 617 //! The single texture to use 618 StelTextureSP texture; 619 //! The original StelOBJ data, deleted after loading to GL 620 StelOBJ* obj; 621 //! The opengl array, created by loadObjModel() but filled later in main thread 622 StelOpenGLArray* arr; 623 }; 624 625 static StelTextureSP texEarthShadow; // for lunar eclipses 626 627 // Used in drawSphere() to compute shadows, and inside a function to derive eclipse sizes. 628 // For reasons currently unknown we must handle solar eclipses as special case. 629 void computeModelMatrix(Mat4d &result, bool solarEclipseCase) const; 630 631 //! Update the orbit position values. 632 void computeOrbit(); 633 634 Vec3f getCurrentOrbitColor() const; 635 636 // Return the information string "ready to print" :) 637 QString getPlanetLabel() const; 638 639 // Draw the 3d model. Call the proper functions if there are rings etc.. 640 void draw3dModel(StelCore* core, StelProjector::ModelViewTranformP transfo, float screenSz, bool drawOnlyRing=false); 641 642 // Draws the OBJ model, assuming it is available 643 // @return false if the model can currently not be drawn (not loaded) 644 bool drawObjModel(StelPainter* painter, float screenSz); 645 646 bool drawObjShadowMap(StelPainter* painter, QMatrix4x4 &shadowMatrix); 647 648 //! Starts the OBJ loading process, if it has not been done yet. 649 //! Returns true when the OBJ is ready to draw 650 bool ensureObjLoaded(); 651 652 // Draw the 3D sphere 653 void drawSphere(StelPainter* painter, float screenSz, bool drawOnlyRing=false); 654 655 // Draw the Hips survey. 656 void drawSurvey(StelCore* core, StelPainter* painter); 657 658 // Draw the circle and name of the Planet 659 void drawHints(const StelCore* core, const QFont& planetNameFont); 660 661 PlanetOBJModel* loadObjModel() const; 662 663 QString englishName; // english planet name 664 QString nameI18; // International translated name 665 QString nativeName; // Can be used in a skyculture 666 QString nativeNameMeaning; // Can be used in a skyculture 667 QString nativeNameMeaningI18n; // Can be used in a skyculture 668 QString texMapName; // Texture file path 669 QString normalMapName; // Texture file path 670 RotationElements re; // Rotation and axis orientation parameters 671 double siderealPeriod; // sidereal period (Planet year or a moon's sidereal month) [earth days] 672 double equatorialRadius; // Planet's equatorial radius in AU 673 double oneMinusOblateness; // OneMinusOblateness=(polar radius)/(equatorial radius). Geometric flattening f=1-oneMinusOblateness (ExplanSup2013 10.1) 674 Vec3d eclipticPos; // Position in AU in the rectangular ecliptic coordinate system (J2000) centered on the parent body. 675 // To get heliocentric coordinates, use getHeliocentricEclipticPos() 676 Vec3d eclipticVelocity; // Speed in AU/d in the rectangular ecliptic coordinate system (J2000) around the parent body. 677 // NEW FEATURE in late 2017. For now, this may be 0/0/0 when we are not yet able to compute it. 678 // to get velocity, preferrably read getEclipticVelocity() and getHeliocentricEclipticVelocity() 679 // The "State Vector" [Heafner 1999] can be formed from (JDE, eclipticPos, eclipticVelocity) 680 Vec3d aberrationPush; // 0.21.2+: a small displacement to be applied if aberred positions are requested. 681 Vec3d screenPos; // Used to store temporarily the 2D position on screen. We need double for moons. Observe Styx from Pluto w/o atmosphere to see that. 682 Vec3f haloColor; // used for drawing the planet halo. Also, when non-spherical (OBJ) model without texture is used, its color is derived from haloColour*albedo. 683 684 float absoluteMagnitude; // since 2017 this moved to the Planet class: V(1,0) from Explanatory Supplement or WGCCRE2009 paper for the planets, H in the H,G magnitude system for Minor planets, H10 for comets. 685 // This is the apparent visual magnitude when 1AU from sun and observer, with zero phase angle. 686 float albedo; // Planet albedo. Used for magnitude computation when no other formula in use. Also, when non-spherical (OBJ) model without texture is used, its color is derived from haloColour*albedo. 687 float roughness; // Oren-Nayar roughness for Moon and OBJ-based models 688 float outgas_intensity; // The intensity of a pseudo-outgas effect, based on an inverse exponential Lambert shading, with the light at the viewing position 689 // Non-null only for Comets, but we use one shader for all Planets and derivatives, so we need a placeholder here. 690 float outgas_falloff; // Exponent for falloff of outgas effect, should probably be < 1 691 // Non-null only for Comets, but we use one shader for all Planets and derivatives, so we need a placeholder here. 692 Mat4d rotLocalToParent; // retro-documented: 693 // rotation matrix of axis orientation with respect to the rotation axes of the parent body. 694 // For planets, this is the axis orientation w.r.t. VSOP87A/J2000 ecliptical system. 695 // For planets' satellites, this used to be a rotation into the planet's equatorial system. 696 // 0.21+: if rot_pole... data available in ssystem_*.ini (and therefore re.method==WGCCRE), this is not the rotation from planet axes over ICRF to the VSOP frame on which Stellarium is defined. 697 // 698 float axisRotation; // Rotation angle of the Planet on its axis, degrees. 699 // For Earth, this should be Greenwich Mean Sidereal Time GMST. 700 // For V0.21+, and for planets computed after the IAU2009/WGCCRE papers this is angle W (rotDeg), 701 // i.e. angle between ascending node of body equator w.r.t. ICRF equator and its prime meridian. 702 StelTextureSP texMap; // Planet map texture 703 StelTextureSP normalMap; // Planet normal map texture 704 705 PlanetOBJModel* objModel; // Planet model (when it has been loaded) 706 QFuture<PlanetOBJModel*>* objModelLoader;// For async loading of the OBJ file 707 QString objModelPath; 708 709 HipsSurveyP survey; 710 711 Ring* rings; // Planet rings 712 double distance; // Temporary variable used to store the distance to a given point 713 // it is used for sorting while drawing 714 double sphereScale; // Artificial scaling for better viewing. 715 double lastJDE; // caches JDE of last positional computation 716 717 posFuncType coordFunc; // callback for the calculation of the equatorial rectangular heliocentric position at time JDE. 718 Orbit* orbitPtr; // Usually a KeplerOrbit for positional computations of Minor Planets, Comets and Moons. 719 // For an "observer", it is GimbalOrbit. 720 // For the major planets, it is Q_NULLPTR. 721 722 OsculatingFunctType *const osculatingFunc; 723 QSharedPointer<Planet> parent; // Planet parent i.e. sun for earth 724 QList<QSharedPointer<Planet> > satellites; // satellites of the Planet 725 LinearFader hintFader; 726 LinearFader labelsFader; // Store the current state of the label for this planet 727 bool flagLabels; // Define whether labels should be displayed 728 bool hidden; // useful for fake planets used as observation positions - not drawn or labeled 729 bool atmosphere; // Does the planet have an atmosphere? 730 bool halo; // Does the planet have a halo? 731 PlanetType pType; // Type of body 732 bool multisamplingEnabled_; 733 734 static ApparentMagnitudeAlgorithm vMagAlgorithm; 735 736 QOpenGLFunctions* gl; 737 738 static Vec3f labelColor; 739 static StelTextureSP hintCircleTex; 740 static const QMap<PlanetType, QString> pTypeMap; // Maps fast type to english name. 741 static const QMap<ApparentMagnitudeAlgorithm, QString> vMagAlgorithmMap; 742 static bool drawMoonHalo; 743 static bool drawSunHalo; 744 //! If true, planet orbits will be drawn even if planet is off screen. 745 static bool permanentDrawingOrbits; 746 static int orbitsThickness; 747 748 private: 749 class StelPropertyMgr* propMgr; 750 QString iauMoonNumber; 751 // File path for texture and normal map; both variables used for saving original names of files 752 QString texMapFileOrig; 753 QString normalMapFileOrig; 754 755 const QString getContextString() const; 756 QPair<double, double> getLunarEclipseMagnitudes() const; 757 758 // Shader-related variables 759 struct PlanetShaderVars { 760 // Vertex attributes 761 int texCoord; 762 int unprojectedVertex; 763 int vertex; 764 int normalIn; 765 766 // Common uniforms 767 int projectionMatrix; 768 int tex; 769 int lightDirection; 770 int eyeDirection; 771 int diffuseLight; 772 int ambientLight; 773 int shadowCount; 774 int shadowData; 775 int sunInfo; 776 int skyBrightness; 777 int orenNayarParameters; 778 int outgasParameters; 779 780 // Moon-specific variables 781 int earthShadow; 782 int eclipsePush; // apparent brightness push for partial Lunar Eclipse (make bright rim overbright) 783 int normalMap; 784 785 // Rings-specific variables 786 int isRing; 787 int ring; 788 int outerRadius; 789 int innerRadius; 790 int ringS; 791 792 // Shadowmap variables 793 int shadowMatrix; 794 int shadowTex; 795 int poissonDisk; 796 797 void initLocations(QOpenGLShaderProgram*); 798 }; 799 800 //! Encapsulates some calculated information used for rendering 801 struct RenderData 802 { 803 Mat4d modelMatrix; 804 Mat4d mTarget; 805 QVector<const Planet*> shadowCandidates; 806 QMatrix4x4 shadowCandidatesData; 807 Vec3d eyePos; 808 }; 809 810 //! Calculates and uploads the common shader uniforms (projection matrix, texture, lighting&shadow data) 811 RenderData setCommonShaderUniforms(const StelPainter &painter, QOpenGLShaderProgram* shader, const PlanetShaderVars& shaderVars); // const; 812 813 static PlanetShaderVars planetShaderVars; 814 static QOpenGLShaderProgram* planetShaderProgram; 815 816 static PlanetShaderVars ringPlanetShaderVars; 817 static QOpenGLShaderProgram* ringPlanetShaderProgram; 818 819 static PlanetShaderVars moonShaderVars; 820 static QOpenGLShaderProgram* moonShaderProgram; 821 822 static PlanetShaderVars objShaderVars; 823 static QOpenGLShaderProgram* objShaderProgram; 824 825 static PlanetShaderVars objShadowShaderVars; 826 static QOpenGLShaderProgram* objShadowShaderProgram; 827 828 static PlanetShaderVars transformShaderVars; 829 static QOpenGLShaderProgram* transformShaderProgram; 830 831 static bool shaderError; // True if loading shaders caused errors 832 833 static bool shadowInitialized; 834 static Vec2f shadowPolyOffset; 835 #ifdef DEBUG_SHADOWMAP 836 static QOpenGLFramebufferObject* shadowFBO; 837 #else 838 static unsigned int shadowFBO; 839 #endif 840 static unsigned int shadowTex; 841 842 843 static bool initShader(); 844 static void deinitShader(); 845 static bool initFBO(); 846 static void deinitFBO(); 847 848 static QOpenGLShaderProgram* createShader(const QString& name, 849 PlanetShaderVars& vars, 850 const QByteArray& vSrc, 851 const QByteArray& fSrc, 852 const QByteArray& prefix=QByteArray(), 853 const QMap<QByteArray,int>& fixedAttributeLocations=QMap<QByteArray,int>()); 854 855 // Cache of positions in the parent ecliptic coordinates in AU. 856 // Used only for orbit plotting 857 mutable QCache<double, Vec3d> orbitPositionsCache; 858 }; 859 860 #endif // PLANET_HPP 861