Goal
A complete, deterministic calibration loop from inputs → selected kernel → portable manifest. Plain ASCII, reproducible.
Helpers (utilities)wrap360(x) = x - 360*floor(x/360)wrap180(d) = ((d + 180) % 360) - 180unwrap_series(L_sid_deg) -> y (add/subtract 360 so neighbors stay continuous)central_diff(y, dt) -> v (speed; endpoints one-sided), with dt = 1 on daily gridscusp_dist_deg(x) = min( (x % 30), 30 - (x % 30) )
Event detectors (reference stubs)detect_crossings(t, y_unwrapped, step=30) -> tau_k (linear-interpolated 30° crossings)detect_stations(t, y_unwrapped) -> tau_s (minima of |v|; dedup within 2 d)pair_events(A_times, B_times, cap_days) -> pairs (nearest-neighbor within a cap)
Calibration loop (single body)
- Time anchor & sampling.
t0 = midpoint(train_start, train_stop);t = days_since(date, t0); pick a declared schedule (daily,D1D15, etc.). - Inputs.
Obtain daily sidereal anglesL_sid_degat the chosen timestamp (siderealize first if needed). - Pre-process.
y = unwrap_series(L_sid_deg);v = central_diff(y, dt=1). - Design candidates.
Choose family (fixed-norfree-n); define carrier setsΩ(e.g.,BASE,CANDIDATE). - Fit per candidate.
- fixed-n:
y_fit(t) = L_actual_unwrapped(t) - n*t;X = [1, sin(w1*t), cos(w1*t), ...] - free-n :
y_fit(t) = L_actual_unwrapped(t);X = [1, t, sin(w1*t), cos(w1*t), ...]
Solve OLS:beta_hat = argmin || y_fit - X*beta ||_2^2; computeRSS,k,N.
- Score per candidate.
BIC = k*log(N) + N*log( max(RSS/N, 1e-16) )
BuildL_hat_unwrapped(t)and central-diffv_hat.mae_v = mean( |v_hat - v_act| );MAE_deg_trainon wrapped degrees viawrap180.cusp_MAE_train = mean( |cusp(model) - cusp(actual)| )withcusp(x) = cusp_dist_deg(x).
Combined loss:loss = 1.0*MAE_deg_train + 0.3*cusp_MAE_train + 0.4*mae_v. - Gate & select.
ADMISSIBLEiff(BIC_EXTRA <= BIC_BASE - 6.0)and(loss_EXTRA < loss_BASE).
Pick the admissible model with smallest(loss, then BIC); else useBASE. - Freeze manifest.
- fixed-n: store
a0, harmonics; setn_deg_per_day = 360 / P_sid. - free-n : store
a0, learnedn_deg_per_day, harmonics.
- Report metrics (TEST slice).
Compute:MAE_deg,P90_deg,MAX_deg,misclass_rate,rasi_cross_MAE_days,cusp_dist_MAE_deg,station_date_MAE_days.
Use pairing caps: crossings±60 d, stations±90 d.
Metrics (TRAIN/TEST definitions)err_deg[i] = | wrap180( L_model[i] - L_actual[i] ) |
Aggregate: MAE_deg, P90_deg, MAX_deg, misclass_rate.rasi_cross_MAE_days via paired 30° crossings; station_date_MAE_days via paired stations; cusp_dist_MAE_deg via cusp_dist_deg.
Manifest (portable single source of truth){ "planet": "...", "family": "fixed-n|free-n", "t0": "YYYY-MM-DD", "P_sid": <days or null>, "n_deg_per_day": <float>, "omegas": { ... }, "beta": { ... }, "notes": "midpoint anchor; BIC-loss gate" }
Evaluator (runtime reminder)L_hat_deg(D) = wrap360( a0 + n_deg_per_day*t + sum_k[ c_k*sin(omega_k*t) + d_k*cos(omega_k*t) ] ) with t = days_since(D, t0).
Navigation
Back: SSM-JTK – Data & Calibration — OLS target, BIC, event-aware loss, selection (2.7)
Next: SSM-JTK – Data & Calibration — Event detectors (reference) (2.8)