SSM-JTK – Locked Kernels – Tiny Reproduction Kit (4)

Goal. Anyone can reproduce daily sidereal angles from a single JSON manifest and a one-line evaluator — no runtime ephemeris, no hidden knobs.

Status. All bodies use the same evaluator. Two kernel families share one format:

  • Fixed-n (Moon, Jupiter, Saturn, Uranus, Neptune, Pluto): n_deg_per_day = 360 / P_sid
  • Free-n (Sun, Mercury, Venus, Mars): n_deg_per_day is learned once and frozen
  • Nodes (Rahu/Ketu): store Rahu; derive Ketu via L_ketu = wrap360(L_rahu + 180)

4.1 What to load (universal manifest)

Schema (plain ASCII JSON; one file per body):

{
  "planet": "Sun|Moon|Mercury|Venus|Mars|Jupiter|Saturn|Uranus|Neptune|Pluto|Rahu|Ketu",
  "t0": "YYYY-MM-DD",                 // midpoint anchor used during fit
  "P_sid": null,                      // days; present for fixed-n, null/omitted for free-n
  "n_deg_per_day": 0.000000,          // fixed-n: 360/P_sid ; free-n: learned slope
  "P_syn": null,                      // optional, informational
  "selected_model": "base|add_nE|add_3w|add_nE_3w|...",
  "omegas": {                         // rad/day; include only keys you actually use
    "w1": 0.0, "w2": 0.0, "w3": 0.0,
    "wS": 0.0, "nE": 0.0
  },
  "beta": {                           // degrees
    "a0": 0.000000,                   // intercept at t0
    "c1": 0.0, "d1": 0.0,             // for w1
    "c2": 0.0, "d2": 0.0,             // for w2
    "c3": 0.0, "d3": 0.0,             // for w3
    "cS": 0.0, "dS": 0.0,             // for wS
    "cE": 0.0, "dE": 0.0              // for nE
  },
  "notes": "train/test ranges, sampling mode, gate policy, etc."
}

  • Do not hand-edit numbers. The manifest is the single source of truth.
  • Nodes: if you store only Rahu, compute Ketu at runtime as L_ketu = wrap360(L_rahu + 180).

4.2 One-line evaluator (ASCII; identical for all bodies)

Daily timestamp convention: 05:30 IST (date-based evaluation; keep this choice consistent end-to-end)

Inputs: D := calendar_date, M := manifest

Base-linear evaluator (harmonics disabled).

t := days_since(D, M.t0)
y := M.beta.a0 + M.n_deg_per_day * t
L_hat_deg := wrap360(y)

Harmonic scaffold (general form; optional).

for each omega_k present:
  y := y + (c_k)*sin(omega_k*t) + (d_k)*cos(omega_k*t)
L_hat_deg := wrap360(y)

Utility. wrap360(x) := x - 360 * floor(x / 360)

Family note. For free-n bodies the learned slope lives in n_deg_per_day; no extra linear term is introduced at runtime.


4.3 Minimal Python (drop-in, planet-agnostic)

# plain-ASCII; Python 3.8+
import json, math, sys, datetime as dt

def wrap360(x):
    return x - 360.0 * math.floor(x / 360.0)

def days_since(date_obj, t0_date):
    # Daily evaluation at 05:30 IST by convention; date-only index is sufficient
    return (date_obj - t0_date).days

def eval_angle_deg(man, date_obj):
    t0 = dt.date.fromisoformat(man["t0"])
    t  = days_since(date_obj, t0)

    bet = man.get("beta", {})
    omg = man.get("omegas", {})

    # base-linear track
    y = float(bet.get("a0", 0.0)) + float(man["n_deg_per_day"]) * t

    # generic harmonic scaffold (keys optional)
    keymap = {
        "w1": ("c1","d1"),
        "w2": ("c2","d2"),
        "w3": ("c3","d3"),
        "wS": ("cS","dS"),
        "nE": ("cE","dE"),
    }
    for k, (ck_key, dk_key) in keymap.items():
        if k in omg:
            w  = float(omg[k])
            ck = float(bet.get(ck_key, 0.0))
            dk = float(bet.get(dk_key, 0.0))
            y += ck * math.sin(w * t) + dk * math.cos(w * t)

    return wrap360(y)

if __name__ == "__main__":
    # Usage: python eval.py manifest.json 2035-10-03
    man = json.load(open(sys.argv[1], "r", encoding="utf-8"))
    D   = dt.date.fromisoformat(sys.argv[2])
    print(f"{eval_angle_deg(man, D):.6f}")

Ketu from Rahu: after evaluating L_rahu, compute L_ketu = wrap360(L_rahu + 180).


4.4 Quick validation probes (suggested)

  • Ingress clocking: evaluate unwrapped y(t), locate linear crossings at every 30 deg, compare to your reference grid.
  • Stations: central-difference speed on unwrapped y; find local minima; optionally refine with a quadratic fit around each candidate.

4.5 Invariants (sanity)

  • No drift of n. Fixed-n bodies use n = 360 / P_sid; free-n use one learned slope frozen in the manifest.
  • Parsimony. Extras are admitted only with DeltaBIC >= 6 and lower event-aware loss.
  • Wrap discipline. Wrap for display; unwrap only for OLS and event timing.
  • Prev-day cusps. Record ingresses on the previous day after year-boundary normalization.
  • Bounded snap. When aligning event dates, use at most +-1 day to resolve a clear neighbor mismatch.
  • Node identity. Always enforce Ketu = wrap360(Rahu + 180).
  • Determinism. Outputs are a pure function of (manifest, date); no network or hidden state.

4.6 Body notes (runtime)

  • Sun / Mercury / Venus / Mars (free-n): use the stored n_deg_per_day.
  • Moon (fixed-n): fast body; prioritize cusp distance and 30 deg crossing timing.
  • Jupiter / Saturn / Uranus / Neptune / Pluto (fixed-n): slow carriers; rely on synodic plus slow-sidereal terms (enable harmonics only if they passed the gate).
  • Rahu / Ketu: maintain symmetry with L_ketu = wrap360(L_rahu + 180); you may store only Rahu’s manifest.

Navigation
Back: SSM-JTK – Results to Date – Benchmark cross-checks (observation-only; optional) (3.7)
Next: SSM-JTK – CSV-only Scorecard How-To (5)


Directory of Pages
SSM-JTK – Series index & links


Disclaimer
The contents in the Shunyaya Symbolic Mathematics Jyotish Transit Kernel (SSM-JTK) materials are research/observation material. They are not astrological advice, not a scientific ephemeris, and not operational guidance. Do not use for safety-critical, medical, legal, or financial decisions. Use at your own discretion; no warranties are provided; results depend on correct implementation and inputs.


Explore Further
https://github.com/OMPSHUN