""" Step 1: Ground truth — compute houses/angles locally using swisseph directly. No server, no container, no network. Pure Python + Swiss Ephemeris. This gives us the reference values to compare all servers against. """ from __future__ import annotations import sys import os # Add ephemeris-mcp to path so we can import swisseph from its venv sys.path.insert(0, os.path.expanduser("~/.openclaw/workspace/ephemeris-mcp/src")) import swisseph as swe # ── Reference birth data ────────────────────────────────────────────── PEOPLE = { "trump": { "label": "Donald Trump", "datetime_utc": "1946-06-14T14:54:00", # 10:54 AM EDT = 14:54 UTC "lat": 40.6413, "lon": -73.7781, }, "einstein": { "label": "Albert Einstein", "datetime_utc": "1879-03-14T10:37:00", # 11:30 AM LMT (UTC+0:53) = 10:37 UTC "lat": 48.39841, "lon": 9.99155, }, "chaka": { "label": "Chaka Khan", "datetime_utc": "1953-03-24T03:05:00", # 9:05 PM CST (UTC-6) = 03:05 UTC next day "lat": 41.87811, "lon": -87.62980, }, } TOLERANCE = 0.01 # degrees — very tight def _parse_utc_dt(dt_str): from datetime import datetime return datetime.fromisoformat(dt_str) def _compute_houses(dt_utc, lat, lon, house_system=b"P"): jd = swe.julday(dt_utc.year, dt_utc.month, dt_utc.day, dt_utc.hour + dt_utc.minute / 60 + dt_utc.second / 3600) cusps, ascmc = swe.houses(jd, lat, lon, house_system) return { "jd": jd, "cusps": list(cusps), "asc": ascmc[0], "mc": ascmc[1], "dsc": ascmc[0] + 180.0 if ascmc[0] < 180.0 else ascmc[0] - 180.0, "ic": ascmc[1] + 180.0 if ascmc[1] < 180.0 else ascmc[1] - 180.0, } def _fmt(lon): signs = ["Ari", "Tau", "Gem", "Can", "Leo", "Vir", "Lib", "Sco", "Sag", "Cap", "Aqu", "Pis"] idx = int(lon // 30) % 12 deg = lon % 30.0 return f"{signs[idx]} {deg:05.2f}° (abs {lon:.6f}°)" def main(): print("=" * 70) print("STEP 1: Ground truth — swisseph direct computation") print("=" * 70) results = {} for key, p in PEOPLE.items(): dt = _parse_utc_dt(p["datetime_utc"]) h = _compute_houses(dt, p["lat"], p["lon"]) results[key] = h print(f"\n{p['label']}:") print(f" UTC: {p['datetime_utc']} lat={p['lat']} lon={p['lon']}") print(f" JD: {h['jd']:.6f}") print(f" ASC: {_fmt(h['asc'])}") print(f" MC: {_fmt(h['mc'])}") print(f" DSC: {_fmt(h['dsc'])}") print(f" IC: {_fmt(h['ic'])}") print(f" Cusps: ", end="") cusp_strs = [f"H{i+1}={_fmt(c)}" for i, c in enumerate(h["cusps"])] print(" | ".join(cusp_strs)) # Print machine-readable summary for easy comparison print("\n" + "=" * 70) print("MACHINE-READABLE REFERENCE VALUES:") print("=" * 70) for key, p in PEOPLE.items(): h = results[key] print(f"{key}: ASC={h['asc']:.6f} MC={h['mc']:.6f} " f"DSC={h['dsc']:.6f} IC={h['ic']:.6f} " f"JD={h['jd']:.6f}") return results if __name__ == "__main__": main()