97 lines
3.3 KiB
Python
97 lines
3.3 KiB
Python
"""Native Patyayini from an annual Lagna and the seven classical planets.
|
|
|
|
Durations follow the degree-within-sign arcs. The longitudes passed in are
|
|
not recomputed here.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta
|
|
from typing import Any
|
|
|
|
SIGNS = (
|
|
"Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
|
|
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces",
|
|
)
|
|
_TIE = ("Jupiter", "Moon", "Mercury", "Saturn", "Lagna", "Sun", "Mars", "Venus")
|
|
|
|
|
|
def build_native_patyayini(
|
|
*,
|
|
ascendant_longitude: float,
|
|
planet_longitudes: dict[str, float],
|
|
start: datetime,
|
|
year_days: float = 365.25,
|
|
) -> dict[str, Any]:
|
|
points = {"Lagna": float(ascendant_longitude) % 360.0}
|
|
missing = []
|
|
for lord in ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"):
|
|
value = planet_longitudes.get(lord)
|
|
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
points[lord] = float(value) % 360.0
|
|
else:
|
|
missing.append(lord)
|
|
if missing:
|
|
raise ValueError("patyayini missing longitudes: " + ", ".join(missing))
|
|
ordered = sorted(
|
|
(
|
|
{
|
|
"lord": lord,
|
|
"longitude": longitude,
|
|
"sign": SIGNS[int(longitude // 30) % 12],
|
|
"degree_in_sign": longitude % 30.0,
|
|
}
|
|
for lord, longitude in points.items()
|
|
),
|
|
key=lambda item: (item["degree_in_sign"], _TIE.index(item["lord"])),
|
|
)
|
|
denominator = ordered[-1]["degree_in_sign"]
|
|
if denominator <= 0.0:
|
|
raise ValueError("patyayini denominator is zero")
|
|
arcs = []
|
|
previous = 0.0
|
|
for item in ordered:
|
|
arc = max(0.0, float(item["degree_in_sign"]) - previous)
|
|
arcs.append(arc)
|
|
previous = float(item["degree_in_sign"])
|
|
periods = []
|
|
antardasha = []
|
|
cursor = start
|
|
for index, (item, arc) in enumerate(zip(ordered, arcs), start=1):
|
|
end = cursor + timedelta(days=float(year_days) * arc / denominator)
|
|
periods.append({
|
|
**item,
|
|
"order": index,
|
|
"start": _format(cursor),
|
|
"end": _format(end),
|
|
})
|
|
sub_cursor = cursor
|
|
cycle = ordered[index - 1:] + ordered[:index - 1]
|
|
cycle_arcs = arcs[index - 1:] + arcs[:index - 1]
|
|
main_seconds = (end - cursor).total_seconds()
|
|
for sub_index, (sub_item, sub_arc) in enumerate(zip(cycle, cycle_arcs), start=1):
|
|
sub_end = sub_cursor + timedelta(seconds=main_seconds * sub_arc / denominator)
|
|
antardasha.append({
|
|
"main": item["lord"],
|
|
"sub": sub_item["lord"],
|
|
"main_order": index,
|
|
"sub_order": sub_index,
|
|
"start": _format(sub_cursor),
|
|
"end": _format(sub_end),
|
|
"boundary": _format(sub_end),
|
|
"boundary_status": "dasha_ending_dates",
|
|
})
|
|
sub_cursor = sub_end
|
|
cursor = end
|
|
return {
|
|
"schema": "jyotish.patyayini_patyamsha_dasha.v1",
|
|
"status": "parameter_sensitive",
|
|
"model": "degree_within_sign",
|
|
"periods": periods,
|
|
"antardasha_periods": antardasha,
|
|
}
|
|
|
|
|
|
def _format(value: datetime) -> str:
|
|
return value.strftime("%Y-%m-%d %H:%M:%S")
|