Goldener Henkel ist drin
This commit is contained in:
Binary file not shown.
+131
-2
@@ -466,6 +466,130 @@ def action_moon_phases_for_month(args: list[str]) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def action_golden_handle_for_month(args: list[str]) -> dict:
|
||||
if len(args) != 3:
|
||||
fail(
|
||||
"Aktion golden_handle_for_month erwartet 3 Argumente: year month timezone",
|
||||
extra={"argv": args},
|
||||
)
|
||||
|
||||
try:
|
||||
year = int(args[0])
|
||||
month = int(args[1])
|
||||
except ValueError as exc:
|
||||
fail("Jahr oder Monat sind ungueltig.", extra={"details": str(exc), "argv": args})
|
||||
|
||||
if month < 1 or month > 12:
|
||||
fail("Monat muss zwischen 1 und 12 liegen.", extra={"month": month})
|
||||
|
||||
timezone_name = args[2]
|
||||
try:
|
||||
tz = ZoneInfo(timezone_name)
|
||||
except Exception as exc:
|
||||
fail("Zeitzone ist ungueltig oder auf dem Server nicht verfuegbar.", extra={"details": str(exc)})
|
||||
|
||||
local_start = datetime(year, month, 1, 0, 0, 0, tzinfo=tz)
|
||||
if month == 12:
|
||||
local_end = datetime(year + 1, 1, 1, 0, 0, 0, tzinfo=tz)
|
||||
else:
|
||||
local_end = datetime(year, month + 1, 1, 0, 0, 0, tzinfo=tz)
|
||||
|
||||
# Approximate the Golden Handle geometrically by the effective selenographic
|
||||
# colongitude over Montes Jura/Sinus Iridum (roughly 44 N, 36 W),
|
||||
# corrected by libration. Empirically, the best-fit target shifts with
|
||||
# libration in latitude by several tenths of a degree, i.e. by hours.
|
||||
def target_colongitude(elat_deg: float) -> float:
|
||||
return 33.385316705346064 + 0.11176238230626076 * elat_deg + 0.0734267792065295 * elat_deg * elat_deg
|
||||
|
||||
def golden_handle_metric(dt_utc: datetime) -> float:
|
||||
time_value = dt_to_time(dt_utc)
|
||||
phase_deg = normalize_degrees(astronomy.MoonPhase(time_value))
|
||||
libration = astronomy.Libration(time_value)
|
||||
effective_colongitude = normalize_degrees(phase_deg - 90.0 - float(libration.elon))
|
||||
return normalize_signed_degrees(effective_colongitude - target_colongitude(float(libration.elat)))
|
||||
|
||||
def refine_peak_time(left_utc: datetime, right_utc: datetime) -> datetime:
|
||||
left = left_utc
|
||||
right = right_utc
|
||||
f_left = golden_handle_metric(left)
|
||||
f_right = golden_handle_metric(right)
|
||||
for _ in range(40):
|
||||
mid = left + (right - left) / 2
|
||||
f_mid = golden_handle_metric(mid)
|
||||
if abs(f_mid) < 1e-5:
|
||||
return mid
|
||||
if f_left == 0:
|
||||
return left
|
||||
if f_right == 0:
|
||||
return right
|
||||
if f_left * f_mid <= 0:
|
||||
right = mid
|
||||
f_right = f_mid
|
||||
else:
|
||||
left = mid
|
||||
f_left = f_mid
|
||||
return left + (right - left) / 2
|
||||
|
||||
search_start = dt_to_time(local_start.astimezone(timezone.utc) - timedelta(days=20))
|
||||
events = []
|
||||
probe = search_start
|
||||
for _ in range(3):
|
||||
first_quarter = astronomy.SearchMoonPhase(90.0, probe, 40.0)
|
||||
if first_quarter is None:
|
||||
break
|
||||
|
||||
full_moon = astronomy.SearchMoonPhase(180.0, first_quarter, 12.0)
|
||||
if full_moon is None:
|
||||
break
|
||||
|
||||
interval_start = time_to_datetime(first_quarter)
|
||||
interval_end = time_to_datetime(full_moon)
|
||||
step = timedelta(hours=1)
|
||||
previous_time = interval_start
|
||||
previous_value = golden_handle_metric(previous_time)
|
||||
current = interval_start + step
|
||||
peak_utc = None
|
||||
|
||||
while current <= interval_end:
|
||||
current_value = golden_handle_metric(current)
|
||||
if previous_value == 0 or current_value == 0 or previous_value * current_value < 0:
|
||||
peak_utc = refine_peak_time(previous_time, current)
|
||||
break
|
||||
previous_time = current
|
||||
previous_value = current_value
|
||||
current += step
|
||||
|
||||
if peak_utc is not None:
|
||||
peak_local = peak_utc.astimezone(tz)
|
||||
if peak_local.year == year and peak_local.month == month:
|
||||
events.append({
|
||||
"kind": "golden_handle",
|
||||
"label": "Goldener Henkel am Mond",
|
||||
"utc_iso": peak_utc.isoformat().replace("+00:00", "Z"),
|
||||
"local_iso": peak_local.isoformat(),
|
||||
"local_date": peak_local.strftime("%d.%m.%Y"),
|
||||
"local_time": peak_local.strftime("%H:%M"),
|
||||
"peak_local_iso": peak_local.isoformat(),
|
||||
"peak_local_date": peak_local.strftime("%d.%m.%Y"),
|
||||
"peak_local_time": peak_local.strftime("%H:%M"),
|
||||
})
|
||||
|
||||
probe = dt_to_time(interval_end + timedelta(days=10))
|
||||
|
||||
events.sort(key=lambda item: item["local_iso"])
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"action": "golden_handle_for_month",
|
||||
"selected": {
|
||||
"year": year,
|
||||
"month": month,
|
||||
"timezone": timezone_name,
|
||||
},
|
||||
"events": events,
|
||||
}
|
||||
|
||||
|
||||
def action_season_changes_for_month(args: list[str]) -> dict:
|
||||
if len(args) != 3:
|
||||
fail(
|
||||
@@ -2578,7 +2702,7 @@ def action_satellite_passes(args: list[str]) -> dict:
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) < 2:
|
||||
fail("Es wurde keine Aktion uebergeben.", extra={"available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "month_sky_context", "moon_phases_for_month", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "inner_planet_elongations_for_month", "venus_peak_magnitude_for_month", "outer_planet_events_for_month", "outer_planet_stations_for_month", "moon_star_occultations_for_month"]})
|
||||
fail("Es wurde keine Aktion uebergeben.", extra={"available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "month_sky_context", "moon_phases_for_month", "golden_handle_for_month", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "inner_planet_elongations_for_month", "venus_peak_magnitude_for_month", "outer_planet_events_for_month", "outer_planet_stations_for_month", "moon_star_occultations_for_month"]})
|
||||
|
||||
action = sys.argv[1]
|
||||
args = sys.argv[2:]
|
||||
@@ -2623,6 +2747,11 @@ def main() -> None:
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
return
|
||||
|
||||
if action == "golden_handle_for_month":
|
||||
result = action_golden_handle_for_month(args)
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
return
|
||||
|
||||
if action == "moon_planet_approaches":
|
||||
result = action_moon_planet_approaches(args)
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
@@ -2673,7 +2802,7 @@ def main() -> None:
|
||||
print(json.dumps(result, ensure_ascii=False))
|
||||
return
|
||||
|
||||
fail("Unbekannte Aktion.", extra={"action": action, "available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "month_sky_context", "moon_phases_for_month", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "inner_planet_elongations_for_month", "venus_peak_magnitude_for_month", "outer_planet_events_for_month", "outer_planet_stations_for_month", "moon_star_occultations_for_month"]})
|
||||
fail("Unbekannte Aktion.", extra={"action": action, "available_actions": ["sun_moon_rise_set", "moon_star_occultations", "moon_phase_details", "satellite_passes", "planet_rise_set", "month_sky_context", "moon_phases_for_month", "golden_handle_for_month", "season_changes_for_month", "moon_planet_approaches", "planet_conjunctions_for_month", "eclipses_for_month", "moon_apsides_for_month", "sun_apsides_for_month", "inner_planet_elongations_for_month", "venus_peak_magnitude_for_month", "outer_planet_events_for_month", "outer_planet_stations_for_month", "moon_star_occultations_for_month"]})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user