From a4e2367f3321836ada0d9377a77d4d926bc2d48a Mon Sep 17 00:00:00 2001 From: Eskimue Date: Fri, 3 Apr 2026 15:06:09 +0200 Subject: [PATCH] =?UTF-8?q?station=C3=A4re=20Punkte=20drin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codex-temp/php-server.err.log | 24 ++++ public/monatsvorhersage_old.php | 40 ++++++ public/py/__pycache__/api.cpython-314.pyc | Bin 126731 -> 134314 bytes public/py/api.py | 155 +++++++++++++++++++++- 4 files changed, 217 insertions(+), 2 deletions(-) diff --git a/.codex-temp/php-server.err.log b/.codex-temp/php-server.err.log index 8cb2a71..d677643 100644 --- a/.codex-temp/php-server.err.log +++ b/.codex-temp/php-server.err.log @@ -626,3 +626,27 @@ Stack trace: [Fri Apr 3 14:43:53 2026] 127.0.0.1:63593 Accepted [Fri Apr 3 14:43:55 2026] 127.0.0.1:63593 [200]: GET /favicon.ico [Fri Apr 3 14:43:55 2026] 127.0.0.1:63593 Closing +[Fri Apr 3 14:50:16 2026] 127.0.0.1:61254 Accepted +[Fri Apr 3 14:50:34 2026] 127.0.0.1:61254 [200]: GET /monatsvorhersage_old.php?year=2026&month=1 +[Fri Apr 3 14:50:34 2026] 127.0.0.1:61254 Closing +[Fri Apr 3 14:50:34 2026] 127.0.0.1:61256 Accepted +[Fri Apr 3 14:50:51 2026] 127.0.0.1:61256 [200]: GET /monatsvorhersage_old.php?year=2026&month=1 +[Fri Apr 3 14:50:51 2026] 127.0.0.1:61256 Closing +[Fri Apr 3 14:51:26 2026] 127.0.0.1:52317 Accepted +[Fri Apr 3 14:51:41 2026] 127.0.0.1:52317 [200]: GET /monatsvorhersage_old.php?year=2026&month=1 +[Fri Apr 3 14:51:41 2026] 127.0.0.1:52317 Closing +[Fri Apr 3 14:51:41 2026] 127.0.0.1:63047 Accepted +[Fri Apr 3 14:51:41 2026] 127.0.0.1:61026 Accepted +[Fri Apr 3 14:51:41 2026] 127.0.0.1:63047 [200]: GET /css/style.css +[Fri Apr 3 14:51:41 2026] 127.0.0.1:61026 [200]: GET /js/astronomy.browser.min.js +[Fri Apr 3 14:51:41 2026] 127.0.0.1:63047 Closing +[Fri Apr 3 14:51:41 2026] 127.0.0.1:61026 Closing +[Fri Apr 3 14:51:41 2026] 127.0.0.1:52369 Accepted +[Fri Apr 3 14:51:41 2026] 127.0.0.1:61642 Accepted +[Fri Apr 3 14:51:41 2026] 127.0.0.1:52369 [200]: GET /images/star16x16.png +[Fri Apr 3 14:51:41 2026] 127.0.0.1:61642 [200]: GET /images/star16x16_rays.png +[Fri Apr 3 14:51:41 2026] 127.0.0.1:52369 Closing +[Fri Apr 3 14:51:41 2026] 127.0.0.1:61642 Closing +[Fri Apr 3 14:51:41 2026] 127.0.0.1:57770 Accepted +[Fri Apr 3 14:51:43 2026] 127.0.0.1:57770 [200]: GET /favicon.ico +[Fri Apr 3 14:51:43 2026] 127.0.0.1:57770 Closing diff --git a/public/monatsvorhersage_old.php b/public/monatsvorhersage_old.php index ab041ff..9eec81e 100644 --- a/public/monatsvorhersage_old.php +++ b/public/monatsvorhersage_old.php @@ -64,6 +64,7 @@ $moonApsisEvents = []; $sunApsisEvents = []; $innerPlanetElongationEvents = []; $outerPlanetEvents = []; +$outerPlanetStationEvents = []; $moonOccultations = []; $seasonChanges = []; $monthEventList = []; @@ -440,6 +441,23 @@ if (is_array($outerPlanetDecoded) && ($outerPlanetDecoded['ok'] ?? false)) { $outerPlanetEvents = is_array($outerPlanetDecoded['events'] ?? null) ? $outerPlanetDecoded['events'] : []; } +$outerPlanetStationOutput = []; +$outerPlanetStationExitCode = 0; +$outerPlanetStationArgs = [ + $pythonScriptPath, + 'outer_planet_stations_for_month', + (string) $selectedYear, + (string) $selectedMonth, + $chartTimezone, +]; +$outerPlanetStationCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $outerPlanetStationArgs)) . ' 2>&1'; +exec($outerPlanetStationCommand, $outerPlanetStationOutput, $outerPlanetStationExitCode); +$outerPlanetStationRaw = trim(implode("\n", $outerPlanetStationOutput)); +$outerPlanetStationDecoded = json_decode($outerPlanetStationRaw, true); +if (is_array($outerPlanetStationDecoded) && ($outerPlanetStationDecoded['ok'] ?? false)) { + $outerPlanetStationEvents = is_array($outerPlanetStationDecoded['events'] ?? null) ? $outerPlanetStationDecoded['events'] : []; +} + $moonPlanetOutput = []; $moonPlanetExitCode = 0; $moonPlanetArgs = [ @@ -672,6 +690,28 @@ foreach ($outerPlanetEvents as $outerPlanetEvent) { ]; } +foreach ($outerPlanetStationEvents as $outerPlanetStationEvent) { + if (!is_array($outerPlanetStationEvent)) { + continue; + } + + $stationPlanet = (string) ($outerPlanetStationEvent['planet_label'] ?? 'Planet'); + $stationKind = (string) ($outerPlanetStationEvent['kind'] ?? ''); + $stationLabel = $stationKind === 'retrograde' + ? $stationPlanet . ' stationär, dann rückläufig' + : ($stationKind === 'prograde' + ? $stationPlanet . ' stationär, dann rechtläufig' + : (string) ($outerPlanetStationEvent['label'] ?? 'Stationärer Punkt')); + + $monthEventList[] = [ + 'event' => $stationLabel, + 'date' => (string) ($outerPlanetStationEvent['local_date'] ?? ''), + 'time' => (string) ($outerPlanetStationEvent['local_time'] ?? ''), + 'local_iso' => (string) ($outerPlanetStationEvent['local_iso'] ?? ''), + 'type' => 'outer_planet_station', + ]; +} + foreach ($moonPlanetApproaches as $approachEvent) { if (!is_array($approachEvent)) { continue; diff --git a/public/py/__pycache__/api.cpython-314.pyc b/public/py/__pycache__/api.cpython-314.pyc index ede6ca3ff5c0a2870af6d8facde7888aeb28e1f3..18f02cc0193c0e6f80baf8f6786bf33b24bb3b34 100644 GIT binary patch delta 9080 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 delta 5066 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 VjI-<6kkH1QZ=8;hZoC63{}&va**^dP diff --git a/public/py/api.py b/public/py/api.py index 86b5d86..4e00831 100644 --- a/public/py/api.py +++ b/public/py/api.py @@ -1308,6 +1308,152 @@ def action_outer_planet_events_for_month(args: list[str]) -> dict: } +def geocentric_ecliptic_longitude_deg(body: astronomy.Body, dt_utc: datetime) -> float: + time_value = dt_to_time(dt_utc) + vector = astronomy.GeoVector(body, time_value, True) + ecliptic = astronomy.Ecliptic(vector) + return float(ecliptic.elon) + + +def longitude_delta_deg(left_deg: float, right_deg: float) -> float: + delta = right_deg - left_deg + while delta <= -180.0: + delta += 360.0 + while delta > 180.0: + delta -= 360.0 + return delta + + +def planet_longitude_rate_deg_per_day(body: astronomy.Body, dt_utc: datetime) -> float: + step = timedelta(hours=12) + left = geocentric_ecliptic_longitude_deg(body, dt_utc - step) + right = geocentric_ecliptic_longitude_deg(body, dt_utc + step) + return longitude_delta_deg(left, right) + + +def refine_stationary_time(body: astronomy.Body, left_utc: datetime, right_utc: datetime) -> datetime: + left = left_utc + right = right_utc + f_left = planet_longitude_rate_deg_per_day(body, left) + f_right = planet_longitude_rate_deg_per_day(body, right) + + for _ in range(40): + mid = left + (right - left) / 2 + f_mid = planet_longitude_rate_deg_per_day(body, 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 + + +def action_outer_planet_stations_for_month(args: list[str]) -> dict: + if len(args) != 3: + fail( + "Aktion outer_planet_stations_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) + + utc_start = local_start.astimezone(timezone.utc) + utc_end = local_end.astimezone(timezone.utc) + scan_start = utc_start - timedelta(days=70) + scan_end = utc_end + timedelta(days=70) + scan_step = timedelta(hours=12) + + planet_defs = [ + ("Mars", astronomy.Body.Mars, "mars"), + ("Jupiter", astronomy.Body.Jupiter, "jupiter"), + ("Saturn", astronomy.Body.Saturn, "saturn"), + ("Uranus", astronomy.Body.Uranus, "uranus"), + ("Neptun", astronomy.Body.Neptune, "neptune"), + ] + + events: list[dict] = [] + for label, body, key in planet_defs: + current = scan_start + previous_time = current + previous_rate = planet_longitude_rate_deg_per_day(body, current) + current += scan_step + + while current <= scan_end: + current_rate = planet_longitude_rate_deg_per_day(body, current) + if previous_rate == 0 or current_rate == 0 or previous_rate * current_rate < 0: + station_time_utc = refine_stationary_time(body, previous_time, current) + local_dt = station_time_utc.astimezone(tz) + if local_start <= local_dt < local_end: + rate_before = planet_longitude_rate_deg_per_day(body, station_time_utc - timedelta(days=3)) + rate_after = planet_longitude_rate_deg_per_day(body, station_time_utc + timedelta(days=3)) + if rate_before > 0 and rate_after < 0: + kind = "retrograde" + text = "stationaer, dann ruecklaeufig" + elif rate_before < 0 and rate_after > 0: + kind = "prograde" + text = "stationaer, dann rechtlaeufig" + else: + kind = "stationary" + text = "stationaer" + + if not any( + item["planet_key"] == key and abs(datetime.fromisoformat(item["local_iso"]).timestamp() - local_dt.timestamp()) < 3600 + for item in events + ): + events.append({ + "planet_key": key, + "planet_label": label, + "kind": kind, + "label": f"{label} {text}", + "utc_iso": station_time_utc.isoformat().replace("+00:00", "Z"), + "local_iso": local_dt.isoformat(), + "local_date": local_dt.strftime("%d.%m.%Y"), + "local_time": local_dt.strftime("%H:%M"), + }) + + previous_time = current + previous_rate = current_rate + current += scan_step + + events.sort(key=lambda item: item["local_iso"]) + + return { + "ok": True, + "action": "outer_planet_stations_for_month", + "selected": { + "year": year, + "month": month, + "timezone": timezone_name, + }, + "events": events, + } + + def action_moon_star_occultations_for_month(args: list[str]) -> dict: if len(args) != 7: fail( @@ -2364,7 +2510,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", "outer_planet_events_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", "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", "outer_planet_events_for_month", "outer_planet_stations_for_month", "moon_star_occultations_for_month"]}) action = sys.argv[1] args = sys.argv[2:] @@ -2444,12 +2590,17 @@ def main() -> None: print(json.dumps(result, ensure_ascii=False)) return + if action == "outer_planet_stations_for_month": + result = action_outer_planet_stations_for_month(args) + print(json.dumps(result, ensure_ascii=False)) + return + if action == "moon_star_occultations_for_month": result = action_moon_star_occultations_for_month(args) 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", "outer_planet_events_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", "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", "outer_planet_events_for_month", "outer_planet_stations_for_month", "moon_star_occultations_for_month"]}) if __name__ == "__main__":