从报告日期所在月起列出 36 个月的九星表。报告口径和 KP 岁差口径各一张,另有五级大运月采样。黄经仍走本系统恒星计算。
Co-Authored-By: Grok (agent loop)
This commit is contained in:
co-authored by
Grok (agent loop)
parent
4c405d2a51
commit
355946cd1a
@@ -0,0 +1,181 @@
|
||||
"""36-month ephemeris from the report month, on this repository's sidereal frame.
|
||||
|
||||
Longitudes come from ``_calc_sidereal_planets_for_jd`` (true precession, not
|
||||
apparent position). The KP table uses the same function with the KP ayanamsa.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
|
||||
_PLANETS = ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn", "Rahu", "Ketu")
|
||||
SCHEMA = "jyotish.report_month_ephemeris.v1"
|
||||
|
||||
|
||||
def attach_report_month_ephemeris(args: Any, annual: dict | None = None) -> dict:
|
||||
today = getattr(args, "today", None)
|
||||
if isinstance(today, str) and len(today) >= 7 and today[4] == "-":
|
||||
start_year = int(today[0:4])
|
||||
start_month = int(today[5:7])
|
||||
start_source = "report_date"
|
||||
else:
|
||||
profile = annual.get("profile") if isinstance(annual, dict) and isinstance(annual.get("profile"), dict) else {}
|
||||
target = profile.get("target_year", getattr(args, "target_year", None))
|
||||
start_year = int(target if target not in (None, "") else args.year)
|
||||
start_month = 1
|
||||
start_source = "target_january_report_date_missing"
|
||||
settings = {}
|
||||
if isinstance(annual, dict) and isinstance(annual.get("profile"), dict):
|
||||
settings = annual["profile"]
|
||||
ayanamsa = str(getattr(args, "ayanamsa", None) or settings.get("ayanamsa") or "lahiri")
|
||||
node_mode = str(getattr(args, "node_mode", None) or settings.get("node_mode") or "mean")
|
||||
return build_report_ephemeris(
|
||||
birth_year=int(args.year),
|
||||
birth_month=int(args.month),
|
||||
birth_day=int(args.day),
|
||||
birth_hour=int(args.hour),
|
||||
birth_minute=int(getattr(args, "minute", 0) or 0),
|
||||
birth_second=int(getattr(args, "second", 0) or 0),
|
||||
latitude=float(args.lat),
|
||||
longitude=float(args.lon),
|
||||
tz_hours=float(args.tz),
|
||||
ayanamsa=ayanamsa,
|
||||
node_mode=node_mode,
|
||||
start_year=start_year,
|
||||
start_month=start_month,
|
||||
start_source=start_source,
|
||||
)
|
||||
|
||||
|
||||
def build_report_ephemeris(
|
||||
*,
|
||||
birth_year: int,
|
||||
birth_month: int,
|
||||
birth_day: int,
|
||||
birth_hour: int,
|
||||
birth_minute: int,
|
||||
birth_second: int,
|
||||
latitude: float,
|
||||
longitude: float,
|
||||
tz_hours: float,
|
||||
ayanamsa: str,
|
||||
node_mode: str,
|
||||
start_year: int,
|
||||
start_month: int,
|
||||
start_source: str,
|
||||
month_count: int = 36,
|
||||
) -> dict:
|
||||
if month_count < 1:
|
||||
raise ValueError("month_count must be at least 1")
|
||||
if not 1 <= int(start_month) <= 12:
|
||||
raise ValueError(f"start_month out of range: {start_month}")
|
||||
natal_jd = _jd(birth_year, birth_month, birth_day, birth_hour + birth_minute / 60 + birth_second / 3600, tz_hours)
|
||||
natal = _bodies(natal_jd, node_mode, ayanamsa)
|
||||
moon_body = natal.get("Moon") if isinstance(natal.get("Moon"), dict) else {}
|
||||
moon = moon_body.get("degree_raw")
|
||||
if not isinstance(moon, (int, float)) or isinstance(moon, bool):
|
||||
raise RuntimeError("natal Moon longitude missing for the monthly Vimshottari sample")
|
||||
report_rows = []
|
||||
kp_rows = []
|
||||
level_rows = []
|
||||
level_error = None
|
||||
for offset in range(month_count):
|
||||
year, month = _add_months(start_year, start_month, offset)
|
||||
jd = _jd(year, month, 1, 12.0, tz_hours)
|
||||
month_text = f"{year:04d}-{month:02d}"
|
||||
report_rows.extend(_planet_rows(month_text, _bodies(jd, node_mode, ayanamsa)))
|
||||
kp_rows.extend(_planet_rows(month_text, _bodies(jd, node_mode, "kp")))
|
||||
if level_error is None:
|
||||
try:
|
||||
level_rows.append(_levels(birth_year, birth_month, birth_day, birth_hour, birth_minute, birth_second, moon, year, month))
|
||||
except Exception as exc:
|
||||
level_error = f"{exc.__class__.__name__}: {exc}"
|
||||
return {
|
||||
"schema": SCHEMA,
|
||||
"status": "partial_verified",
|
||||
"start_source": start_source,
|
||||
"start_month": f"{int(start_year):04d}-{int(start_month):02d}",
|
||||
"month_count": month_count,
|
||||
"ayanamsa": ayanamsa,
|
||||
"kp_ayanamsa": "kp",
|
||||
"node_mode": node_mode,
|
||||
"anchor": "local_month_start_noon",
|
||||
"report_rows": report_rows,
|
||||
"kp_rows": kp_rows,
|
||||
"vimshottari_rows": level_rows,
|
||||
"vimshottari_reason": level_error,
|
||||
"sample": {
|
||||
"month": report_rows[0]["month"] if report_rows else None,
|
||||
"jd_ut": _jd(start_year, start_month, 1, 12.0, tz_hours),
|
||||
"moon_degree_raw": next(row["degree_raw"] for row in report_rows if row["planet"] == "Moon"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _planet_rows(month_text: str, bodies: dict) -> list[dict]:
|
||||
rows = []
|
||||
for name in _PLANETS:
|
||||
body = bodies.get(name)
|
||||
if not isinstance(body, dict) or not isinstance(body.get("degree_raw"), (int, float)):
|
||||
raise RuntimeError(f"{name} longitude missing for {month_text}")
|
||||
rows.append({
|
||||
"month": month_text,
|
||||
"planet": name,
|
||||
"sign": body.get("sign"),
|
||||
"longitude": round(float(body["degree_raw"]), 6),
|
||||
"degree_raw": float(body["degree_raw"]),
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def _bodies(jd: float, node_mode: str, ayanamsa: str) -> dict:
|
||||
try:
|
||||
from jyotish_engine import _calc_sidereal_planets_for_jd
|
||||
except ImportError:
|
||||
from scripts.jyotish_engine import _calc_sidereal_planets_for_jd
|
||||
data, _ayan = _calc_sidereal_planets_for_jd(jd, node_mode=node_mode, ayanamsa_name=ayanamsa)
|
||||
if not isinstance(data, dict) or "Moon" not in data:
|
||||
raise RuntimeError(f"sidereal planets missing at jd {jd}")
|
||||
return data
|
||||
|
||||
|
||||
def _levels(year, month, day, hour, minute, second, moon_lon: float, anchor_year: int, anchor_month: int) -> dict:
|
||||
try:
|
||||
from kp_monthly_vimshottari import build_monthly_vimshottari_snapshot
|
||||
except ImportError:
|
||||
from scripts.kp_monthly_vimshottari import build_monthly_vimshottari_snapshot
|
||||
snapshot = build_monthly_vimshottari_snapshot(
|
||||
birth_dt=datetime(year, month, day, hour, minute, second),
|
||||
moon_lon=float(moon_lon),
|
||||
anchor_dt=datetime(anchor_year, anchor_month, 1, 12, 0, 0),
|
||||
)
|
||||
levels = snapshot.get("levels") if isinstance(snapshot.get("levels"), dict) else {}
|
||||
return {
|
||||
"month": f"{anchor_year:04d}-{anchor_month:02d}",
|
||||
"mahadasha": _lord(levels.get("mahadasha")),
|
||||
"antardasha": _lord(levels.get("antardasha")),
|
||||
"pratyantardasha": _lord(levels.get("pratyantardasha")),
|
||||
"sookshma": _lord(levels.get("sookshma")),
|
||||
"prana": _lord(levels.get("prana")),
|
||||
}
|
||||
|
||||
|
||||
def _lord(value: Any) -> str | None:
|
||||
if isinstance(value, dict):
|
||||
lord = value.get("lord") or value.get("planet")
|
||||
return str(lord) if lord else None
|
||||
if isinstance(value, str) and value.strip():
|
||||
return value.strip()
|
||||
return None
|
||||
|
||||
|
||||
def _jd(year: int, month: int, day: int, local_hours: float, tz_hours: float) -> float:
|
||||
import swisseph as swe
|
||||
return float(swe.julday(int(year), int(month), int(day), float(local_hours) - float(tz_hours)))
|
||||
|
||||
|
||||
def _add_months(year: int, month: int, offset: int) -> tuple[int, int]:
|
||||
index = int(year) * 12 + (int(month) - 1) + int(offset)
|
||||
return index // 12, index % 12 + 1
|
||||
Reference in New Issue
Block a user