Files
Jyotisha/scripts/kp_public_worked_example_comparator.py
T
2026-07-23 12:03:58 +08:00

141 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""Compare one public KP cusp row with an isolated VedicAstro observation.
This is a field-level observation. It must not be promoted to verified KP
timing, prediction, or truth-matrix evidence.
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
EXPECTED: dict[str, Any] = {
"HouseNr": 5,
"LonDecDeg": 69 + 7 / 60 + 14 / 3600,
"Rasi": "Gemini",
"RasiLord": "Mercury",
"Nakshatra": "Ardra",
"NakshatraLord": "Rahu",
"SubLord": "Jupiter",
"SubSubLord": "Saturn",
}
PUBLIC_AYANAMSA_DEGREES = 23 + 32 / 60 + 29 / 3600
COMPARABLE_FIELDS = (
"Rasi",
"RasiLord",
"Nakshatra",
"NakshatraLord",
"SubLord",
"SubSubLord",
)
LONGITUDE_TOLERANCE_ARCSECONDS = 60.0
def _find_house(raw: dict[str, Any], house_number: int) -> dict[str, Any]:
for house in raw.get("raw", {}).get("houses", []):
if house.get("HouseNr") == house_number:
return house
raise ValueError(f"Missing house {house_number}")
def build_report(observed_house: dict[str, Any], observed_ayanamsa_degrees: float | None = None) -> dict[str, Any]:
delta_arcseconds = round(
abs(float(observed_house["LonDecDeg"]) - EXPECTED["LonDecDeg"]) * 3600,
1,
)
field_comparisons = [
{
"field": field,
"public_value": EXPECTED[field],
"vedicastro_value": observed_house.get(field),
"matches": observed_house.get(field) == EXPECTED[field],
}
for field in COMPARABLE_FIELDS
]
matched_count = sum(row["matches"] for row in field_comparisons)
longitude_within_tolerance = delta_arcseconds <= LONGITUDE_TOLERANCE_ARCSECONDS
ayanamsa_delta_arcseconds = (
round(abs(observed_ayanamsa_degrees - PUBLIC_AYANAMSA_DEGREES) * 3600, 1)
if observed_ayanamsa_degrees is not None
else None
)
return {
"scope": "kp_public_worked_example_field_comparison",
"status": (
"public_kp_worked_example_partial_field_match"
if matched_count == len(field_comparisons) and longitude_within_tolerance
else "public_kp_worked_example_field_mismatch"
),
"claim_status": "observation_only",
"consumer_policy": "research_observation_only",
"production_tuning_allowed": False,
"truth_matrix_allowed": False,
"public_source": {
"url": "https://www.scribd.com/document/462272035/KP-Ezine-159-April-2020",
"source_lines": [2775, 2782, 2800, 2807, 2816, 2859, 2869],
"case_boundary": (
"The public page supplies one 5th-cusp row and case settings; "
"it is not a full independent timing validation."
),
},
"public_case": {
"birth": "1983-11-04 14:04:00, Brunswick, Georgia, USA",
"coordinates": "31N08, 81W30",
"timezone": "UTC-05:00, DST 0",
"ayanamsa": "KP Ayanamsa / KP New value printed as 23°32'29\"",
"house_system": "Placidus",
"expected_fifth_cusp": EXPECTED,
},
"comparison": {
"longitude_delta_arcseconds": delta_arcseconds,
"longitude_tolerance_arcseconds": LONGITUDE_TOLERANCE_ARCSECONDS,
"longitude_within_tolerance": longitude_within_tolerance,
"public_ayanamsa_degrees": PUBLIC_AYANAMSA_DEGREES,
"vedicastro_ayanamsa_degrees": observed_ayanamsa_degrees,
"ayanamsa_delta_arcseconds": ayanamsa_delta_arcseconds,
"field_comparisons": field_comparisons,
},
"summary": {
"matched_field_count": matched_count,
"mismatched_field_count": len(field_comparisons) - matched_count,
"longitude_within_tolerance": longitude_within_tolerance,
},
"claim_boundary": (
"The source's fifth-cusp sign, star, sub and sub-sub labels agree with "
"this isolated VedicAstro replay, and the cusp longitude differs by less "
"than one arcminute. The printed KP New and replayed Krishnamurti ayanamsa "
"also differ by 43 arcseconds, so they must not be treated as identical. "
"This does not identify hosted VedAstro, validate remaining cusps or "
"sub-sub-sub values, or validate timing/predictions."
),
"remaining_requirements": [
"A source or software identity proving the exact KP New ayanamsa implementation used by the public table.",
"Complete multi-cusp and planet rows with independent reproduction before exact KP timing claims.",
"Independent outcome holdout; no prediction claim follows from one technical row.",
],
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--vedicastro-raw", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
raw = json.loads(args.vedicastro_raw.read_text(encoding="utf-8"))
observed_ayanamsa = raw.get("raw", {}).get("ayanamsa_identity", {}).get("value_degrees")
report = build_report(_find_house(raw, 5), observed_ayanamsa_degrees=observed_ayanamsa)
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())