Files
Jyotisha/scripts/gulika.py
T
2026-07-14 18:05:53 +08:00

68 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Swiss-Ephemeris Gulika calculator using the Prasna Marga Ghatika table."""
from __future__ import annotations
from datetime import datetime
from typing import Any
import swisseph as swe
try:
from saham_daynight import determine_daytime
except ImportError:
from scripts.saham_daynight import determine_daytime
# Monday=0, matching datetime.weekday(). Values are the end of Saturn's share
# measured in Ghatika from the relevant sunrise/sunset (30 Ghatika per period).
GHATIKA_END = {
0: {"day": 22, "night": 6},
1: {"day": 18, "night": 2},
2: {"day": 14, "night": 26},
3: {"day": 10, "night": 22},
4: {"day": 6, "night": 18},
5: {"day": 2, "night": 14},
6: {"day": 26, "night": 10},
}
def _sidereal_ascendant(jd_ut: float, lat: float, lon: float) -> float:
swe.set_sid_mode(swe.SIDM_LAHIRI)
cusps, ascmc = swe.houses_ex(jd_ut, lat, lon, b"P", swe.FLG_SIDEREAL)
return float(ascmc[0]) % 360
def calculate_gulika(
moment: datetime,
*,
lat: float,
lon: float,
tz: float,
) -> dict[str, Any]:
"""Return Gulika from local moment/location using Swiss sunrise and sunset."""
daynight = determine_daytime(moment, lat=lat, lon=lon, tz=tz)
is_day = bool(daynight["is_daytime"])
period = "day" if is_day else "night"
ghatika_end = GHATIKA_END[moment.weekday()][period]
start_jd = daynight["sunrise_jd_ut"] if is_day else daynight["sunset_jd_ut"]
end_jd = daynight["sunset_jd_ut"] if is_day else daynight["sunrise_jd_ut"] + 1.0
if end_jd <= start_jd:
end_jd += 1.0
segment_jd = start_jd + (end_jd - start_jd) * (ghatika_end / 30.0)
longitude = _sidereal_ascendant(segment_jd, float(lat), float(lon))
return {
"scope": "gulika_prasna_marga",
"status": "partial",
"longitude": round(longitude, 6),
"sign_idx": int(longitude / 30) % 12,
"degree_in_sign": round(longitude % 30, 6),
"period": period,
"weekday": moment.weekday(),
"ghatika_end": ghatika_end,
"segment_jd_ut": segment_jd,
"daynight_evidence": daynight,
"ayanamsa": "lahiri",
"rule_source": "references/prashna-complete-guide.md#3.5",
"boundary": "Formula is implemented from the local classical guide; external JHora/PyJHora numeric parity remains required before enabling Sphuta or verdict layers.",
}