"""Osie i domy — czysta matematyka sferyczna (LOG-05). Bezstanowe funkcje: z lokalnego czasu gwiazdowego (RAMC), nachylenia ekliptyki (ε) i szerokości geograficznej (φ) wyliczają Ascendent i MC, a stąd cusps domów dla prostych systemów (Whole Sign, Equal, Porphyry). Niezależne od silnika — silnik dostarcza tylko RAMC i ε. """ from __future__ import annotations import math from app.engine.formats import SIGN_ABBR, norm360, sign_index # noqa: F401 WHOLE_SIGN = "whole_sign" EQUAL = "equal" PORPHYRY = "porphyry" SYSTEMS = (WHOLE_SIGN, EQUAL, PORPHYRY) def mean_obliquity(tt_jd: float) -> float: """Średnie nachylenie ekliptyki [°] dla daty (Julian TT). Wystarcza do domów.""" t = (tt_jd - 2451545.0) / 36525.0 arcsec = 84381.448 - 46.8150 * t - 0.00059 * t * t + 0.001813 * t ** 3 return arcsec / 3600.0 def compute_mc(ramc_deg: float, eps_deg: float) -> float: r, e = math.radians(ramc_deg), math.radians(eps_deg) mc = math.atan2(math.sin(r), math.cos(r) * math.cos(e)) return norm360(math.degrees(mc)) def compute_asc(ramc_deg: float, eps_deg: float, lat_deg: float) -> float: r, e, phi = math.radians(ramc_deg), math.radians(eps_deg), math.radians(lat_deg) asc = math.atan2( math.cos(r), -(math.sin(r) * math.cos(e) + math.tan(phi) * math.sin(e)), ) return norm360(math.degrees(asc)) def _trisect(a: float, b: float) -> tuple[float, float]: """Dwa punkty dzielące łuk a→b (w kierunku zodiaku) na trzy równe części.""" span = (b - a) % 360.0 return norm360(a + span / 3.0), norm360(a + 2.0 * span / 3.0) def cusps(asc: float, mc: float, system: str) -> list[float]: """Zwraca 12 cusps (długości) domów 1..12.""" if system == WHOLE_SIGN: start = sign_index(asc) * 30.0 return [norm360(start + 30.0 * i) for i in range(12)] if system == EQUAL: return [norm360(asc + 30.0 * i) for i in range(12)] if system == PORPHYRY: dsc, ic = norm360(asc + 180.0), norm360(mc + 180.0) c = [0.0] * 12 c[0], c[3], c[6], c[9] = asc, ic, dsc, mc c[1], c[2] = _trisect(asc, ic) # domy 2,3 c[4], c[5] = _trisect(ic, dsc) # domy 5,6 c[7], c[8] = _trisect(dsc, mc) # domy 8,9 c[10], c[11] = _trisect(mc, asc) # domy 11,12 return c raise ValueError(f"nieznany system domów: {system}") def assign_house(lon: float, cusp_list: list[float]) -> int: """Numer domu (1..12), w którym leży dana długość ekliptyczna.""" lon = norm360(lon) for i in range(12): start = cusp_list[i] end = cusp_list[(i + 1) % 12] span = (end - start) % 360.0 offset = (lon - start) % 360.0 if offset < span: return i + 1 return 12