|
28 | 28 | PREVIOUS = -1 |
29 | 29 | NEXT = 1 |
30 | 30 |
|
| 31 | +DAYS = 0 |
| 32 | +TROPICAL_YEARS = 1 |
| 33 | + |
| 34 | +SYNODIC_MIN = -1 |
| 35 | +SYNODIC_AVG = 0 |
| 36 | +SYNODIC_MAX = 1 |
| 37 | + |
31 | 38 | _SWE = { |
32 | 39 | chart.ALCABITUS: b"B", |
33 | 40 | chart.AZIMUTHAL: b"H", |
@@ -1032,6 +1039,72 @@ def relative_position(object1: dict | float, object2: dict | float) -> int: |
1032 | 1039 | return calc.OCCIDENTAL if swe.difdegn(lon1, lon2) > 180 else calc.ORIENTAL |
1033 | 1040 |
|
1034 | 1041 |
|
| 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 | + |
1035 | 1108 | def solar_year_length(jd: float) -> float: |
1036 | 1109 | """Returns the tropical year length in days of the given Julian date. |
1037 | 1110 | This is a direct copy of astro.com's calculations.""" |
@@ -1091,23 +1164,29 @@ def _is_daytime( |
1091 | 1164 | return is_daytime_from(sun, asc) |
1092 | 1165 |
|
1093 | 1166 |
|
| 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 | + |
1094 | 1173 | """ |
1095 | 1174 | PREDICTIVE CALCULATIONS |
1096 | 1175 | -------------------------------------------------------------------------------- |
1097 | 1176 | These functions deal with predicting aspects, moon phases, eclipses, etc. |
1098 | 1177 | """ |
1099 | 1178 |
|
1100 | 1179 |
|
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: |
1102 | 1181 | """Returns the Julian day of the requested transit previous |
1103 | 1182 | to the passed Julian day.""" |
1104 | | - return _search(first, second, jd, aspect, PREVIOUS) |
| 1183 | + return _search(index1, index2, jd, aspect, PREVIOUS) |
1105 | 1184 |
|
1106 | 1185 |
|
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: |
1108 | 1187 | """Returns the Julian day of the requested transit after |
1109 | 1188 | the passed Julian day.""" |
1110 | | - return _search(first, second, jd, aspect, NEXT) |
| 1189 | + return _search(index1, index2, jd, aspect, NEXT) |
1111 | 1190 |
|
1112 | 1191 |
|
1113 | 1192 | def previous_new_moon(jd: float) -> float: |
@@ -1188,14 +1267,14 @@ def _eclipse_type(swe_index: int) -> int: |
1188 | 1267 |
|
1189 | 1268 |
|
1190 | 1269 | def _search( |
1191 | | - object1: int, object2: int, jd: float, aspect: float, direction: int |
| 1270 | + index1: int, index2: int, jd: float, aspect: float, direction: int |
1192 | 1271 | ) -> float: |
1193 | 1272 | """Iteratively searches for and returns the Julian date of the previous |
1194 | 1273 | or next requested aspect. Useful for short dates and fast planets but too |
1195 | 1274 | expensive for anything more advanced.""" |
1196 | 1275 | 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) |
1199 | 1278 | distance = abs(swe.difdeg2n(planet1["lon"], planet2["lon"])) |
1200 | 1279 | diff = abs(aspect - distance) |
1201 | 1280 |
|
|
0 commit comments