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