126 lines
4.7 KiB
Python
126 lines
4.7 KiB
Python
"""Twelve monthly charts measured from an annual solar-return instant.
|
|
|
|
The Sun search uses this repository's sidereal Sun. Each row's planets come
|
|
from ``compute_chart_data``. No apparent-position flag is passed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta
|
|
from typing import Any
|
|
|
|
try:
|
|
from annual_native.sidereal_bodies import sun_moon_longitudes
|
|
except ImportError: # pragma: no cover - repo root is on sys.path
|
|
from scripts.annual_native.sidereal_bodies import sun_moon_longitudes
|
|
|
|
SCHEMA = "jyotish.tajika_monthly_chart_pack.v1"
|
|
TROPICAL_YEAR_DAYS = 365.24219
|
|
_PLANETS = ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu")
|
|
|
|
|
|
def build_monthly_charts(
|
|
*,
|
|
annual_jd: float,
|
|
birth_sun_longitude: float,
|
|
latitude: float,
|
|
longitude: float,
|
|
tz_hours: float,
|
|
ayanamsa: str,
|
|
node_mode: str,
|
|
target_year: int,
|
|
timezone_id: str | None = None,
|
|
) -> dict[str, Any]:
|
|
rows = []
|
|
for month_index in range(1, 13):
|
|
target_sun = (float(birth_sun_longitude) + 30.0 * (month_index - 1)) % 360.0
|
|
guess = float(annual_jd) + TROPICAL_YEAR_DAYS * (month_index - 1) / 12.0
|
|
monthly_jd = _find_sun(guess, target_sun, ayanamsa, node_mode)
|
|
rows.append(_chart_row(
|
|
month_index, monthly_jd, target_sun, target_year,
|
|
latitude, longitude, tz_hours, ayanamsa, node_mode, timezone_id,
|
|
))
|
|
ready = [row for row in rows if row.get("status") == "partial_verified"]
|
|
return {
|
|
"schema": SCHEMA,
|
|
"status": "partial_verified" if len(ready) == 12 else "blocked",
|
|
"reason": None if len(ready) == 12 else "monthly_root_incomplete",
|
|
"target_year": target_year,
|
|
"rows": rows,
|
|
}
|
|
|
|
|
|
def _find_sun(guess: float, target: float, ayanamsa: str, node_mode: str) -> float | None:
|
|
jd = guess
|
|
for _ in range(24):
|
|
longitude = sun_moon_longitudes(jd, node_mode=node_mode, ayanamsa_name=ayanamsa)[0]
|
|
difference = ((longitude - target + 180.0) % 360.0) - 180.0
|
|
if abs(difference) < 0.0001:
|
|
return jd
|
|
future = sun_moon_longitudes(jd + 1.0 / 24.0, node_mode=node_mode, ayanamsa_name=ayanamsa)[0]
|
|
speed = (((future - longitude + 180.0) % 360.0) - 180.0) * 24.0
|
|
jd -= difference / speed if abs(speed) > 0.01 else difference
|
|
final = sun_moon_longitudes(jd, node_mode=node_mode, ayanamsa_name=ayanamsa)[0]
|
|
if abs(((final - target + 180.0) % 360.0) - 180.0) < 0.001:
|
|
return jd
|
|
return None
|
|
|
|
|
|
def _chart_row(
|
|
month_index: int,
|
|
jd: float | None,
|
|
target_sun: float,
|
|
target_year: int,
|
|
latitude: float,
|
|
longitude: float,
|
|
tz_hours: float,
|
|
ayanamsa: str,
|
|
node_mode: str,
|
|
timezone_id: str | None,
|
|
) -> dict[str, Any]:
|
|
if jd is None:
|
|
return {
|
|
"month_index": month_index,
|
|
"target_year": target_year,
|
|
"status": "blocked",
|
|
"reason": "solar_longitude_root_failed",
|
|
}
|
|
try:
|
|
from solar_return import _localize_return_datetime
|
|
from jyotish_engine import compute_chart_data
|
|
except ImportError:
|
|
from scripts.solar_return import _localize_return_datetime
|
|
from scripts.jyotish_engine import compute_chart_data
|
|
utc = datetime(2000, 1, 1) + timedelta(days=jd - 2451544.5)
|
|
local = _localize_return_datetime(utc, tz_hours, timezone_id)
|
|
chart, _asc, _chart_jd, _ayan = compute_chart_data(
|
|
utc.year, utc.month, utc.day, utc.hour, utc.minute,
|
|
float(latitude), float(longitude), 0,
|
|
node_mode, second=utc.second, ayanamsa_name=ayanamsa,
|
|
)
|
|
if not isinstance(chart, dict):
|
|
raise RuntimeError(f"compute_chart_data returned no chart for monthly jd {jd}")
|
|
planets = chart.get("planets") if isinstance(chart.get("planets"), dict) else {}
|
|
sun = planets.get("Sun") if isinstance(planets.get("Sun"), dict) else {}
|
|
ascendant = chart.get("ascendant") if isinstance(chart.get("ascendant"), dict) else {}
|
|
return {
|
|
"month_index": month_index,
|
|
"target_year": target_year,
|
|
"jd_ut": jd,
|
|
"local_datetime": local.strftime("%Y-%m-%d %H:%M:%S"),
|
|
"utc_datetime": utc.strftime("%Y-%m-%d %H:%M:%S"),
|
|
"target_sun_longitude": target_sun,
|
|
"sun_longitude": sun.get("degree_raw"),
|
|
"ascendant": {
|
|
"sign": ascendant.get("sign"),
|
|
"degree_in_sign": ascendant.get("degree_in_sign", ascendant.get("degree")),
|
|
"longitude": ascendant.get("degree_raw", ascendant.get("lon")),
|
|
},
|
|
"planet_longitudes": {
|
|
name: planets[name].get("degree_raw")
|
|
for name in _PLANETS
|
|
if isinstance(planets.get(name), dict) and planets[name].get("degree_raw") is not None
|
|
},
|
|
"status": "partial_verified",
|
|
}
|