feat(domy): Placidus i Koch + jawny fallback poza kołem podbiegunowym
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m30s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 17s
Testy / Kontrola składni wszystkich warstw (push) Successful in 8s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m28s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 18s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 8s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m30s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 17s
Testy / Kontrola składni wszystkich warstw (push) Successful in 8s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m28s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 18s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 8s
Etap 2: dwa systemy łuku dobowego — jedyne, które mają miejsca, gdzie po prostu NIE ISTNIEJĄ. Koch zgadza się z wyrocznią co do zera; Placidus do 1,7e-6°, przy czym to próg zbieżności WYROCZNI, nie nasz: nasze cuspy spełniają definicję Placidusa z dokładnością 1e-12° (osobny test, działa bez swissepha). Placidus jako jedyny nie ma wzoru zamkniętego — cusp jest zdefiniowany warunkiem na samego siebie („punkt, który przebył 1/3 swojego półłuku"), więc iterujemy po punkcie stałym do 1e-11°. Brak zbieżności traktujemy jako wyjście poza dziedzinę, nie jako wynik. Koch okazał się natomiast ZAMKNIĘTY: jego kryterium to czas od wschodu stopnia stojącego na MC, a półłuk tego stopnia znamy wprost z jego deklinacji. Definicja za Astrodienst (astro.com/astrowiki/en/Koch_House_System) — nie zgadywana. Powyżej koła podbiegunowego oba odmawiają liczenia i wyrocznia odmawia dokładnie tych samych przypadków (5245/20 000 losowych, zero rozjazdów dziedziny). Odmowa jest wyjątkiem, nie liczbą: cicha podmiana systemu jest niewykrywalna z wykresu. Ustępstwo wobec rzeczywistości siedzi osobno, w cusps_detailed(): podstawia Porphyry'ego i ZAWSZE zostawia ślad. Ten ślad idzie wszystkimi trzema wyjściami — na ekran (ramka, nie „muted"), w prompt do modelu (inaczej napisze „Twój Placidus" o Porphyrym) i do PDF-a, w ramce PRZED rysunkami. Astrolog z Tromsø dostaje wynik i wie, że go dostał inaczej. Przy okazji: nazwy systemów były zaszyte w czterech szablonach naraz. Przy trzech systemach uchodziło to na sucho, przy dziesięciu nie — jest katalog w jednym miejscu, a testy szablonów RENDERUJĄ je zamiast szukać tekstu w źródle, więc łapią też literówki w Jinja. Większe jądro efemeryd (de441): świadomie zdegradowane do „nice to have" — rozszerza wyłącznie zakres dat, nie poprawia niczego w obecnym. Odnotowane w domain.py przy JD_MIN/JD_MAX, żeby nie wróciło po cichu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -64,10 +64,12 @@ def test_primary_always_first_even_if_not_listed(own_engine, reference_moment):
|
||||
|
||||
|
||||
def test_unknown_extra_system_is_ignored(own_engine, reference_moment):
|
||||
"""Nieobsługiwany system (np. placidus — dojdzie przez swisseph osobno) jest
|
||||
po prostu pomijany, nie wywala horoskopu."""
|
||||
"""Nieznana nazwa systemu jest po prostu pomijana, nie wywala horoskopu.
|
||||
|
||||
(Placidus był tu kiedyś przykładem systemu nieobsługiwanego — od Etapu 2
|
||||
jest liczony, więc przykładem musi być nazwa, której naprawdę nie znamy.)"""
|
||||
chart = build_chart(own_engine, reference_moment, "whole_sign",
|
||||
house_systems=["placidus", "equal"])
|
||||
house_systems=["nie-ma-takiego-systemu", "equal"])
|
||||
assert [h["system"] for h in chart["house_systems"]] == ["whole_sign", "equal"]
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Testy osi i domów — czysta matematyka (LOG-05, bez efemeryd)."""
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
from app.engine import houses as H
|
||||
@@ -126,3 +128,71 @@ def test_morinus_ignores_latitude():
|
||||
def test_unknown_system_is_rejected():
|
||||
with pytest.raises(ValueError):
|
||||
H.cusps_for(100.0, 23.4393, 50.0, "nie-ma-takiego")
|
||||
|
||||
|
||||
# ── systemy łuku dobowego: Placidus i Koch (Etap 2) ──────────────────────
|
||||
|
||||
DIURNAL = ("placidus", "koch")
|
||||
TROMSO_LAT = 69.65 # za kołem podbiegunowym — tam te systemy nie istnieją
|
||||
KRAKOW_LAT = 50.06
|
||||
|
||||
|
||||
@pytest.mark.parametrize("system", DIURNAL)
|
||||
def test_diurnal_systems_refuse_beyond_the_polar_circle(system):
|
||||
"""Odmowa, nie liczba. Cicha podmiana systemu jest niewykrywalna z wykresu."""
|
||||
with pytest.raises(H.HouseSystemUndefined):
|
||||
H.cusps_for(100.0, 23.4393, TROMSO_LAT, system)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("system", DIURNAL)
|
||||
def test_fallback_says_what_it_did(system):
|
||||
cs = H.cusps_detailed(100.0, 23.4393, TROMSO_LAT, system)
|
||||
assert cs.is_fallback and cs.system == H.FALLBACK_SYSTEM and cs.requested == system
|
||||
assert len(cs.cusps) == 12
|
||||
assert cs.notice and system in cs.notice and H.FALLBACK_SYSTEM in cs.notice
|
||||
|
||||
|
||||
@pytest.mark.parametrize("system", DIURNAL)
|
||||
def test_no_fallback_and_no_notice_where_the_system_exists(system):
|
||||
cs = H.cusps_detailed(100.0, 23.4393, KRAKOW_LAT, system)
|
||||
assert not cs.is_fallback and cs.notice is None
|
||||
assert cs.cusps == H.cusps_for(100.0, 23.4393, KRAKOW_LAT, system)
|
||||
|
||||
|
||||
def test_placidus_cusps_satisfy_their_own_definition():
|
||||
"""Sprawdzian NIEZALEŻNY od wyroczni: czy znaleziony punkt naprawdę przebył
|
||||
zadany ułamek swojego półłuku. Wyrocznia mówi „zgadza się z swissephem",
|
||||
to mówi „zgadza się z definicją Placidusa" — i działa bez swissepha."""
|
||||
ramc, eps, lat = 212.9986, 23.4393, 66.3144 # tuż pod kołem, najtrudniej
|
||||
eps_rad = math.radians(eps)
|
||||
c = H.cusps_for(ramc, eps, lat, "placidus")
|
||||
for house, (frac, nocturnal) in ((11, (1 / 3, False)), (12, (2 / 3, False)),
|
||||
(2, (2 / 3, True)), (3, (1 / 3, True))):
|
||||
lon = c[house - 1]
|
||||
dec = H._declination_of_ecliptic_lon(lon, eps_rad)
|
||||
ad = H._ascensional_difference(dec, lat)
|
||||
ra = math.degrees(math.atan2(math.sin(math.radians(lon)) * math.cos(eps_rad),
|
||||
math.cos(math.radians(lon))))
|
||||
want = (ramc + 180.0 - frac * (90.0 - ad) if nocturnal
|
||||
else ramc + frac * (90.0 + ad))
|
||||
assert abs((ra - want + 180.0) % 360.0 - 180.0) < 1e-9, f"dom {house}"
|
||||
|
||||
|
||||
def test_koch_reduces_to_equal_spacing_on_the_equator():
|
||||
"""Na równiku półłuk każdego stopnia to dokładnie 90°, więc trójdzielenie
|
||||
czasu daje Ascendenty co 30° czasu gwiazdowego — dobry test zdrowia wzoru."""
|
||||
ramc, eps = 100.0, 23.4393
|
||||
c = H.cusps_for(ramc, eps, 0.0, "koch")
|
||||
for house, offset in ((11, -60.0), (12, -30.0), (2, 30.0), (3, 60.0)):
|
||||
assert c[house - 1] == pytest.approx(
|
||||
H.compute_asc(ramc + offset, eps, 0.0), abs=1e-9), f"dom {house}"
|
||||
|
||||
|
||||
def test_polar_circle_boundary_moves_with_the_date():
|
||||
"""Granica dziedziny NIE jest stałą 66,56° — zależy od ε, czyli od daty."""
|
||||
lat = 66.60
|
||||
assert lat > H.polar_circle(23.4393) # dziś: poza dziedziną
|
||||
assert lat < H.polar_circle(23.3528) # rok 2270: już w dziedzinie
|
||||
with pytest.raises(H.HouseSystemUndefined):
|
||||
H.cusps_for(100.0, 23.4393, lat, "placidus")
|
||||
assert len(H.cusps_for(100.0, 23.3528, lat, "placidus")) == 12
|
||||
|
||||
Reference in New Issue
Block a user