Skip to content

Commit 517582d

Browse files
committed
Ephemeris module tidyup and extra astronomical functions.
1 parent 71b238b commit 517582d

1 file changed

Lines changed: 86 additions & 7 deletions

File tree

immanuel/tools/ephemeris.py

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@
2828
PREVIOUS = -1
2929
NEXT = 1
3030

31+
DAYS = 0
32+
TROPICAL_YEARS = 1
33+
34+
SYNODIC_MIN = -1
35+
SYNODIC_AVG = 0
36+
SYNODIC_MAX = 1
37+
3138
_SWE = {
3239
chart.ALCABITUS: b"B",
3340
chart.AZIMUTHAL: b"H",
@@ -1032,6 +1039,72 @@ def relative_position(object1: dict | float, object2: dict | float) -> int:
10321039
return calc.OCCIDENTAL if swe.difdegn(lon1, lon2) > 180 else calc.ORIENTAL
10331040

10341041

1042+
def orbital_eccentricity(index: int, jd: float) -> float:
1043+
"""Returns the passed object's orbital eccentricity."""
1044+
return _orbital_elements(index, jd)[1]
1045+
1046+
1047+
def sidereal_period(index: int, jd: float, unit: int = DAYS) -> float:
1048+
"""Returns the passed object's sidereal orbital period."""
1049+
sidereal_period = _orbital_elements(index, jd)[10]
1050+
return sidereal_period * solar_year_length(jd) if unit == DAYS else sidereal_period
1051+
1052+
1053+
def tropical_period(index: int, jd: float, unit: int = DAYS) -> float:
1054+
"""Returns the passed object's tropical orbital period."""
1055+
tropical_period = _orbital_elements(index, jd)[12]
1056+
return tropical_period * solar_year_length(jd) if unit == DAYS else tropical_period
1057+
1058+
1059+
def synodic_period(index: int, jd: float, unit: int = DAYS) -> float:
1060+
"""Returns the passed object's synodic period."""
1061+
synodic_period = _orbital_elements(index, jd)[13]
1062+
return synodic_period if unit == DAYS else synodic_period / solar_year_length(jd)
1063+
1064+
1065+
def synodic_period_between(
1066+
index1: int, index2: int, jd: float, type: int = SYNODIC_AVG, unit: int = DAYS
1067+
) -> float:
1068+
"""Returns the approximate synodic period between two objects."""
1069+
sidereal_period1 = sidereal_period(index1, jd)
1070+
sidereal_period2 = sidereal_period(index2, jd)
1071+
1072+
synodic_period = 1 / abs(1 / sidereal_period1 - 1 / sidereal_period2)
1073+
1074+
if type in (SYNODIC_MIN, SYNODIC_MAX):
1075+
orbital_eccentricity1 = orbital_eccentricity(index1, jd)
1076+
orbital_eccentricity2 = orbital_eccentricity(index2, jd)
1077+
synodic_period *= (
1078+
1 + ((orbital_eccentricity1 + orbital_eccentricity2) * type) / 2
1079+
)
1080+
1081+
return synodic_period if unit == DAYS else synodic_period / solar_year_length(jd)
1082+
1083+
1084+
def retrograde_period(index: int, jd: float, unit: int = DAYS) -> float:
1085+
"""Returns an approximate estimate of a planet's retrograde period. This is
1086+
very approximate and should not be used for anything precise since it is
1087+
based on Newtonian mechanics and perfect-circle orbit calculations. Formula
1088+
borrowed from https://physics.stackexchange.com/a/476286."""
1089+
if index in (chart.SUN, chart.MOON):
1090+
return 0.0
1091+
1092+
a1, *_, t1 = _orbital_elements(chart.TERRA, jd)[:11]
1093+
a2 = _orbital_elements(index, jd)[0]
1094+
1095+
r = a2 / a1
1096+
1097+
num = math.acos((math.sqrt(r) + 1) / (r + (1 / math.sqrt(r))))
1098+
den = math.pi * (1 - (1 / (r ** (3 / 2))))
1099+
t_retro = t1 * (num / den)
1100+
1101+
retrograde_period = abs(t_retro)
1102+
1103+
return (
1104+
retrograde_period * solar_year_length(jd) if unit == DAYS else retrograde_period
1105+
)
1106+
1107+
10351108
def solar_year_length(jd: float) -> float:
10361109
"""Returns the tropical year length in days of the given Julian date.
10371110
This is a direct copy of astro.com's calculations."""
@@ -1091,23 +1164,29 @@ def _is_daytime(
10911164
return is_daytime_from(sun, asc)
10921165

10931166

1167+
@cache
1168+
def _orbital_elements(index: int, jd: float) -> tuple:
1169+
"""Returns pyswisseph's orbital data for the passed object."""
1170+
return swe.get_orbital_elements(jd + swe.deltat(jd), _SWE[index], swe.FLG_SWIEPH)
1171+
1172+
10941173
"""
10951174
PREDICTIVE CALCULATIONS
10961175
--------------------------------------------------------------------------------
10971176
These functions deal with predicting aspects, moon phases, eclipses, etc.
10981177
"""
10991178

11001179

1101-
def previous_aspect(first: int, second: int, jd: float, aspect: float) -> float:
1180+
def previous_aspect(index1: int, index2: int, jd: float, aspect: float) -> float:
11021181
"""Returns the Julian day of the requested transit previous
11031182
to the passed Julian day."""
1104-
return _search(first, second, jd, aspect, PREVIOUS)
1183+
return _search(index1, index2, jd, aspect, PREVIOUS)
11051184

11061185

1107-
def next_aspect(first: int, second: int, jd: float, aspect: float) -> float:
1186+
def next_aspect(index1: int, index2: int, jd: float, aspect: float) -> float:
11081187
"""Returns the Julian day of the requested transit after
11091188
the passed Julian day."""
1110-
return _search(first, second, jd, aspect, NEXT)
1189+
return _search(index1, index2, jd, aspect, NEXT)
11111190

11121191

11131192
def previous_new_moon(jd: float) -> float:
@@ -1188,14 +1267,14 @@ def _eclipse_type(swe_index: int) -> int:
11881267

11891268

11901269
def _search(
1191-
object1: int, object2: int, jd: float, aspect: float, direction: int
1270+
index1: int, index2: int, jd: float, aspect: float, direction: int
11921271
) -> float:
11931272
"""Iteratively searches for and returns the Julian date of the previous
11941273
or next requested aspect. Useful for short dates and fast planets but too
11951274
expensive for anything more advanced."""
11961275
while True:
1197-
planet1 = get_planet(object1, jd)
1198-
planet2 = get_planet(object2, jd)
1276+
planet1 = get_planet(index1, jd)
1277+
planet2 = get_planet(index2, jd)
11991278
distance = abs(swe.difdeg2n(planet1["lon"], planet2["lon"]))
12001279
diff = abs(aspect - distance)
12011280

0 commit comments

Comments
 (0)