Planeten helle Sterne begegnung rein

This commit is contained in:
2026-04-04 11:10:44 +02:00
parent ad73f1e5b3
commit de0c700f01
3 changed files with 239 additions and 2 deletions
+43
View File
@@ -18,6 +18,9 @@ $messierDatabaseMagnitudeLimit = 4.0;
$messierDisplayMagnitudeLimit = 1.0;
$moonPlanetMaxSeparationDeg = 1.5;
$moonDeepSkyMaxSeparationDeg = 1.0;
$planetBrightStarMaxSeparationDeg = 1.0;
// Maximaler Winkelabstand fuer Begegnungen zwischen hellen Planeten und
// ausgewaehlten hellen Sternen wie Regulus oder Spica.
$planetConjunctionMaxSeparationDeg = 1.0;
$moonOccultationMaxMag = 2.0;
// Gute Merkur-Sichtbarkeit wird nicht nur über Elongation, sondern über echte
@@ -78,6 +81,7 @@ $moonPhaseEvents = [];
$goldenHandleEvents = [];
$moonPlanetApproaches = [];
$moonDeepSkyApproaches = [];
$planetBrightStarApproaches = [];
$mercuryGoodVisibilityEvents = [];
$planetConjunctions = [];
$eclipseEvents = [];
@@ -696,6 +700,27 @@ if (is_array($moonDeepSkyDecoded) && ($moonDeepSkyDecoded['ok'] ?? false)) {
$moonDeepSkyApproaches = is_array($moonDeepSkyDecoded['approaches'] ?? null) ? $moonDeepSkyDecoded['approaches'] : [];
}
$planetBrightStarOutput = [];
$planetBrightStarExitCode = 0;
$planetBrightStarArgs = [
$pythonScriptPath,
'planet_bright_star_approaches_for_month',
(string) $chartLatitude,
(string) $chartLongitude,
(string) $chartElevation,
(string) $selectedYear,
(string) $selectedMonth,
$chartTimezone,
(string) $planetBrightStarMaxSeparationDeg,
];
$planetBrightStarCommand = $pythonExecutable . ' ' . implode(' ', array_map('escapeshellarg', $planetBrightStarArgs)) . ' 2>&1';
exec($planetBrightStarCommand, $planetBrightStarOutput, $planetBrightStarExitCode);
$planetBrightStarRaw = trim(implode("\n", $planetBrightStarOutput));
$planetBrightStarDecoded = json_decode($planetBrightStarRaw, true);
if (is_array($planetBrightStarDecoded) && ($planetBrightStarDecoded['ok'] ?? false)) {
$planetBrightStarApproaches = is_array($planetBrightStarDecoded['approaches'] ?? null) ? $planetBrightStarDecoded['approaches'] : [];
}
$planetConjunctionOutput = [];
$planetConjunctionExitCode = 0;
$planetConjunctionArgs = [
@@ -1069,6 +1094,24 @@ foreach ($moonDeepSkyApproaches as $approachEvent) {
];
}
foreach ($planetBrightStarApproaches as $approachEvent) {
if (!is_array($approachEvent)) {
continue;
}
$distanceText = isset($approachEvent['separation_deg'])
? number_format((float) ($approachEvent['separation_deg']), 2, ',', '') . ' Grad'
: '';
$monthEventList[] = [
'event' => trim((string) ($approachEvent['label'] ?? 'Planet nahe hellem Stern') . ($distanceText !== '' ? ' (' . $distanceText . ')' : '')),
'date' => (string) ($approachEvent['local_date'] ?? ''),
'time' => (string) ($approachEvent['local_time'] ?? ''),
'local_iso' => (string) ($approachEvent['local_iso'] ?? ''),
'type' => 'planet_bright_star',
];
}
foreach ($planetConjunctions as $conjunctionEvent) {
if (!is_array($conjunctionEvent)) {
continue;
Binary file not shown.
+196 -2
View File
@@ -950,6 +950,18 @@ def planet_pair_separation_deg(
return spherical_separation_deg(float(eq_a.ra), float(eq_a.dec), float(eq_b.ra), float(eq_b.dec))
def planet_fixed_equatorial_separation_deg(
body: astronomy.Body,
ra_hours: float,
dec_deg: float,
observer: astronomy.Observer,
dt_utc: datetime,
) -> float:
time_value = dt_to_time(dt_utc)
body_eq = astronomy.Equator(body, time_value, observer, True, True)
return spherical_separation_deg(float(body_eq.ra), float(body_eq.dec), float(ra_hours), float(dec_deg))
def refine_minimum_separation(
body: astronomy.Body,
observer: astronomy.Observer,
@@ -1024,6 +1036,32 @@ def refine_fixed_target_minimum_separation(
return best, moon_fixed_equatorial_separation_deg(ra_hours, dec_deg, observer, best)
def refine_planet_fixed_target_minimum_separation(
body: astronomy.Body,
ra_hours: float,
dec_deg: float,
observer: astronomy.Observer,
left_utc: datetime,
right_utc: datetime,
) -> tuple[datetime, float]:
left = left_utc
right = right_utc
for _ in range(32):
span = (right - left) / 3
m1 = left + span
m2 = right - span
f1 = planet_fixed_equatorial_separation_deg(body, ra_hours, dec_deg, observer, m1)
f2 = planet_fixed_equatorial_separation_deg(body, ra_hours, dec_deg, observer, m2)
if f1 <= f2:
right = m2
else:
left = m1
best = left + (right - left) / 2
return best, planet_fixed_equatorial_separation_deg(body, ra_hours, dec_deg, observer, best)
def action_moon_planet_approaches(args: list[str]) -> dict:
if len(args) != 7:
fail(
@@ -1135,6 +1173,157 @@ def action_moon_planet_approaches(args: list[str]) -> dict:
}
def action_planet_bright_star_approaches_for_month(args: list[str]) -> dict:
if len(args) != 7:
fail(
"Aktion planet_bright_star_approaches_for_month erwartet 7 Argumente: latitude longitude elevation year month timezone max_sep_deg",
extra={"argv": args},
)
latitude = parse_float(args[0], "Latitude")
longitude = parse_float(args[1], "Longitude")
elevation = parse_float(args[2], "Elevation")
try:
year = int(args[3])
month = int(args[4])
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[5]
max_sep_deg = parse_float(args[6], "Maximalabstand")
try:
tz = ZoneInfo(timezone_name)
except Exception as exc:
fail("Zeitzone ist ungueltig oder auf dem Server nicht verfuegbar.", extra={"details": str(exc)})
observer = astronomy.Observer(latitude, longitude, elevation)
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_step = timedelta(hours=1)
coarse_threshold = max_sep_deg + 1.0
planet_defs = [
("Merkur", astronomy.Body.Mercury, "mercury"),
("Venus", astronomy.Body.Venus, "venus"),
("Mars", astronomy.Body.Mars, "mars"),
("Jupiter", astronomy.Body.Jupiter, "jupiter"),
("Saturn", astronomy.Body.Saturn, "saturn"),
]
# Helle, auffaellige Sterne nahe der Ekliptik, die regelmaessig
# attraktive Begegnungen mit den hellen Planeten liefern.
star_defs = [
("Alrescha", 2.0341, 2.7638),
("Hamal", 2.1196, 23.4624),
("Menkar", 3.0380, 4.0897),
("Aldebaran", 4.5987, 16.5093),
("Elnath", 5.4382, 28.6075),
("Pollux", 7.7553, 28.0262),
("Alphard", 9.4598, -8.6586),
("Regulus", 10.1395, 11.9672),
("Denebola", 11.8177, 14.5721),
("Spica", 13.4199, -11.1613),
("Zubenelgenubi", 14.8479, -16.0418),
("Antares", 16.4901, -26.4319),
]
approaches = []
for planet_label, planet_body, planet_key in planet_defs:
for star_label, ra_hours, dec_deg in star_defs:
samples: list[tuple[datetime, float]] = []
current = utc_start
while current <= utc_end:
samples.append((current, planet_fixed_equatorial_separation_deg(planet_body, ra_hours, dec_deg, observer, current)))
current += scan_step
if samples[-1][0] < utc_end:
samples.append((utc_end, planet_fixed_equatorial_separation_deg(planet_body, ra_hours, dec_deg, observer, utc_end)))
seen_ranges: list[tuple[datetime, datetime]] = []
for index in range(1, len(samples) - 1):
curr_t, curr_sep = samples[index]
prev_sep = samples[index - 1][1]
next_sep = samples[index + 1][1]
if curr_sep > coarse_threshold:
continue
if curr_sep > prev_sep or curr_sep > next_sep:
continue
left = max(utc_start, curr_t - scan_step)
right = min(utc_end, curr_t + scan_step)
if any(not (right <= seen_left or left >= seen_right) for seen_left, seen_right in seen_ranges):
continue
min_time_utc, min_sep = refine_planet_fixed_target_minimum_separation(
planet_body,
ra_hours,
dec_deg,
observer,
left,
right,
)
local_dt = min_time_utc.astimezone(tz)
if local_dt.year != year or local_dt.month != month:
continue
if min_sep > max_sep_deg:
continue
seen_ranges.append((left, right))
approaches.append({
"planet_key": planet_key,
"planet_label": planet_label,
"star_label": star_label,
"label": f"{planet_label} nahe {star_label}",
"separation_deg": float(min_sep),
"utc_iso": min_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"),
})
approaches.sort(key=lambda item: item["local_iso"])
deduplicated_approaches: list[dict] = []
for approach in approaches:
if deduplicated_approaches:
previous = deduplicated_approaches[-1]
same_pair = (
previous["planet_key"] == approach["planet_key"]
and previous["star_label"] == approach["star_label"]
)
previous_local = datetime.fromisoformat(previous["local_iso"])
current_local = datetime.fromisoformat(approach["local_iso"])
if same_pair and abs((current_local - previous_local).total_seconds()) <= 36 * 3600:
if float(approach["separation_deg"]) < float(previous["separation_deg"]):
deduplicated_approaches[-1] = approach
continue
deduplicated_approaches.append(approach)
return {
"ok": True,
"action": "planet_bright_star_approaches_for_month",
"selected": {
"year": year,
"month": month,
"timezone": timezone_name,
"max_separation_deg": max_sep_deg,
},
"approaches": deduplicated_approaches,
}
def action_moon_deep_sky_approaches_for_month(args: list[str]) -> dict:
if len(args) != 7:
fail(
@@ -3430,7 +3619,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", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "moon_deep_sky_approaches_for_month", "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", "jupiter_moons_one_side_for_month", "mercury_good_visibility_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", "time_changes_for_month", "moon_planet_approaches", "moon_deep_sky_approaches_for_month", "planet_bright_star_approaches_for_month", "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", "jupiter_moons_one_side_for_month", "mercury_good_visibility_for_month", "moon_star_occultations_for_month"]})
action = sys.argv[1]
args = sys.argv[2:]
@@ -3495,6 +3684,11 @@ def main() -> None:
print(json.dumps(result, ensure_ascii=False))
return
if action == "planet_bright_star_approaches_for_month":
result = action_planet_bright_star_approaches_for_month(args)
print(json.dumps(result, ensure_ascii=False))
return
if action == "planet_conjunctions_for_month":
result = action_planet_conjunctions_for_month(args)
print(json.dumps(result, ensure_ascii=False))
@@ -3550,7 +3744,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", "golden_handle_for_month", "season_changes_for_month", "time_changes_for_month", "moon_planet_approaches", "moon_deep_sky_approaches_for_month", "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", "jupiter_moons_one_side_for_month", "mercury_good_visibility_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", "time_changes_for_month", "moon_planet_approaches", "moon_deep_sky_approaches_for_month", "planet_bright_star_approaches_for_month", "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", "jupiter_moons_one_side_for_month", "mercury_good_visibility_for_month", "moon_star_occultations_for_month"]})
if __name__ == "__main__":