From b3e6f185b6c03565cabbf0d7ecdb5fb18db12c90 Mon Sep 17 00:00:00 2001 From: Don Cross Date: Mon, 8 Jul 2019 15:37:56 -0400 Subject: [PATCH] Reworking Python body codes as enumerated type Body. This will help documentation generator pydown.py organize body codes and other similar enumerations together as classes. --- generate/template/astronomy.py | 142 +++++++++++----------- generate/test.py | 208 ++++++++++++++++----------------- pydown/pydown.py | 24 ++++ source/python/.gitignore | 1 + source/python/README.md | 143 ----------------------- source/python/astronomy.py | 142 +++++++++++----------- 6 files changed, 275 insertions(+), 385 deletions(-) create mode 100644 source/python/.gitignore diff --git a/generate/template/astronomy.py b/generate/template/astronomy.py index 7236eedc..47d997ad 100644 --- a/generate/template/astronomy.py +++ b/generate/template/astronomy.py @@ -33,6 +33,7 @@ https://github.com/cosinekitty/astronomy import math import datetime +from enum import IntEnum, unique _PI2 = 2.0 * math.pi _EPOCH = datetime.datetime(2000, 1, 1, 12) @@ -84,40 +85,43 @@ class Vector: def Length(self): return math.sqrt(self.x**2 + self.y**2 + self.z**2) -BODY_INVALID = -1 +@unique +class Body(IntEnum): + Invalid = -1 + """A placeholder value for an unknown, undefined, or invalid body.""" -BODY_MERCURY = 0 -"""The body code for the planet Mercury.""" + Mercury = 0 + """The body code for the planet Mercury.""" -BODY_VENUS = 1 -"""The body code for the planet Venus.""" + Venus = 1 + """The body code for the planet Venus.""" -BODY_EARTH = 2 -"""The body code for the planet Earth.""" + Earth = 2 + """The body code for the planet Earth.""" -BODY_MARS = 3 -"""The body code for the planet Mars.""" + Mars = 3 + """The body code for the planet Mars.""" -BODY_JUPITER = 4 -"""The body code for the planet Jupiter.""" + Jupiter = 4 + """The body code for the planet Jupiter.""" -BODY_SATURN = 5 -"""The body code for the planet Saturn.""" + Saturn = 5 + """The body code for the planet Saturn.""" -BODY_URANUS = 6 -"""The body code for the planet Uranus.""" + Uranus = 6 + """The body code for the planet Uranus.""" -BODY_NEPTUNE = 7 -"""The body code for the planet Neptune.""" + Neptune = 7 + """The body code for the planet Neptune.""" -BODY_PLUTO = 8 -"""The body code for the planet Pluto.""" + Pluto = 8 + """The body code for the planet Pluto.""" -BODY_SUN = 9 -"""The body code for the Sun.""" + Sun = 9 + """The body code for the Sun.""" -BODY_MOON = 10 -"""The body code for the Moon.""" + Moon = 10 + """The body code for the Moon.""" BodyName = [ 'Mercury', @@ -134,12 +138,12 @@ BodyName = [ ] """The English names of the supported celestial bodies. -The list `BodyName` is indexed using one of the `BODY_...` constants. +The list `BodyName` is indexed using one of the `Body....` constants. Example ------- ->>> astronomy.BodyName[astronomy.BODY_JUPITER] +>>> astronomy.BodyName[astronomy.Body.Jupiter] 'Jupiter' """ @@ -157,7 +161,7 @@ def BodyCode(name): int If `name` is a valid body name, returns the integer value of the body code associated with that body. - Otherwise, returns `BODY_INVALID`. + Otherwise, returns `Body.Invalid`. Example ------- @@ -167,11 +171,11 @@ def BodyCode(name): """ if name not in BodyName: - return BODY_INVALID - return BodyName.index(name) + return Body.Invalid + return Body[name] def _IsSuperiorPlanet(body): - return body in [BODY_MARS, BODY_JUPITER, BODY_SATURN, BODY_URANUS, BODY_NEPTUNE, BODY_PLUTO] + return body in [Body.Mars, Body.Jupiter, Body.Saturn, Body.Uranus, Body.Neptune, Body.Pluto] _PlanetOrbitalPeriod = [ 87.969, @@ -210,11 +214,11 @@ class NoConvergeError(Error): Error.__init__(self, 'Numeric solver did not converge - please report issue at https://github.com/cosinekitty/astronomy/issues') def _SynodicPeriod(body): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() if body < 0 or body >= len(_PlanetOrbitalPeriod): raise InvalidBodyError() - if body == BODY_MOON: + if body == Body.Moon: return _MEAN_SYNODIC_MONTH return abs(_EARTH_ORBITAL_PERIOD / (_EARTH_ORBITAL_PERIOD/_PlanetOrbitalPeriod[body] - 1.0)) @@ -816,7 +820,7 @@ def _CalcVsop(model, time): return Vector(vx, vy, vz, time) def _CalcEarth(time): - return _CalcVsop(_vsop[BODY_EARTH], time) + return _CalcVsop(_vsop[Body.Earth], time) # END VSOP #---------------------------------------------------------------------------- @@ -964,16 +968,16 @@ def Search(func, context, t1, t2, dt_tolerance_seconds): #---------------------------------------------------------------------------- def HelioVector(body, time): - if body == BODY_PLUTO: + if body == Body.Pluto: return _CalcChebyshev(_pluto, time) if 0 <= body <= len(_vsop): return _CalcVsop(_vsop[body], time) - if body == BODY_SUN: + if body == Body.Sun: return Vector(0.0, 0.0, 0.0, time) - if body == BODY_MOON: + if body == Body.Moon: e = _CalcEarth(time) m = GeoMoon(time) return Vector(e.x+m.x, e.y+m.y, e.z+m.z, time) @@ -982,10 +986,10 @@ def HelioVector(body, time): def GeoVector(body, time, aberration): - if body == BODY_MOON: + if body == Body.Moon: return GeoMoon(time) - if body == BODY_EARTH: + if body == Body.Earth: return Vector(0.0, 0.0, 0.0, time) if not aberration: @@ -1011,7 +1015,7 @@ def GeoVector(body, time, aberration): earth = _CalcEarth(ltime) geo = Vector(h.x-earth.x, h.y-earth.y, h.z-earth.z, time) - if body == BODY_SUN: + if body == Body.Sun: # The Sun's heliocentric coordinates are always (0,0,0). No need to correct. return geo @@ -1181,23 +1185,23 @@ def Ecliptic(equ): return _RotateEquatorialToEcliptic([equ.x, equ.y, equ.z], ob2000) def EclipticLongitude(body, time): - if body == BODY_SUN: + if body == Body.Sun: raise InvalidBodyError() hv = HelioVector(body, time) eclip = Ecliptic(hv) return eclip.elon def AngleFromSun(body, time): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() - sv = GeoVector(BODY_SUN, time, True) + sv = GeoVector(Body.Sun, time, True) bv = GeoVector(body, time, True) return _AngleBetween(sv, bv) def LongitudeFromSun(body, time): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() - sv = GeoVector(BODY_SUN, time, True) + sv = GeoVector(Body.Sun, time, True) se = Ecliptic(sv) bv = GeoVector(body, time, True) be = Ecliptic(bv) @@ -1223,14 +1227,14 @@ def Elongation(body, time): def _rlon_offset(body, time, direction, targetRelLon): plon = EclipticLongitude(body, time) - elon = EclipticLongitude(BODY_EARTH, time) + elon = EclipticLongitude(Body.Earth, time) diff = direction * (elon - plon) return _LongitudeOffset(diff - targetRelLon) def SearchRelativeLongitude(body, targetRelLon, startTime): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() - if body == BODY_MOON or body == BODY_SUN: + if body == Body.Moon or body == Body.Sun: raise InvalidBodyError() syn = _SynodicPeriod(body) direction = +1 if _IsSuperiorPlanet(body) else -1 @@ -1270,10 +1274,10 @@ def _neg_elong_slope(body, time): return (e1 - e2)/dt def SearchMaxElongation(body, startTime): - if body == BODY_MERCURY: + if body == Body.Mercury: s1 = 50.0 s2 = 85.0 - elif body == BODY_VENUS: + elif body == Body.Venus: s1 = 40.0 s2 = 50.0 else: @@ -1282,7 +1286,7 @@ def SearchMaxElongation(body, startTime): iter = 1 while iter <= 2: plon = EclipticLongitude(body, startTime) - elon = EclipticLongitude(BODY_EARTH, startTime) + elon = EclipticLongitude(Body.Earth, startTime) rlon = _LongitudeOffset(plon - elon) # clamp to (-180, +180] # The slope function is not well-behaved when rlon is near 0 degrees or 180 degrees @@ -1360,7 +1364,7 @@ def SearchSunLongitude(targetLon, startTime, limitDays): return Search(_sun_offset, targetLon, startTime, t2, 1.0) def MoonPhase(time): - return LongitudeFromSun(BODY_MOON, time) + return LongitudeFromSun(Body.Moon, time) def _moon_offset(targetLon, time): angle = MoonPhase(time) @@ -1463,22 +1467,22 @@ def _SaturnMagnitude(phase, helio_dist, geo_dist, gc, time): def _VisualMagnitude(body, phase, helio_dist, geo_dist): # For Mercury and Venus, see: https://iopscience.iop.org/article/10.1086/430212 c0 = c1 = c2 = c3 = 0 - if body == BODY_MERCURY: + if body == Body.Mercury: c0 = -0.60; c1 = +4.98; c2 = -4.88; c3 = +3.02 - elif body == BODY_VENUS: + elif body == Body.Venus: if phase < 163.6: c0 = -4.47; c1 = +1.03; c2 = +0.57; c3 = +0.13 else: c0 = +0.98; c1 = -1.02 - elif body == BODY_MARS: + elif body == Body.Mars: c0 = -1.52; c1 = +1.60 - elif body == BODY_JUPITER: + elif body == Body.Jupiter: c0 = -9.40; c1 = +0.50 - elif body == BODY_URANUS: + elif body == Body.Uranus: c0 = -7.19; c1 = +0.25 - elif body == BODY_NEPTUNE: + elif body == Body.Neptune: c0 = -6.87 - elif body == BODY_PLUTO: + elif body == Body.Pluto: c0 = -1.00; c1 = +4.00 else: raise InvalidBodyError() @@ -1489,15 +1493,15 @@ def _VisualMagnitude(body, phase, helio_dist, geo_dist): return mag def Illumination(body, time): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() earth = _CalcEarth(time) - if body == BODY_SUN: + if body == Body.Sun: gc = Vector(-earth.x, -earth.y, -earth.z, time) hc = Vector(0.0, 0.0, 0.0, time) phase = 0.0 # placeholder value; the Sun does not have a phase angle. else: - if body == BODY_MOON: + if body == Body.Moon: # For extra numeric precision, use geocentric moon formula directly. gc = GeoMoon(time) hc = Vector(earth.x + gc.x, earth.y + gc.y, earth.z + gc.z, time) @@ -1510,11 +1514,11 @@ def Illumination(body, time): geo_dist = gc.Length() # distance from body to center of Earth helio_dist = hc.Length() # distance from body to center of Sun ring_tilt = None # only reported for Saturn - if body == BODY_SUN: + if body == Body.Sun: mag = -0.17 + 5.0*math.log10(geo_dist / _AU_PER_PARSEC) - elif body == BODY_MOON: + elif body == Body.Moon: mag = _MoonMagnitude(phase, helio_dist, geo_dist) - elif body == BODY_SATURN: + elif body == Body.Saturn: mag, ring_tilt = _SaturnMagnitude(phase, helio_dist, geo_dist, gc, time) else: mag = _VisualMagnitude(body, phase, helio_dist, geo_dist) @@ -1537,7 +1541,7 @@ def SearchPeakMagnitude(body, startTime): # s1 and s2 are relative longitudes within which peak magnitude of Venus can occur. s1 = 10.0 s2 = 30.0 - if body != BODY_VENUS: + if body != Body.Venus: raise InvalidBodyError() iter = 1 @@ -1545,7 +1549,7 @@ def SearchPeakMagnitude(body, startTime): # Find current heliocentric relative longitude between the # inferior planet and the Earth. plon = EclipticLongitude(body, startTime) - elon = EclipticLongitude(BODY_EARTH, startTime) + elon = EclipticLongitude(Body.Earth, startTime) rlon = _LongitudeOffset(plon - elon) # The slope function is not well-behaved when rlon is near 0 degrees or 180 degrees # because there is a cusp there that causes a discontinuity in the derivative. @@ -1619,7 +1623,7 @@ class HourAngleEvent: self.hor = hor def SearchHourAngle(body, observer, hourAngle, startTime): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() if hourAngle < 0.0 or hourAngle >= 24.0: @@ -1686,11 +1690,11 @@ def _peak_altitude(context, time): return context.direction * (alt + _REFRACTION_NEAR_HORIZON) def SearchRiseSet(body, observer, direction, startTime, limitDays): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() - elif body == BODY_SUN: + elif body == Body.Sun: body_radius = _SUN_RADIUS_AU - elif body == BODY_MOON: + elif body == Body.Moon: body_radius = _MOON_RADIUS_AU else: body_radius = 0.0 diff --git a/generate/test.py b/generate/test.py index 2deeb9a2..8be20d27 100755 --- a/generate/test.py +++ b/generate/test.py @@ -58,7 +58,7 @@ def Test_Issue46(): time = astronomy.Time(-93692.7685882873047376) print('time.ut = {:0.16f}'.format(time.ut)) print('time.tt = {:0.16f}'.format(time.tt)) - body = astronomy.BODY_SUN + body = astronomy.Body.Sun j2000 = astronomy.Equator(body, time, observer, False, False) print('j2000 ra = {:0.16f}'.format(j2000.ra)) print('j2000 dec = {:0.16f}'.format(j2000.dec)) @@ -80,19 +80,19 @@ def Test_AstroCheck(printflag): print('o {:0.6f} {:0.6f} {:0.6f}'.format(observer.latitude, observer.longitude, observer.height)) dt = 10 + math.pi/100 bodylist = [ - astronomy.BODY_SUN, astronomy.BODY_MOON, astronomy.BODY_MERCURY, astronomy.BODY_VENUS, - astronomy.BODY_EARTH, astronomy.BODY_MARS, astronomy.BODY_JUPITER, astronomy.BODY_SATURN, - astronomy.BODY_URANUS, astronomy.BODY_NEPTUNE, astronomy.BODY_PLUTO + astronomy.Body.Sun, astronomy.Body.Moon, astronomy.Body.Mercury, astronomy.Body.Venus, + astronomy.Body.Earth, astronomy.Body.Mars, astronomy.Body.Jupiter, astronomy.Body.Saturn, + astronomy.Body.Uranus, astronomy.Body.Neptune, astronomy.Body.Pluto ] while time.tt < stop.tt: for body in bodylist: name = astronomy.BodyName[body] - if body != astronomy.BODY_MOON: + if body != astronomy.Body.Moon: pos = astronomy.HelioVector(body, time) if printflag: print('v {} {:0.16f} {:0.16f} {:0.16f} {:0.16f}'.format(name, pos.t.tt, pos.x, pos.y, pos.z)) - if body != astronomy.BODY_EARTH: + if body != astronomy.Body.Earth: j2000 = astronomy.Equator(body, time, observer, False, False) ofdate = astronomy.Equator(body, time, observer, True, True) hor = astronomy.Horizon(time, observer, ofdate.ra, ofdate.dec, astronomy.REFRACTION_NONE) @@ -101,8 +101,8 @@ def Test_AstroCheck(printflag): pos = astronomy.GeoMoon(time) if printflag: print('v GM {:0.16f} {:0.16f} {:0.16f} {:0.16f}'.format(pos.t.tt, pos.x, pos.y, pos.z)) - j2000 = astronomy.Equator(astronomy.BODY_MOON, time, observer, False, False) - ofdate = astronomy.Equator(astronomy.BODY_MOON, time, observer, True, True) + j2000 = astronomy.Equator(astronomy.Body.Moon, time, observer, False, False) + ofdate = astronomy.Equator(astronomy.Body.Moon, time, observer, True, True) hor = astronomy.Horizon(time, observer, ofdate.ra, ofdate.dec, astronomy.REFRACTION_NONE) if printflag: print('s GM {:0.16f} {:0.16f} {:0.16f} {:0.16f} {:0.16f} {:0.16f} {:0.16f}'.format(time.tt, time.ut, j2000.ra, j2000.dec, j2000.dist, hor.azimuth, hor.altitude)) @@ -311,9 +311,9 @@ def TestPlanetLongitudes(body, outFileName, zeroLonEventName): # Search for the opposite longitude vent next time. time = found_time rlon = 180.0 - rlon - if body == astronomy.BODY_MERCURY: + if body == astronomy.Body.Mercury: thresh = 1.65 - elif body == astronomy.BODY_MARS: + elif body == astronomy.Body.Mars: thresh = 1.30 else: thresh = 1.07 @@ -327,81 +327,81 @@ def TestPlanetLongitudes(body, outFileName, zeroLonEventName): ElongTestData = [ # Max elongation data obtained from: # http://www.skycaramba.com/greatest_elongations.shtml - ( astronomy.BODY_MERCURY, "2010-01-17T05:22Z", "2010-01-27T05:22Z", 24.80, 'morning' ), - ( astronomy.BODY_MERCURY, "2010-05-16T02:15Z", "2010-05-26T02:15Z", 25.10, 'morning' ), - ( astronomy.BODY_MERCURY, "2010-09-09T17:24Z", "2010-09-19T17:24Z", 17.90, 'morning' ), - ( astronomy.BODY_MERCURY, "2010-12-30T14:33Z", "2011-01-09T14:33Z", 23.30, 'morning' ), - ( astronomy.BODY_MERCURY, "2011-04-27T19:03Z", "2011-05-07T19:03Z", 26.60, 'morning' ), - ( astronomy.BODY_MERCURY, "2011-08-24T05:52Z", "2011-09-03T05:52Z", 18.10, 'morning' ), - ( astronomy.BODY_MERCURY, "2011-12-13T02:56Z", "2011-12-23T02:56Z", 21.80, 'morning' ), - ( astronomy.BODY_MERCURY, "2012-04-08T17:22Z", "2012-04-18T17:22Z", 27.50, 'morning' ), - ( astronomy.BODY_MERCURY, "2012-08-06T12:04Z", "2012-08-16T12:04Z", 18.70, 'morning' ), - ( astronomy.BODY_MERCURY, "2012-11-24T22:55Z", "2012-12-04T22:55Z", 20.60, 'morning' ), - ( astronomy.BODY_MERCURY, "2013-03-21T22:02Z", "2013-03-31T22:02Z", 27.80, 'morning' ), - ( astronomy.BODY_MERCURY, "2013-07-20T08:51Z", "2013-07-30T08:51Z", 19.60, 'morning' ), - ( astronomy.BODY_MERCURY, "2013-11-08T02:28Z", "2013-11-18T02:28Z", 19.50, 'morning' ), - ( astronomy.BODY_MERCURY, "2014-03-04T06:38Z", "2014-03-14T06:38Z", 27.60, 'morning' ), - ( astronomy.BODY_MERCURY, "2014-07-02T18:22Z", "2014-07-12T18:22Z", 20.90, 'morning' ), - ( astronomy.BODY_MERCURY, "2014-10-22T12:36Z", "2014-11-01T12:36Z", 18.70, 'morning' ), - ( astronomy.BODY_MERCURY, "2015-02-14T16:20Z", "2015-02-24T16:20Z", 26.70, 'morning' ), - ( astronomy.BODY_MERCURY, "2015-06-14T17:10Z", "2015-06-24T17:10Z", 22.50, 'morning' ), - ( astronomy.BODY_MERCURY, "2015-10-06T03:20Z", "2015-10-16T03:20Z", 18.10, 'morning' ), - ( astronomy.BODY_MERCURY, "2016-01-28T01:22Z", "2016-02-07T01:22Z", 25.60, 'morning' ), - ( astronomy.BODY_MERCURY, "2016-05-26T08:45Z", "2016-06-05T08:45Z", 24.20, 'morning' ), - ( astronomy.BODY_MERCURY, "2016-09-18T19:27Z", "2016-09-28T19:27Z", 17.90, 'morning' ), - ( astronomy.BODY_MERCURY, "2017-01-09T09:42Z", "2017-01-19T09:42Z", 24.10, 'morning' ), - ( astronomy.BODY_MERCURY, "2017-05-07T23:19Z", "2017-05-17T23:19Z", 25.80, 'morning' ), - ( astronomy.BODY_MERCURY, "2017-09-02T10:14Z", "2017-09-12T10:14Z", 17.90, 'morning' ), - ( astronomy.BODY_MERCURY, "2017-12-22T19:48Z", "2018-01-01T19:48Z", 22.70, 'morning' ), - ( astronomy.BODY_MERCURY, "2018-04-19T18:17Z", "2018-04-29T18:17Z", 27.00, 'morning' ), - ( astronomy.BODY_MERCURY, "2018-08-16T20:35Z", "2018-08-26T20:35Z", 18.30, 'morning' ), - ( astronomy.BODY_MERCURY, "2018-12-05T11:34Z", "2018-12-15T11:34Z", 21.30, 'morning' ), - ( astronomy.BODY_MERCURY, "2019-04-01T19:40Z", "2019-04-11T19:40Z", 27.70, 'morning' ), - ( astronomy.BODY_MERCURY, "2019-07-30T23:08Z", "2019-08-09T23:08Z", 19.00, 'morning' ), - ( astronomy.BODY_MERCURY, "2019-11-18T10:31Z", "2019-11-28T10:31Z", 20.10, 'morning' ), - ( astronomy.BODY_MERCURY, "2010-03-29T23:32Z", "2010-04-08T23:32Z", 19.40, 'evening' ), - ( astronomy.BODY_MERCURY, "2010-07-28T01:03Z", "2010-08-07T01:03Z", 27.40, 'evening' ), - ( astronomy.BODY_MERCURY, "2010-11-21T15:42Z", "2010-12-01T15:42Z", 21.50, 'evening' ), - ( astronomy.BODY_MERCURY, "2011-03-13T01:07Z", "2011-03-23T01:07Z", 18.60, 'evening' ), - ( astronomy.BODY_MERCURY, "2011-07-10T04:56Z", "2011-07-20T04:56Z", 26.80, 'evening' ), - ( astronomy.BODY_MERCURY, "2011-11-04T08:40Z", "2011-11-14T08:40Z", 22.70, 'evening' ), - ( astronomy.BODY_MERCURY, "2012-02-24T09:39Z", "2012-03-05T09:39Z", 18.20, 'evening' ), - ( astronomy.BODY_MERCURY, "2012-06-21T02:00Z", "2012-07-01T02:00Z", 25.70, 'evening' ), - ( astronomy.BODY_MERCURY, "2012-10-16T21:59Z", "2012-10-26T21:59Z", 24.10, 'evening' ), - ( astronomy.BODY_MERCURY, "2013-02-06T21:24Z", "2013-02-16T21:24Z", 18.10, 'evening' ), - ( astronomy.BODY_MERCURY, "2013-06-02T16:45Z", "2013-06-12T16:45Z", 24.30, 'evening' ), - ( astronomy.BODY_MERCURY, "2013-09-29T09:59Z", "2013-10-09T09:59Z", 25.30, 'evening' ), - ( astronomy.BODY_MERCURY, "2014-01-21T10:00Z", "2014-01-31T10:00Z", 18.40, 'evening' ), - ( astronomy.BODY_MERCURY, "2014-05-15T07:06Z", "2014-05-25T07:06Z", 22.70, 'evening' ), - ( astronomy.BODY_MERCURY, "2014-09-11T22:20Z", "2014-09-21T22:20Z", 26.40, 'evening' ), - ( astronomy.BODY_MERCURY, "2015-01-04T20:26Z", "2015-01-14T20:26Z", 18.90, 'evening' ), - ( astronomy.BODY_MERCURY, "2015-04-27T04:46Z", "2015-05-07T04:46Z", 21.20, 'evening' ), - ( astronomy.BODY_MERCURY, "2015-08-25T10:20Z", "2015-09-04T10:20Z", 27.10, 'evening' ), - ( astronomy.BODY_MERCURY, "2015-12-19T03:11Z", "2015-12-29T03:11Z", 19.70, 'evening' ), - ( astronomy.BODY_MERCURY, "2016-04-08T14:00Z", "2016-04-18T14:00Z", 19.90, 'evening' ), - ( astronomy.BODY_MERCURY, "2016-08-06T21:24Z", "2016-08-16T21:24Z", 27.40, 'evening' ), - ( astronomy.BODY_MERCURY, "2016-12-01T04:36Z", "2016-12-11T04:36Z", 20.80, 'evening' ), - ( astronomy.BODY_MERCURY, "2017-03-22T10:24Z", "2017-04-01T10:24Z", 19.00, 'evening' ), - ( astronomy.BODY_MERCURY, "2017-07-20T04:34Z", "2017-07-30T04:34Z", 27.20, 'evening' ), - ( astronomy.BODY_MERCURY, "2017-11-14T00:32Z", "2017-11-24T00:32Z", 22.00, 'evening' ), - ( astronomy.BODY_MERCURY, "2018-03-05T15:07Z", "2018-03-15T15:07Z", 18.40, 'evening' ), - ( astronomy.BODY_MERCURY, "2018-07-02T05:24Z", "2018-07-12T05:24Z", 26.40, 'evening' ), - ( astronomy.BODY_MERCURY, "2018-10-27T15:25Z", "2018-11-06T15:25Z", 23.30, 'evening' ), - ( astronomy.BODY_MERCURY, "2019-02-17T01:23Z", "2019-02-27T01:23Z", 18.10, 'evening' ), - ( astronomy.BODY_MERCURY, "2019-06-13T23:14Z", "2019-06-23T23:14Z", 25.20, 'evening' ), - ( astronomy.BODY_MERCURY, "2019-10-10T04:00Z", "2019-10-20T04:00Z", 24.60, 'evening' ), - ( astronomy.BODY_VENUS, "2010-12-29T15:57Z", "2011-01-08T15:57Z", 47.00, 'morning' ), - ( astronomy.BODY_VENUS, "2012-08-05T08:59Z", "2012-08-15T08:59Z", 45.80, 'morning' ), - ( astronomy.BODY_VENUS, "2014-03-12T19:25Z", "2014-03-22T19:25Z", 46.60, 'morning' ), - ( astronomy.BODY_VENUS, "2015-10-16T06:57Z", "2015-10-26T06:57Z", 46.40, 'morning' ), - ( astronomy.BODY_VENUS, "2017-05-24T13:09Z", "2017-06-03T13:09Z", 45.90, 'morning' ), - ( astronomy.BODY_VENUS, "2018-12-27T04:24Z", "2019-01-06T04:24Z", 47.00, 'morning' ), - ( astronomy.BODY_VENUS, "2010-08-10T03:19Z", "2010-08-20T03:19Z", 46.00, 'evening' ), - ( astronomy.BODY_VENUS, "2012-03-17T08:03Z", "2012-03-27T08:03Z", 46.00, 'evening' ), - ( astronomy.BODY_VENUS, "2013-10-22T08:00Z", "2013-11-01T08:00Z", 47.10, 'evening' ), - ( astronomy.BODY_VENUS, "2015-05-27T18:46Z", "2015-06-06T18:46Z", 45.40, 'evening' ), - ( astronomy.BODY_VENUS, "2017-01-02T13:19Z", "2017-01-12T13:19Z", 47.10, 'evening' ), - ( astronomy.BODY_VENUS, "2018-08-07T17:02Z", "2018-08-17T17:02Z", 45.90, 'evening' ) + ( astronomy.Body.Mercury, "2010-01-17T05:22Z", "2010-01-27T05:22Z", 24.80, 'morning' ), + ( astronomy.Body.Mercury, "2010-05-16T02:15Z", "2010-05-26T02:15Z", 25.10, 'morning' ), + ( astronomy.Body.Mercury, "2010-09-09T17:24Z", "2010-09-19T17:24Z", 17.90, 'morning' ), + ( astronomy.Body.Mercury, "2010-12-30T14:33Z", "2011-01-09T14:33Z", 23.30, 'morning' ), + ( astronomy.Body.Mercury, "2011-04-27T19:03Z", "2011-05-07T19:03Z", 26.60, 'morning' ), + ( astronomy.Body.Mercury, "2011-08-24T05:52Z", "2011-09-03T05:52Z", 18.10, 'morning' ), + ( astronomy.Body.Mercury, "2011-12-13T02:56Z", "2011-12-23T02:56Z", 21.80, 'morning' ), + ( astronomy.Body.Mercury, "2012-04-08T17:22Z", "2012-04-18T17:22Z", 27.50, 'morning' ), + ( astronomy.Body.Mercury, "2012-08-06T12:04Z", "2012-08-16T12:04Z", 18.70, 'morning' ), + ( astronomy.Body.Mercury, "2012-11-24T22:55Z", "2012-12-04T22:55Z", 20.60, 'morning' ), + ( astronomy.Body.Mercury, "2013-03-21T22:02Z", "2013-03-31T22:02Z", 27.80, 'morning' ), + ( astronomy.Body.Mercury, "2013-07-20T08:51Z", "2013-07-30T08:51Z", 19.60, 'morning' ), + ( astronomy.Body.Mercury, "2013-11-08T02:28Z", "2013-11-18T02:28Z", 19.50, 'morning' ), + ( astronomy.Body.Mercury, "2014-03-04T06:38Z", "2014-03-14T06:38Z", 27.60, 'morning' ), + ( astronomy.Body.Mercury, "2014-07-02T18:22Z", "2014-07-12T18:22Z", 20.90, 'morning' ), + ( astronomy.Body.Mercury, "2014-10-22T12:36Z", "2014-11-01T12:36Z", 18.70, 'morning' ), + ( astronomy.Body.Mercury, "2015-02-14T16:20Z", "2015-02-24T16:20Z", 26.70, 'morning' ), + ( astronomy.Body.Mercury, "2015-06-14T17:10Z", "2015-06-24T17:10Z", 22.50, 'morning' ), + ( astronomy.Body.Mercury, "2015-10-06T03:20Z", "2015-10-16T03:20Z", 18.10, 'morning' ), + ( astronomy.Body.Mercury, "2016-01-28T01:22Z", "2016-02-07T01:22Z", 25.60, 'morning' ), + ( astronomy.Body.Mercury, "2016-05-26T08:45Z", "2016-06-05T08:45Z", 24.20, 'morning' ), + ( astronomy.Body.Mercury, "2016-09-18T19:27Z", "2016-09-28T19:27Z", 17.90, 'morning' ), + ( astronomy.Body.Mercury, "2017-01-09T09:42Z", "2017-01-19T09:42Z", 24.10, 'morning' ), + ( astronomy.Body.Mercury, "2017-05-07T23:19Z", "2017-05-17T23:19Z", 25.80, 'morning' ), + ( astronomy.Body.Mercury, "2017-09-02T10:14Z", "2017-09-12T10:14Z", 17.90, 'morning' ), + ( astronomy.Body.Mercury, "2017-12-22T19:48Z", "2018-01-01T19:48Z", 22.70, 'morning' ), + ( astronomy.Body.Mercury, "2018-04-19T18:17Z", "2018-04-29T18:17Z", 27.00, 'morning' ), + ( astronomy.Body.Mercury, "2018-08-16T20:35Z", "2018-08-26T20:35Z", 18.30, 'morning' ), + ( astronomy.Body.Mercury, "2018-12-05T11:34Z", "2018-12-15T11:34Z", 21.30, 'morning' ), + ( astronomy.Body.Mercury, "2019-04-01T19:40Z", "2019-04-11T19:40Z", 27.70, 'morning' ), + ( astronomy.Body.Mercury, "2019-07-30T23:08Z", "2019-08-09T23:08Z", 19.00, 'morning' ), + ( astronomy.Body.Mercury, "2019-11-18T10:31Z", "2019-11-28T10:31Z", 20.10, 'morning' ), + ( astronomy.Body.Mercury, "2010-03-29T23:32Z", "2010-04-08T23:32Z", 19.40, 'evening' ), + ( astronomy.Body.Mercury, "2010-07-28T01:03Z", "2010-08-07T01:03Z", 27.40, 'evening' ), + ( astronomy.Body.Mercury, "2010-11-21T15:42Z", "2010-12-01T15:42Z", 21.50, 'evening' ), + ( astronomy.Body.Mercury, "2011-03-13T01:07Z", "2011-03-23T01:07Z", 18.60, 'evening' ), + ( astronomy.Body.Mercury, "2011-07-10T04:56Z", "2011-07-20T04:56Z", 26.80, 'evening' ), + ( astronomy.Body.Mercury, "2011-11-04T08:40Z", "2011-11-14T08:40Z", 22.70, 'evening' ), + ( astronomy.Body.Mercury, "2012-02-24T09:39Z", "2012-03-05T09:39Z", 18.20, 'evening' ), + ( astronomy.Body.Mercury, "2012-06-21T02:00Z", "2012-07-01T02:00Z", 25.70, 'evening' ), + ( astronomy.Body.Mercury, "2012-10-16T21:59Z", "2012-10-26T21:59Z", 24.10, 'evening' ), + ( astronomy.Body.Mercury, "2013-02-06T21:24Z", "2013-02-16T21:24Z", 18.10, 'evening' ), + ( astronomy.Body.Mercury, "2013-06-02T16:45Z", "2013-06-12T16:45Z", 24.30, 'evening' ), + ( astronomy.Body.Mercury, "2013-09-29T09:59Z", "2013-10-09T09:59Z", 25.30, 'evening' ), + ( astronomy.Body.Mercury, "2014-01-21T10:00Z", "2014-01-31T10:00Z", 18.40, 'evening' ), + ( astronomy.Body.Mercury, "2014-05-15T07:06Z", "2014-05-25T07:06Z", 22.70, 'evening' ), + ( astronomy.Body.Mercury, "2014-09-11T22:20Z", "2014-09-21T22:20Z", 26.40, 'evening' ), + ( astronomy.Body.Mercury, "2015-01-04T20:26Z", "2015-01-14T20:26Z", 18.90, 'evening' ), + ( astronomy.Body.Mercury, "2015-04-27T04:46Z", "2015-05-07T04:46Z", 21.20, 'evening' ), + ( astronomy.Body.Mercury, "2015-08-25T10:20Z", "2015-09-04T10:20Z", 27.10, 'evening' ), + ( astronomy.Body.Mercury, "2015-12-19T03:11Z", "2015-12-29T03:11Z", 19.70, 'evening' ), + ( astronomy.Body.Mercury, "2016-04-08T14:00Z", "2016-04-18T14:00Z", 19.90, 'evening' ), + ( astronomy.Body.Mercury, "2016-08-06T21:24Z", "2016-08-16T21:24Z", 27.40, 'evening' ), + ( astronomy.Body.Mercury, "2016-12-01T04:36Z", "2016-12-11T04:36Z", 20.80, 'evening' ), + ( astronomy.Body.Mercury, "2017-03-22T10:24Z", "2017-04-01T10:24Z", 19.00, 'evening' ), + ( astronomy.Body.Mercury, "2017-07-20T04:34Z", "2017-07-30T04:34Z", 27.20, 'evening' ), + ( astronomy.Body.Mercury, "2017-11-14T00:32Z", "2017-11-24T00:32Z", 22.00, 'evening' ), + ( astronomy.Body.Mercury, "2018-03-05T15:07Z", "2018-03-15T15:07Z", 18.40, 'evening' ), + ( astronomy.Body.Mercury, "2018-07-02T05:24Z", "2018-07-12T05:24Z", 26.40, 'evening' ), + ( astronomy.Body.Mercury, "2018-10-27T15:25Z", "2018-11-06T15:25Z", 23.30, 'evening' ), + ( astronomy.Body.Mercury, "2019-02-17T01:23Z", "2019-02-27T01:23Z", 18.10, 'evening' ), + ( astronomy.Body.Mercury, "2019-06-13T23:14Z", "2019-06-23T23:14Z", 25.20, 'evening' ), + ( astronomy.Body.Mercury, "2019-10-10T04:00Z", "2019-10-20T04:00Z", 24.60, 'evening' ), + ( astronomy.Body.Venus, "2010-12-29T15:57Z", "2011-01-08T15:57Z", 47.00, 'morning' ), + ( astronomy.Body.Venus, "2012-08-05T08:59Z", "2012-08-15T08:59Z", 45.80, 'morning' ), + ( astronomy.Body.Venus, "2014-03-12T19:25Z", "2014-03-22T19:25Z", 46.60, 'morning' ), + ( astronomy.Body.Venus, "2015-10-16T06:57Z", "2015-10-26T06:57Z", 46.40, 'morning' ), + ( astronomy.Body.Venus, "2017-05-24T13:09Z", "2017-06-03T13:09Z", 45.90, 'morning' ), + ( astronomy.Body.Venus, "2018-12-27T04:24Z", "2019-01-06T04:24Z", 47.00, 'morning' ), + ( astronomy.Body.Venus, "2010-08-10T03:19Z", "2010-08-20T03:19Z", 46.00, 'evening' ), + ( astronomy.Body.Venus, "2012-03-17T08:03Z", "2012-03-27T08:03Z", 46.00, 'evening' ), + ( astronomy.Body.Venus, "2013-10-22T08:00Z", "2013-11-01T08:00Z", 47.10, 'evening' ), + ( astronomy.Body.Venus, "2015-05-27T18:46Z", "2015-06-06T18:46Z", 45.40, 'evening' ), + ( astronomy.Body.Venus, "2017-01-02T13:19Z", "2017-01-12T13:19Z", 47.10, 'evening' ), + ( astronomy.Body.Venus, "2018-08-07T17:02Z", "2018-08-17T17:02Z", 45.90, 'evening' ) ] def ParseDate(text): @@ -443,14 +443,14 @@ def SearchElongTest(): def Test_Elongation(): if 0 != TestElongFile('longitude/opposition_2018.txt', 0.0): return 1 - if 0 != TestPlanetLongitudes(astronomy.BODY_MERCURY, "temp/py_longitude_Mercury.txt", "inf"): return 1 - if 0 != TestPlanetLongitudes(astronomy.BODY_VENUS, "temp/py_longitude_Venus.txt", "inf"): return 1 - if 0 != TestPlanetLongitudes(astronomy.BODY_MARS, "temp/py_longitude_Mars.txt", "opp"): return 1 - if 0 != TestPlanetLongitudes(astronomy.BODY_JUPITER, "temp/py_longitude_Jupiter.txt", "opp"): return 1 - if 0 != TestPlanetLongitudes(astronomy.BODY_SATURN, "temp/py_longitude_Saturn.txt", "opp"): return 1 - if 0 != TestPlanetLongitudes(astronomy.BODY_URANUS, "temp/py_longitude_Uranus.txt", "opp"): return 1 - if 0 != TestPlanetLongitudes(astronomy.BODY_NEPTUNE, "temp/py_longitude_Neptune.txt", "opp"): return 1 - if 0 != TestPlanetLongitudes(astronomy.BODY_PLUTO, "temp/py_longitude_Pluto.txt", "opp"): return 1 + if 0 != TestPlanetLongitudes(astronomy.Body.Mercury, "temp/py_longitude_Mercury.txt", "inf"): return 1 + if 0 != TestPlanetLongitudes(astronomy.Body.Venus, "temp/py_longitude_Venus.txt", "inf"): return 1 + if 0 != TestPlanetLongitudes(astronomy.Body.Mars, "temp/py_longitude_Mars.txt", "opp"): return 1 + if 0 != TestPlanetLongitudes(astronomy.Body.Jupiter, "temp/py_longitude_Jupiter.txt", "opp"): return 1 + if 0 != TestPlanetLongitudes(astronomy.Body.Saturn, "temp/py_longitude_Saturn.txt", "opp"): return 1 + if 0 != TestPlanetLongitudes(astronomy.Body.Uranus, "temp/py_longitude_Uranus.txt", "opp"): return 1 + if 0 != TestPlanetLongitudes(astronomy.Body.Neptune, "temp/py_longitude_Neptune.txt", "opp"): return 1 + if 0 != TestPlanetLongitudes(astronomy.Body.Pluto, "temp/py_longitude_Pluto.txt", "opp"): return 1 if 0 != SearchElongTest(): return 1 return 0 @@ -520,7 +520,7 @@ def CheckSaturn(): ] for (dtext, mag, tilt) in data: time = ParseDate(dtext) - illum = astronomy.Illumination(astronomy.BODY_SATURN, time) + illum = astronomy.Illumination(astronomy.Body.Saturn, time) print('Saturn: date={} calc mag={:12.8f} ring_tilt={:12.8f}'.format(dtext, illum.mag, illum.ring_tilt)) mag_diff = abs(illum.mag - mag) if mag_diff > 1.0e-8: @@ -570,17 +570,17 @@ def TestMaxMag(body, filename): def Test_Magnitude(): nfailed = 0 - nfailed += CheckMagnitudeData(astronomy.BODY_SUN, 'magnitude/Sun.txt') - nfailed += CheckMagnitudeData(astronomy.BODY_MOON, 'magnitude/Moon.txt') - nfailed += CheckMagnitudeData(astronomy.BODY_MERCURY, 'magnitude/Mercury.txt') - nfailed += CheckMagnitudeData(astronomy.BODY_VENUS, 'magnitude/Venus.txt') - nfailed += CheckMagnitudeData(astronomy.BODY_MARS, 'magnitude/Mars.txt') - nfailed += CheckMagnitudeData(astronomy.BODY_JUPITER, 'magnitude/Jupiter.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Sun, 'magnitude/Sun.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Moon, 'magnitude/Moon.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Mercury, 'magnitude/Mercury.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Venus, 'magnitude/Venus.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Mars, 'magnitude/Mars.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Jupiter, 'magnitude/Jupiter.txt') nfailed += CheckSaturn() - nfailed += CheckMagnitudeData(astronomy.BODY_URANUS, 'magnitude/Uranus.txt') - nfailed += CheckMagnitudeData(astronomy.BODY_NEPTUNE, 'magnitude/Neptune.txt') - nfailed += CheckMagnitudeData(astronomy.BODY_PLUTO, 'magnitude/Pluto.txt') - nfailed += TestMaxMag(astronomy.BODY_VENUS, 'magnitude/maxmag_Venus.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Uranus, 'magnitude/Uranus.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Neptune, 'magnitude/Neptune.txt') + nfailed += CheckMagnitudeData(astronomy.Body.Pluto, 'magnitude/Pluto.txt') + nfailed += TestMaxMag(astronomy.Body.Venus, 'magnitude/maxmag_Venus.txt') if nfailed > 0: print('Test_Magnitude: failed {} test(s).'.format(nfailed)) return 1 diff --git a/pydown/pydown.py b/pydown/pydown.py index 743bae1d..5e17d61b 100755 --- a/pydown/pydown.py +++ b/pydown/pydown.py @@ -2,6 +2,7 @@ import sys import os import importlib +import inspect def PrintUsage(): print(""" @@ -20,12 +21,35 @@ def LoadModule(inPythonFileName): module = importlib.import_module(modname) return module +def Markdown(module): + funclist = [] + classlist = [] + for name, obj in inspect.getmembers(module): + if not name.startswith('_'): + if inspect.isfunction(obj): + funclist.append(obj) + elif inspect.isclass(obj): + classlist.append(obj) + elif inspect.ismodule(obj): + pass # ignore other modules pulled in + else: + print('other', name) + return '' + def main(): if len(sys.argv) != 3: return PrintUsage() inPythonFileName = sys.argv[1] outMarkdownFileName = sys.argv[2] + # Delete output file before we begin. + # That way, if anything goes wrong, it won't exist, + # and thus the error becomes conspicuous to scripts/tools. + if os.access(outMarkdownFileName, os.F_OK): + os.remove(outMarkdownFileName) module = LoadModule(inPythonFileName) + md = Markdown(module) + with open(outMarkdownFileName, 'wt') as outfile: + outfile.write(md) return 0 if __name__ == '__main__': diff --git a/source/python/.gitignore b/source/python/.gitignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/source/python/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/source/python/README.md b/source/python/README.md index 41acccd2..e69de29b 100644 --- a/source/python/README.md +++ b/source/python/README.md @@ -1,143 +0,0 @@ -# astronomy -Astronomy Engine by Don Cross - -See the GitHub project page for full documentation, examples, -and other information: - -https://github.com/cosinekitty/astronomy - - -## BodyCode -```python -BodyCode(name) -``` -Finds the integer body code given the name of a body. - -Parameters ----------- -name: str - The common English name of a supported celestial body. - -Returns -------- -int - If `name` is a valid body name, returns the integer value - of the body code associated with that body. - Otherwise, returns `BODY_INVALID`. - -Example -------- - ->>> astronomy.BodyCode('Mars') -3 - - -## Time -```python -Time(self, ut) -``` -Represents a date and time used for performing astronomy calculations. - -All calculations performed by Astronomy Engine are based on -dates and times represented by `Time` objects. - -Parameters ----------- -ut : float - UT1/UTC number of days since noon on January 1, 2000. - See the `ut` attribute of this class for more details. - -Attributes ----------- -ut : float - The floating point number of days of Universal Time since noon UTC January 1, 2000. - Astronomy Engine approximates UTC and UT1 as being the same thing, although they are - not exactly equivalent; UTC and UT1 can disagree by up to 0.9 seconds. - This approximation is sufficient for the accuracy requirements of Astronomy Engine. - Universal Time Coordinate (UTC) is the international standard for legal and civil - timekeeping and replaces the older Greenwich Mean Time (GMT) standard. - UTC is kept in sync with unpredictable observed changes in the Earth's rotation - by occasionally adding leap seconds as needed. - UT1 is an idealized time scale based on observed rotation of the Earth, which - gradually slows down in an unpredictable way over time, due to tidal drag by the Moon and Sun, - large scale weather events like hurricanes, and internal seismic and convection effects. - Conceptually, UT1 drifts from atomic time continuously and erratically, whereas UTC - is adjusted by a scheduled whole number of leap seconds as needed. - The value in `ut` is appropriate for any calculation involving the Earth's rotation, - such as calculating rise/set times, culumination, and anything involving apparent - sidereal time. - Before the era of atomic timekeeping, days based on the Earth's rotation - were often known as mean solar days. -tt : float - Terrestrial Time days since noon on January 1, 2000. - Terrestrial Time is an atomic time scale defined as a number of days since noon on January 1, 2000. - In this system, days are not based on Earth rotations, but instead by - the number of elapsed [SI seconds](https://physics.nist.gov/cuu/Units/second.html) - divided by 86400. Unlike `ut`, `tt` increases uniformly without adjustments - for changes in the Earth's rotation. - The value in `tt` is used for calculations of movements not involving the Earth's rotation, - such as the orbits of planets around the Sun, or the Moon around the Earth. - Historically, Terrestrial Time has also been known by the term Ephemeris Time (ET). - -### Make -```python -Time.Make(year, month, day, hour, minute, second) -``` -Creates a `Time` object from a UTC calendar date and time. - -Parameters ----------- -year : int - The UTC 4-digit year value, e.g. 2019. -month : int - The UTC month in the range 1..12. -day : int - The UTC day of the month, in the range 1..31. -hour : int - The UTC hour, in the range 0..23. -minute : int - The UTC minute, in the range 0..59. -second : float - The real-valued UTC second, in the range [0, 60). - -Returns -------- -Time - -## Observer -```python -Observer(self, latitude, longitude, height=0) -``` -Represents the geographic location of an observer on the surface of the Earth. - -:param latitude: Geographic latitude in degrees north of the equator. -:param longitude: Geographic longitude in degrees east of the prime meridian at Greenwich, England. -:param height: Elevation above sea level in meters. - -## GeoMoon -```python -GeoMoon(time) -``` -Calculates the geocentric position of the Moon at a given time. - -Given a time of observation, calculates the Moon's position as a vector. -The vector gives the location of the Moon's center relative to the Earth's center -with x-, y-, and z-components measured in astronomical units. - -This algorithm is based on Nautical Almanac Office's Improved Lunar Ephemeris of 1954, -which in turn derives from E. W. Brown's lunar theories from the early twentieth century. -It is adapted from Turbo Pascal code from the book -[Astronomy on the Personal Computer](https://www.springer.com/us/book/9783540672210) -by Montenbruck and Pfleger. - -Parameters ----------- -time : Time - The date and time for which to calculate the Moon's position. - -Returns -------- -Vector - The Moon's position as a vector in J2000 Cartesian equatorial coordinates. - - diff --git a/source/python/astronomy.py b/source/python/astronomy.py index 17eb14a0..562cae34 100644 --- a/source/python/astronomy.py +++ b/source/python/astronomy.py @@ -33,6 +33,7 @@ https://github.com/cosinekitty/astronomy import math import datetime +from enum import IntEnum, unique _PI2 = 2.0 * math.pi _EPOCH = datetime.datetime(2000, 1, 1, 12) @@ -84,40 +85,43 @@ class Vector: def Length(self): return math.sqrt(self.x**2 + self.y**2 + self.z**2) -BODY_INVALID = -1 +@unique +class Body(IntEnum): + Invalid = -1 + """A placeholder value for an unknown, undefined, or invalid body.""" -BODY_MERCURY = 0 -"""The body code for the planet Mercury.""" + Mercury = 0 + """The body code for the planet Mercury.""" -BODY_VENUS = 1 -"""The body code for the planet Venus.""" + Venus = 1 + """The body code for the planet Venus.""" -BODY_EARTH = 2 -"""The body code for the planet Earth.""" + Earth = 2 + """The body code for the planet Earth.""" -BODY_MARS = 3 -"""The body code for the planet Mars.""" + Mars = 3 + """The body code for the planet Mars.""" -BODY_JUPITER = 4 -"""The body code for the planet Jupiter.""" + Jupiter = 4 + """The body code for the planet Jupiter.""" -BODY_SATURN = 5 -"""The body code for the planet Saturn.""" + Saturn = 5 + """The body code for the planet Saturn.""" -BODY_URANUS = 6 -"""The body code for the planet Uranus.""" + Uranus = 6 + """The body code for the planet Uranus.""" -BODY_NEPTUNE = 7 -"""The body code for the planet Neptune.""" + Neptune = 7 + """The body code for the planet Neptune.""" -BODY_PLUTO = 8 -"""The body code for the planet Pluto.""" + Pluto = 8 + """The body code for the planet Pluto.""" -BODY_SUN = 9 -"""The body code for the Sun.""" + Sun = 9 + """The body code for the Sun.""" -BODY_MOON = 10 -"""The body code for the Moon.""" + Moon = 10 + """The body code for the Moon.""" BodyName = [ 'Mercury', @@ -134,12 +138,12 @@ BodyName = [ ] """The English names of the supported celestial bodies. -The list `BodyName` is indexed using one of the `BODY_...` constants. +The list `BodyName` is indexed using one of the `Body....` constants. Example ------- ->>> astronomy.BodyName[astronomy.BODY_JUPITER] +>>> astronomy.BodyName[astronomy.Body.Jupiter] 'Jupiter' """ @@ -157,7 +161,7 @@ def BodyCode(name): int If `name` is a valid body name, returns the integer value of the body code associated with that body. - Otherwise, returns `BODY_INVALID`. + Otherwise, returns `Body.Invalid`. Example ------- @@ -167,11 +171,11 @@ def BodyCode(name): """ if name not in BodyName: - return BODY_INVALID - return BodyName.index(name) + return Body.Invalid + return Body[name] def _IsSuperiorPlanet(body): - return body in [BODY_MARS, BODY_JUPITER, BODY_SATURN, BODY_URANUS, BODY_NEPTUNE, BODY_PLUTO] + return body in [Body.Mars, Body.Jupiter, Body.Saturn, Body.Uranus, Body.Neptune, Body.Pluto] _PlanetOrbitalPeriod = [ 87.969, @@ -210,11 +214,11 @@ class NoConvergeError(Error): Error.__init__(self, 'Numeric solver did not converge - please report issue at https://github.com/cosinekitty/astronomy/issues') def _SynodicPeriod(body): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() if body < 0 or body >= len(_PlanetOrbitalPeriod): raise InvalidBodyError() - if body == BODY_MOON: + if body == Body.Moon: return _MEAN_SYNODIC_MONTH return abs(_EARTH_ORBITAL_PERIOD / (_EARTH_ORBITAL_PERIOD/_PlanetOrbitalPeriod[body] - 1.0)) @@ -2730,7 +2734,7 @@ def _CalcVsop(model, time): return Vector(vx, vy, vz, time) def _CalcEarth(time): - return _CalcVsop(_vsop[BODY_EARTH], time) + return _CalcVsop(_vsop[Body.Earth], time) # END VSOP #---------------------------------------------------------------------------- @@ -3025,16 +3029,16 @@ def Search(func, context, t1, t2, dt_tolerance_seconds): #---------------------------------------------------------------------------- def HelioVector(body, time): - if body == BODY_PLUTO: + if body == Body.Pluto: return _CalcChebyshev(_pluto, time) if 0 <= body <= len(_vsop): return _CalcVsop(_vsop[body], time) - if body == BODY_SUN: + if body == Body.Sun: return Vector(0.0, 0.0, 0.0, time) - if body == BODY_MOON: + if body == Body.Moon: e = _CalcEarth(time) m = GeoMoon(time) return Vector(e.x+m.x, e.y+m.y, e.z+m.z, time) @@ -3043,10 +3047,10 @@ def HelioVector(body, time): def GeoVector(body, time, aberration): - if body == BODY_MOON: + if body == Body.Moon: return GeoMoon(time) - if body == BODY_EARTH: + if body == Body.Earth: return Vector(0.0, 0.0, 0.0, time) if not aberration: @@ -3072,7 +3076,7 @@ def GeoVector(body, time, aberration): earth = _CalcEarth(ltime) geo = Vector(h.x-earth.x, h.y-earth.y, h.z-earth.z, time) - if body == BODY_SUN: + if body == Body.Sun: # The Sun's heliocentric coordinates are always (0,0,0). No need to correct. return geo @@ -3242,23 +3246,23 @@ def Ecliptic(equ): return _RotateEquatorialToEcliptic([equ.x, equ.y, equ.z], ob2000) def EclipticLongitude(body, time): - if body == BODY_SUN: + if body == Body.Sun: raise InvalidBodyError() hv = HelioVector(body, time) eclip = Ecliptic(hv) return eclip.elon def AngleFromSun(body, time): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() - sv = GeoVector(BODY_SUN, time, True) + sv = GeoVector(Body.Sun, time, True) bv = GeoVector(body, time, True) return _AngleBetween(sv, bv) def LongitudeFromSun(body, time): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() - sv = GeoVector(BODY_SUN, time, True) + sv = GeoVector(Body.Sun, time, True) se = Ecliptic(sv) bv = GeoVector(body, time, True) be = Ecliptic(bv) @@ -3284,14 +3288,14 @@ def Elongation(body, time): def _rlon_offset(body, time, direction, targetRelLon): plon = EclipticLongitude(body, time) - elon = EclipticLongitude(BODY_EARTH, time) + elon = EclipticLongitude(Body.Earth, time) diff = direction * (elon - plon) return _LongitudeOffset(diff - targetRelLon) def SearchRelativeLongitude(body, targetRelLon, startTime): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() - if body == BODY_MOON or body == BODY_SUN: + if body == Body.Moon or body == Body.Sun: raise InvalidBodyError() syn = _SynodicPeriod(body) direction = +1 if _IsSuperiorPlanet(body) else -1 @@ -3331,10 +3335,10 @@ def _neg_elong_slope(body, time): return (e1 - e2)/dt def SearchMaxElongation(body, startTime): - if body == BODY_MERCURY: + if body == Body.Mercury: s1 = 50.0 s2 = 85.0 - elif body == BODY_VENUS: + elif body == Body.Venus: s1 = 40.0 s2 = 50.0 else: @@ -3343,7 +3347,7 @@ def SearchMaxElongation(body, startTime): iter = 1 while iter <= 2: plon = EclipticLongitude(body, startTime) - elon = EclipticLongitude(BODY_EARTH, startTime) + elon = EclipticLongitude(Body.Earth, startTime) rlon = _LongitudeOffset(plon - elon) # clamp to (-180, +180] # The slope function is not well-behaved when rlon is near 0 degrees or 180 degrees @@ -3421,7 +3425,7 @@ def SearchSunLongitude(targetLon, startTime, limitDays): return Search(_sun_offset, targetLon, startTime, t2, 1.0) def MoonPhase(time): - return LongitudeFromSun(BODY_MOON, time) + return LongitudeFromSun(Body.Moon, time) def _moon_offset(targetLon, time): angle = MoonPhase(time) @@ -3524,22 +3528,22 @@ def _SaturnMagnitude(phase, helio_dist, geo_dist, gc, time): def _VisualMagnitude(body, phase, helio_dist, geo_dist): # For Mercury and Venus, see: https://iopscience.iop.org/article/10.1086/430212 c0 = c1 = c2 = c3 = 0 - if body == BODY_MERCURY: + if body == Body.Mercury: c0 = -0.60; c1 = +4.98; c2 = -4.88; c3 = +3.02 - elif body == BODY_VENUS: + elif body == Body.Venus: if phase < 163.6: c0 = -4.47; c1 = +1.03; c2 = +0.57; c3 = +0.13 else: c0 = +0.98; c1 = -1.02 - elif body == BODY_MARS: + elif body == Body.Mars: c0 = -1.52; c1 = +1.60 - elif body == BODY_JUPITER: + elif body == Body.Jupiter: c0 = -9.40; c1 = +0.50 - elif body == BODY_URANUS: + elif body == Body.Uranus: c0 = -7.19; c1 = +0.25 - elif body == BODY_NEPTUNE: + elif body == Body.Neptune: c0 = -6.87 - elif body == BODY_PLUTO: + elif body == Body.Pluto: c0 = -1.00; c1 = +4.00 else: raise InvalidBodyError() @@ -3550,15 +3554,15 @@ def _VisualMagnitude(body, phase, helio_dist, geo_dist): return mag def Illumination(body, time): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() earth = _CalcEarth(time) - if body == BODY_SUN: + if body == Body.Sun: gc = Vector(-earth.x, -earth.y, -earth.z, time) hc = Vector(0.0, 0.0, 0.0, time) phase = 0.0 # placeholder value; the Sun does not have a phase angle. else: - if body == BODY_MOON: + if body == Body.Moon: # For extra numeric precision, use geocentric moon formula directly. gc = GeoMoon(time) hc = Vector(earth.x + gc.x, earth.y + gc.y, earth.z + gc.z, time) @@ -3571,11 +3575,11 @@ def Illumination(body, time): geo_dist = gc.Length() # distance from body to center of Earth helio_dist = hc.Length() # distance from body to center of Sun ring_tilt = None # only reported for Saturn - if body == BODY_SUN: + if body == Body.Sun: mag = -0.17 + 5.0*math.log10(geo_dist / _AU_PER_PARSEC) - elif body == BODY_MOON: + elif body == Body.Moon: mag = _MoonMagnitude(phase, helio_dist, geo_dist) - elif body == BODY_SATURN: + elif body == Body.Saturn: mag, ring_tilt = _SaturnMagnitude(phase, helio_dist, geo_dist, gc, time) else: mag = _VisualMagnitude(body, phase, helio_dist, geo_dist) @@ -3598,7 +3602,7 @@ def SearchPeakMagnitude(body, startTime): # s1 and s2 are relative longitudes within which peak magnitude of Venus can occur. s1 = 10.0 s2 = 30.0 - if body != BODY_VENUS: + if body != Body.Venus: raise InvalidBodyError() iter = 1 @@ -3606,7 +3610,7 @@ def SearchPeakMagnitude(body, startTime): # Find current heliocentric relative longitude between the # inferior planet and the Earth. plon = EclipticLongitude(body, startTime) - elon = EclipticLongitude(BODY_EARTH, startTime) + elon = EclipticLongitude(Body.Earth, startTime) rlon = _LongitudeOffset(plon - elon) # The slope function is not well-behaved when rlon is near 0 degrees or 180 degrees # because there is a cusp there that causes a discontinuity in the derivative. @@ -3680,7 +3684,7 @@ class HourAngleEvent: self.hor = hor def SearchHourAngle(body, observer, hourAngle, startTime): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() if hourAngle < 0.0 or hourAngle >= 24.0: @@ -3747,11 +3751,11 @@ def _peak_altitude(context, time): return context.direction * (alt + _REFRACTION_NEAR_HORIZON) def SearchRiseSet(body, observer, direction, startTime, limitDays): - if body == BODY_EARTH: + if body == Body.Earth: raise EarthNotAllowedError() - elif body == BODY_SUN: + elif body == Body.Sun: body_radius = _SUN_RADIUS_AU - elif body == BODY_MOON: + elif body == Body.Moon: body_radius = _MOON_RADIUS_AU else: body_radius = 0.0