A.1 CLI reference (one screen, copy-paste)
Single-shot inverse (one snapshot -> tau_hat_days, confidence)
python ssm_clock_runner.py --manifest "clock_manifest.json" --obs "clock_obs.csv" --grid_step_min 2.0 --refine brent
Streaming inverse (ticks -> tau_days per line)
python ssm_clock_stream_v2.py --manifest "clock_manifest.json" ^
--obs_stream "clock_obs_stream.csv" --dt_days 0.0416667 --grid_step_min 2.0 --refine brent
Acceptance bench (v1.1 knobs; stacked + multistart + Brent)
python ssm_clock_longbench_v2.py ^
--period_sets "1,7,29.5306,365.2422" ^
--seeds 200 ^
--noise_list "2,6" ^
--grid_step_min 2.0 ^
--alpha_kz 0.0 ^
--stack 5 --stack_dt_days 0.5 ^
--multistart_k 7 --refine brent --refine_steps 80 --bracket_mult 2
Optional: live stream tee to file (PowerShell; ensure UTF-8 or prefer CMD > )
powershell -NoProfile -Command ^
"python ssm_clock_stream_v2.py --manifest 'clock_manifest.json' --obs_stream 'clock_obs_stream.csv' --dt_days 0.0416667 --grid_step_min 2.0 --refine brent | Tee-Object -FilePath 'notes\\tau_stream.log'"
CSV formats
# Pairs (robust)
label,phi,label,phi,label,phi,label,phi
day,120.0,week,40.0,lunar,275.0,solar,15.0
# Wide (fast; columns must match manifest order)
day,week,lunar,solar
120.0,40.0,275.0,15.0
A.2 Pocket calculator (stdlib-only, ASCII)
Paste the following into a file named
ssm_clock_calc.py. It provides quick checks forT_search, minutes↔days conversion, modeled phases, residuals and energy, circular error, and tau features.
#!/usr/bin/env python3
# ssm_clock_calc.py (stdlib-only; ASCII)
import argparse, json, math, sys
EPS_INT = 1e-9
def wrap360(x):
y = x % 360.0
return (y + 360.0) if (y < 0.0) else y
def angdiff(a, b):
return ((a - b + 180.0) % 360.0) - 180.0
def gcd(a, b):
# a,b are integers
while b:
a, b = b, a % b
return abs(a)
def lcm(a, b):
return abs(a * b) // gcd(a, b) if a and b else 0
def lcm_days(periods):
# assumes all periods are exact integers (days)
vals = [int(round(p)) for p in periods]
L = vals[0]
for x in vals[1:]:
L = lcm(L, x)
return float(L)
def load_manifest(path):
with open(path, "r", encoding="utf-8") as f:
M = json.load(f)
chans = M.get("channels", [])
if not chans:
print("error: manifest has no channels", file=sys.stderr)
sys.exit(2)
return M, chans
def is_all_integers(periods):
return all(abs(p - round(p)) < EPS_INT for p in periods)
def compute_tsearch(periods):
if is_all_integers(periods):
return lcm_days(periods)
return max(periods)
def print_labels(chans):
print(",".join([c["label"] for c in chans]))
def cmd_tsearch(args):
M, chans = load_manifest(args.manifest)
periods = [float(c["period_days"]) for c in chans]
T = compute_tsearch(periods)
print(f"T_search_days={T:.10f}")
def cmd_convert(args):
if args.minutes is not None:
d = float(args.minutes) / 1440.0
print(f"days={d:.12f}")
if args.days is not None:
m = float(args.days) * 1440.0
print(f"minutes={m:.6f}")
if args.minutes is None and args.days is None:
print("error: provide --minutes or --days", file=sys.stderr); sys.exit(2)
def modeled_phase_at(ch, t_days):
b0 = float(ch["b0_deg"])
w = float(ch["w_deg_per_day"])
return wrap360(b0 + w * t_days)
def parse_phi_kv(items):
# items like ["day=120","week=40",...]
out = {}
for kv in items or []:
if "=" not in kv:
print(f"warn: ignoring '{kv}' (expected label=deg)", file=sys.stderr)
continue
k,v = kv.split("=",1)
out[k.strip()] = float(v.strip())
return out
def cmd_phase(args):
M, chans = load_manifest(args.manifest)
t = float(args.t_days)
for ch in chans:
lab = ch["label"]
phi = modeled_phase_at(ch, t)
print(f"{lab}={phi:.6f}")
def cmd_labels(args):
_, chans = load_manifest(args.manifest)
print_labels(chans)
def cmd_err(args):
M, chans = load_manifest(args.manifest)
t = float(args.t_days)
obs = {}
if args.ordered:
vals = [float(x.strip()) for x in args.ordered.split(",") if x.strip()!=""]
if len(vals) != len(chans):
print("error: --ordered count must equal #channels", file=sys.stderr); sys.exit(2)
for ch, val in zip(chans, vals):
obs[ch["label"]] = float(val)
else:
obs = parse_phi_kv(args.phi)
missing = [c["label"] for c in chans if c["label"] not in obs]
if missing:
print(f"error: missing phases for labels: {','.join(missing)}", file=sys.stderr); sys.exit(2)
E = 0.0
for ch in chans:
lab = ch["label"]
model = modeled_phase_at(ch, t)
e = angdiff(obs[lab], model)
E += e*e
print(f"err[{lab}]={e:.6f} deg")
print(f"E(t)={E:.6f}")
def cmd_cerror(args):
T = float(args.tsearch_days)
a = float(args.a_days)
b = float(args.b_days)
d = abs(a - b)
err_days = min(d, T - d)
err_min = 1440.0 * err_days
print(f"err_days={err_days:.10f}")
print(f"err_min={err_min:.4f}")
def cmd_features(args):
M, chans = load_manifest(args.manifest)
periods = [float(c["period_days"]) for c in chans]
T = compute_tsearch(periods)
tau = float(args.tau_days)
conf = float(args.confidence) if (args.confidence is not None) else 1.0
two_pi = 6.283185307179586
x = (tau % T) / T
f0 = math.sin(two_pi * x)
f1 = math.cos(two_pi * x)
print(f"tau_norm_sin={f0:.12f}")
print(f"tau_norm_cos={f1:.12f}")
print(f"confidence={conf:.6f}")
if args.per_cycle:
for ch in chans:
P = float(ch["period_days"])
xi = (tau % P) / P
si = math.sin(two_pi * xi) * (conf if args.mask_by_conf else 1.0)
ci = math.cos(two_pi * xi) * (conf if args.mask_by_conf else 1.0)
print(f"{ch['label']}_sin={si:.12f}")
print(f"{ch['label']}_cos={ci:.12f}")
def main():
ap = argparse.ArgumentParser(description="SSM-Clock pocket calculator (ASCII, stdlib-only)")
sp = ap.add_subparsers(dest="cmd", required=True)
p = sp.add_parser("tsearch"); p.add_argument("--manifest", required=True); p.set_defaults(func=cmd_tsearch)
p = sp.add_parser("convert"); p.add_argument("--minutes", type=float); p.add_argument("--days", type=float); p.set_defaults(func=cmd_convert)
p = sp.add_parser("phase"); p.add_argument("--manifest", required=True); p.add_argument("--t_days", required=True, type=float); p.set_defaults(func=cmd_phase)
p = sp.add_parser("labels"); p.add_argument("--manifest", required=True); p.set_defaults(func=cmd_labels)
p = sp.add_parser("err"); p.add_argument("--manifest", required=True); p.add_argument("--t_days", required=True, type=float)
p.add_argument("--phi", action="append"); p.add_argument("--ordered"); p.set_defaults(func=cmd_err)
p = sp.add_parser("cerror"); p.add_argument("--tsearch_days", required=True, type=float); p.add_argument("--a_days", required=True, type=float); p.add_argument("--b_days", required=True, type=float); p.set_defaults(func=cmd_cerror)
p = sp.add_parser("features");p.add_argument("--manifest", required=True); p.add_argument("--tau_days", required=True, type=float)
p.add_argument("--per_cycle", action="store_true"); p.add_argument("--mask_by_conf", action="store_true"); p.add_argument("--confidence", type=float)
p.set_defaults(func=cmd_features)
args = ap.parse_args()
args.func(args)
if __name__ == "__main__":
main()
Quick sanity checks
# Horizon from manifest
python ssm_clock_calc.py tsearch --manifest clock_manifest.json
# Convert minutes <-> days
python ssm_clock_calc.py convert --minutes 5
python ssm_clock_calc.py convert --days 0.1
# Model phases at t = 0.25 d
python ssm_clock_calc.py phase --manifest clock_manifest.json --t_days 0.25
# Residuals and E(t) with inline phases
python ssm_clock_calc.py err --manifest clock_manifest.json --t_days 0.25 --phi day=120 --phi week=40 --phi lunar=275 --phi solar=15
# Residuals and E(t) with ordered list (manifest label order)
python ssm_clock_calc.py labels --manifest clock_manifest.json
python ssm_clock_calc.py err --manifest clock_manifest.json --t_days 0.25 --ordered "120,40,275,15"
# Circular error modulo horizon
python ssm_clock_calc.py cerror --tsearch_days 29.5306 --a_days 0.1 --b_days 29.6
# Tau features (global + per-cycle embeddings)
python ssm_clock_calc.py features --manifest clock_manifest.json --tau_days 1.234 --per_cycle --confidence 0.92
Navigation
Back: SSM-Clock—Acceptance Note & Provenance Chain (7.7–7.8)
Next: Appendix B — Manifest JSON Schema