diff --git a/generate/template/astronomy.py b/generate/template/astronomy.py index d91abaaf..03c715dd 100644 --- a/generate/template/astronomy.py +++ b/generate/template/astronomy.py @@ -85,41 +85,35 @@ class Vector: @enum.unique class Body(enum.IntEnum): + """The celestial bodies supported by Astronomy Engine calculations. + + Values + ------ + Invalid: An unknown, invalid, or undefined celestial body. + Mercury: The planet Mercury. + Venus: The planet Venus. + Earth: The planet Earth. + Mars: The planet Mars. + Jupiter: The planet Jupiter. + Saturn: The planet Saturn. + Uranus: The planet Uranus. + Neptune: The planet Neptune. + Pluto: The planet Pluto. + Sun: The Sun. + Moon: The Earth's moon. + """ Invalid = -1 - """A placeholder value for an unknown, undefined, or invalid body.""" - Mercury = 0 - """The body code for the planet Mercury.""" - Venus = 1 - """The body code for the planet Venus.""" - Earth = 2 - """The body code for the planet Earth.""" - Mars = 3 - """The body code for the planet Mars.""" - Jupiter = 4 - """The body code for the planet Jupiter.""" - Saturn = 5 - """The body code for the planet Saturn.""" - Uranus = 6 - """The body code for the planet Uranus.""" - Neptune = 7 - """The body code for the planet Neptune.""" - Pluto = 8 - """The body code for the planet Pluto.""" - Sun = 9 - """The body code for the Sun.""" - Moon = 10 - """The body code for the Moon.""" def BodyCode(name): """Finds the Body enumeration value, given the name of a body. @@ -343,9 +337,14 @@ class Time: class Observer: """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. + Parameters + ---------- + latitude : float + Geographic latitude in degrees north of the equator. + longitude : float + Geographic longitude in degrees east of the prime meridian at Greenwich, England. + height : float + Elevation above sea level in meters. """ def __init__(self, latitude, longitude, height=0): self.latitude = latitude @@ -1018,6 +1017,18 @@ def Equator(body, time, observer, ofdate, aberration): @enum.unique class Refraction(enum.IntEnum): + """Selects if/how to correct for atmospheric refraction. + + Some functions allow enabling or disabling atmospheric refraction + for the calculated apparent position of a celestial body + as seen by an observer on the surface of the Earth. + + Values + ------ + Airless: No atmospheric refraction correction. + Normal: Recommended correction for standard atmospheric refraction. + JplHorizons: Used only for compatibility testing with JPL Horizons online tool. + """ Airless = 0 Normal = 1 JplHorizons = 2 @@ -1646,6 +1657,17 @@ def SearchHourAngle(body, observer, hourAngle, startTime): @enum.unique class Direction(enum.IntEnum): + """Indicates whether a body is rising above or setting below the horizon. + + Specifies the direction of a rising or setting event for a body. + For example, `Direction.Rise` is used to find sunrise times, + and `Direction.Set` is used to find sunset times. + + Values + ------ + Rise: First appearance of a body as it rises above the horizon. + Set: Last appearance of a body as it sinks below the horizon. + """ Rise = +1 Set = -1 @@ -1766,6 +1788,19 @@ def _distance_slope(direction, time): @enum.unique class ApsisKind(enum.IntEnum): + """Represents whether a satellite is at a closest or farthest point in its orbit. + + An apsis is a point in a satellite's orbit that is closest to, + or farthest from, the body it orbits (its primary). + `ApsisKind` is an enumerated type that indicates which of these + two cases applies to a particular apsis event. + + Values + ------ + Pericenter: The satellite is at its closest point to its primary. + Apocenter: The satellite is at its farthest point from its primary. + Invalid: A placeholder for an undefined, unknown, or invalid apsis. + """ Pericenter = 0 Apocenter = 1 Invalid = 2 diff --git a/pydown/pydown.py b/pydown/pydown.py index 6bff87c8..11149556 100755 --- a/pydown/pydown.py +++ b/pydown/pydown.py @@ -50,6 +50,7 @@ class DocInfo: self.description = '' self.parameters = [] self.attributes = [] + self.enumValues = [] lines = doc.split('\n') @@ -66,7 +67,7 @@ class DocInfo: for line in lines: if re.match(r'^\-+$', line): continue - if line in ['Parameters', 'Returns', 'Example', 'Examples', 'Attributes']: + if line in ['Parameters', 'Returns', 'Example', 'Examples', 'Attributes', 'Values']: mode = line continue if line.strip() == '': @@ -80,6 +81,19 @@ class DocInfo: pass elif mode == 'Example' or mode == 'Examples': pass + elif mode == 'Values': + self.ProcessEnumValue(line) + elif mode == '': + self.description += line + '\n' + else: + raise Exception('Unknown mode = "{}"'.format(mode)) + + def ProcessEnumValue(self, line): + m = re.match(r'^\s*([A-Za-z][A-Za-z0-9_]+)\s*:\s*(.*)$', line) + if not m: + raise Exception('Invalid enum documentation: "{}"'.format(line)) + pair = (m.group(1), m.group(2).strip()) + self.enumValues.append(pair) def ProcessParmAttrLine(self, line, item, itemlist): if line.startswith(' '): @@ -104,6 +118,15 @@ class DocInfo: md += '\n' return md + def EnumTable(self): + md = '' + if self.enumValues: + md += '| Value | Description |\n' + md += '| --- | --- |\n' + for (name, desc) in self.enumValues: + md += '| {} | {} |\n'.format('`' + name + '`', desc) + return md + def Markdown(self): md = '\n' if self.summary: @@ -112,9 +135,17 @@ class DocInfo: md += self.description + '\n\n' md += self.Table(self.parameters, 'Parameter') md += self.Table(self.attributes, 'Attribute') + md += self.EnumTable() md += '\n' return md + def VerifyEnum(self, members): + defs = set(name for (name, _) in self.enumValues) + if defs != members: + print('Actual enums: [' + ', '.join(members) + ']') + print('Documented enums: [' + ', '.join(defs) + ']') + raise Exception('Documented enums do not match actual enums.') + def MdSignature(sig): text = str(sig) text = HtmlEscape(text) @@ -149,6 +180,21 @@ def MdClass(c): md += '\n' return md +def MdEnumType(c): + md = '' + doc = inspect.getdoc(c) + if doc: + md += '\n' + md += '---\n' + md += '\n' + md += '\n'.format(c.__name__) + md += '### ' + c.__name__ + '\n' + info = DocInfo(doc) + info.VerifyEnum(set(c.__members__)) + md += info.Markdown() + md += '\n' + return md + def Markdown(module): md = '' funclist = [] @@ -179,14 +225,13 @@ def Markdown(module): for c in classlist: md += MdClass(c) - if False: # not yet ready to generate Markdown for enumerated types - md += '---\n' - md += '\n' - md += '\n' - md += '## Enumerated Types\n' - md += '\n' - for c in enumlist: - md += MdEnumType(c) + md += '---\n' + md += '\n' + md += '\n' + md += '## Enumerated Types\n' + md += '\n' + for c in enumlist: + md += MdEnumType(c) if False: # not yet ready to generate Markdown for error types md += '---\n' diff --git a/source/python/README.md b/source/python/README.md index 808a00fd..1f493296 100644 --- a/source/python/README.md +++ b/source/python/README.md @@ -11,6 +11,12 @@ **Represents the geographic location of an observer on the surface of the Earth.** +| Type | Parameter | Description | +| --- | --- | --- | +| `float` | `latitude` | Geographic latitude in degrees north of the equator. | +| `float` | `longitude` | Geographic longitude in degrees east of the prime meridian at Greenwich, England. | +| `float` | `height` | Elevation above sea level in meters. | + @@ -21,6 +27,10 @@ **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. + + | Type | Parameter | Description | | --- | --- | --- | | `float` | `ut` | UT1/UTC number of days since noon on January 1, 2000. See the `ut` attribute of this class for more details. | @@ -32,6 +42,95 @@ +--- + + +## Enumerated Types + + +--- + + +### ApsisKind + +**Represents whether a satellite is at a closest or farthest point in its orbit.** + +An apsis is a point in a satellite's orbit that is closest to, +or farthest from, the body it orbits (its primary). +`ApsisKind` is an enumerated type that indicates which of these +two cases applies to a particular apsis event. + + +| Value | Description | +| --- | --- | +| `Pericenter` | The satellite is at its closest point to its primary. | +| `Apocenter` | The satellite is at its farthest point from its primary. | +| `Invalid` | A placeholder for an undefined, unknown, or invalid apsis. | + + + +--- + + +### Body + +**The celestial bodies supported by Astronomy Engine calculations.** + +| Value | Description | +| --- | --- | +| `Invalid` | An unknown, invalid, or undefined celestial body. | +| `Mercury` | The planet Mercury. | +| `Venus` | The planet Venus. | +| `Earth` | The planet Earth. | +| `Mars` | The planet Mars. | +| `Jupiter` | The planet Jupiter. | +| `Saturn` | The planet Saturn. | +| `Uranus` | The planet Uranus. | +| `Neptune` | The planet Neptune. | +| `Pluto` | The planet Pluto. | +| `Sun` | The Sun. | +| `Moon` | The Earth's moon. | + + + +--- + + +### Direction + +**Indicates whether a body is rising above or setting below the horizon.** + +Specifies the direction of a rising or setting event for a body. +For example, `Direction.Rise` is used to find sunrise times, +and `Direction.Set` is used to find sunset times. + + +| Value | Description | +| --- | --- | +| `Rise` | First appearance of a body as it rises above the horizon. | +| `Set` | Last appearance of a body as it sinks below the horizon. | + + + +--- + + +### Refraction + +**Selects if/how to correct for atmospheric refraction.** + +Some functions allow enabling or disabling atmospheric refraction +for the calculated apparent position of a celestial body +as seen by an observer on the surface of the Earth. + + +| Value | Description | +| --- | --- | +| `Airless` | No atmospheric refraction correction. | +| `Normal` | Recommended correction for standard atmospheric refraction. | +| `JplHorizons` | Used only for compatibility testing with JPL Horizons online tool. | + + --- @@ -45,6 +144,10 @@ **Finds the Body enumeration value, given the name of a body.** +>>> astronomy.BodyCode('Mars') + + + | Type | Parameter | Description | | --- | --- | --- | | `str` | `name` | The common English name of a supported celestial body. | @@ -59,6 +162,16 @@ **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. + + | Type | Parameter | Description | | --- | --- | --- | | [`Time`](#Time) | `time` | The date and time for which to calculate the Moon's position. | diff --git a/source/python/astronomy.py b/source/python/astronomy.py index 2fd91895..a45b2775 100644 --- a/source/python/astronomy.py +++ b/source/python/astronomy.py @@ -85,41 +85,35 @@ class Vector: @enum.unique class Body(enum.IntEnum): + """The celestial bodies supported by Astronomy Engine calculations. + + Values + ------ + Invalid: An unknown, invalid, or undefined celestial body. + Mercury: The planet Mercury. + Venus: The planet Venus. + Earth: The planet Earth. + Mars: The planet Mars. + Jupiter: The planet Jupiter. + Saturn: The planet Saturn. + Uranus: The planet Uranus. + Neptune: The planet Neptune. + Pluto: The planet Pluto. + Sun: The Sun. + Moon: The Earth's moon. + """ Invalid = -1 - """A placeholder value for an unknown, undefined, or invalid body.""" - Mercury = 0 - """The body code for the planet Mercury.""" - Venus = 1 - """The body code for the planet Venus.""" - Earth = 2 - """The body code for the planet Earth.""" - Mars = 3 - """The body code for the planet Mars.""" - Jupiter = 4 - """The body code for the planet Jupiter.""" - Saturn = 5 - """The body code for the planet Saturn.""" - Uranus = 6 - """The body code for the planet Uranus.""" - Neptune = 7 - """The body code for the planet Neptune.""" - Pluto = 8 - """The body code for the planet Pluto.""" - Sun = 9 - """The body code for the Sun.""" - Moon = 10 - """The body code for the Moon.""" def BodyCode(name): """Finds the Body enumeration value, given the name of a body. @@ -434,9 +428,14 @@ class Time: class Observer: """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. + Parameters + ---------- + latitude : float + Geographic latitude in degrees north of the equator. + longitude : float + Geographic longitude in degrees east of the prime meridian at Greenwich, England. + height : float + Elevation above sea level in meters. """ def __init__(self, latitude, longitude, height=0): self.latitude = latitude @@ -3079,6 +3078,18 @@ def Equator(body, time, observer, ofdate, aberration): @enum.unique class Refraction(enum.IntEnum): + """Selects if/how to correct for atmospheric refraction. + + Some functions allow enabling or disabling atmospheric refraction + for the calculated apparent position of a celestial body + as seen by an observer on the surface of the Earth. + + Values + ------ + Airless: No atmospheric refraction correction. + Normal: Recommended correction for standard atmospheric refraction. + JplHorizons: Used only for compatibility testing with JPL Horizons online tool. + """ Airless = 0 Normal = 1 JplHorizons = 2 @@ -3707,6 +3718,17 @@ def SearchHourAngle(body, observer, hourAngle, startTime): @enum.unique class Direction(enum.IntEnum): + """Indicates whether a body is rising above or setting below the horizon. + + Specifies the direction of a rising or setting event for a body. + For example, `Direction.Rise` is used to find sunrise times, + and `Direction.Set` is used to find sunset times. + + Values + ------ + Rise: First appearance of a body as it rises above the horizon. + Set: Last appearance of a body as it sinks below the horizon. + """ Rise = +1 Set = -1 @@ -3827,6 +3849,19 @@ def _distance_slope(direction, time): @enum.unique class ApsisKind(enum.IntEnum): + """Represents whether a satellite is at a closest or farthest point in its orbit. + + An apsis is a point in a satellite's orbit that is closest to, + or farthest from, the body it orbits (its primary). + `ApsisKind` is an enumerated type that indicates which of these + two cases applies to a particular apsis event. + + Values + ------ + Pericenter: The satellite is at its closest point to its primary. + Apocenter: The satellite is at its farthest point from its primary. + Invalid: A placeholder for an undefined, unknown, or invalid apsis. + """ Pericenter = 0 Apocenter = 1 Invalid = 2