Nutze Skyfield fuer praezisere Daemmerungsberechnung

This commit is contained in:
2026-07-13 19:35:39 +02:00
parent dc5292735f
commit 97357a88e0
1092 changed files with 370248 additions and 61 deletions
+60 -6
View File
@@ -1,10 +1,20 @@
#!/usr/bin/env python3
import math
import os
import sys
from datetime import datetime, timedelta, timezone
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP, localcontext
from zoneinfo import ZoneInfo
import astronomy
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
VENDOR_DIR = os.path.join(SCRIPT_DIR, "vendor")
VENDOR_DATA_DIR = os.path.join(SCRIPT_DIR, "vendor_data")
SKYFIELD_EPHEMERIS_PATH = os.path.join(VENDOR_DATA_DIR, "de421.bsp")
if VENDOR_DIR not in sys.path:
sys.path.insert(0, VENDOR_DIR)
try:
from astropy.time import Time as AstropyTime
ASTROPY_AVAILABLE = True
@@ -12,6 +22,15 @@ except ImportError:
AstropyTime = None
ASTROPY_AVAILABLE = False
try:
from skyfield.api import load as skyfield_load
from skyfield.api import wgs84 as skyfield_wgs84
SKYFIELD_AVAILABLE = True
except ImportError:
skyfield_load = None
skyfield_wgs84 = None
SKYFIELD_AVAILABLE = False
SPEED_OF_LIGHT_KM_S = 299792.458
H0 = 70.0
OMEGA_M = 0.3
@@ -102,6 +121,9 @@ EQ_TO_GAL_MATRIX = (
(-0.8676661490190047, -0.1980763734312015, 0.4559837761750669),
)
_SKYFIELD_TIMESCALE = None
_SKYFIELD_PLANETS = None
def parse_decimal_input(value: str) -> Decimal | None:
text = str(value or "").strip()
@@ -177,6 +199,32 @@ def normalize_angle_degrees(value: float) -> float:
return normalized
def skyfield_context() -> tuple[object, object]:
global _SKYFIELD_TIMESCALE, _SKYFIELD_PLANETS
if not SKYFIELD_AVAILABLE:
raise RuntimeError("Skyfield ist lokal nicht verfuegbar.")
if not os.path.exists(SKYFIELD_EPHEMERIS_PATH):
raise RuntimeError("Die lokale Skyfield-Ephemeride de421.bsp fehlt.")
if _SKYFIELD_TIMESCALE is None:
_SKYFIELD_TIMESCALE = skyfield_load.timescale()
if _SKYFIELD_PLANETS is None:
_SKYFIELD_PLANETS = skyfield_load(SKYFIELD_EPHEMERIS_PATH)
return _SKYFIELD_TIMESCALE, _SKYFIELD_PLANETS
def calculate_solar_altaz_skyfield(dt_utc: datetime, latitude: float, longitude: float) -> tuple[float, float]:
timescale, planets = skyfield_context()
time_value = timescale.from_datetime(dt_utc.astimezone(timezone.utc))
observer = planets["earth"] + skyfield_wgs84.latlon(latitude, longitude)
altitude, azimuth, _distance = observer.at(time_value).observe(planets["sun"]).apparent().altaz()
return float(altitude.degrees), float(azimuth.degrees)
def transpose_matrix(matrix: tuple[tuple[float, float, float], ...]) -> tuple[tuple[float, float, float], ...]:
return tuple(tuple(matrix[row][column] for row in range(3)) for column in range(3))
@@ -557,15 +605,21 @@ def convert_tools(payload: dict) -> dict:
return {"valid": False, "fields": {key: "" for key in TOOLS_ORDER}}
dt_utc = local_dt.astimezone(timezone.utc)
time_value = dt_to_time(dt_utc)
observer = astronomy.Observer(latitude, longitude, 0.0)
equator = astronomy.Equator(astronomy.Body.Sun, time_value, observer, True, True)
horizon = astronomy.Horizon(time_value, observer, equator.ra, equator.dec, astronomy.Refraction.Normal)
try:
altitude_deg, azimuth_deg = calculate_solar_altaz_skyfield(dt_utc, latitude, longitude)
except Exception:
time_value = dt_to_time(dt_utc)
observer = astronomy.Observer(latitude, longitude, 0.0)
equator = astronomy.Equator(astronomy.Body.Sun, time_value, observer, True, True)
horizon = astronomy.Horizon(time_value, observer, equator.ra, equator.dec, astronomy.Refraction.Normal)
altitude_deg = float(horizon.altitude)
azimuth_deg = float(horizon.azimuth)
fields = {
"utc": format_utc_datetime(dt_utc),
"altitude": format_decimal_german(horizon.altitude, max_fraction_digits=4),
"azimuth": format_decimal_german(normalize_angle_degrees(horizon.azimuth), max_fraction_digits=4),
"altitude": format_decimal_german(altitude_deg, max_fraction_digits=4),
"azimuth": format_decimal_german(normalize_angle_degrees(azimuth_deg), max_fraction_digits=4),
}
return {"valid": True, "fields": fields}