SSM-JTK – Data & Calibration — Public verification snippets (CSV-only) (2.12)

Scope
Lightweight, copy-paste checks you can run locally using only CSV files (no downloads from this blog). Validates coverage, headers, and the node identity Ketu(t) = wrap360( Rahu(t) + 180 ).

CSV layout (expected headers)
"planet","date","lon_sidereal_lahiri_deg"

A) Python 3 (portable; save as public_check.py)

import csv, sys
from collections import defaultdict

def wrap360(x): 
    return ((x % 360.0) + 360.0) % 360.0

def coverage(path):
    rows = defaultdict(list)
    with open(path, newline='') as f:
        rdr = csv.DictReader(f)
        exp = ["planet","date","lon_sidereal_lahiri_deg"]
        assert rdr.fieldnames == exp, f"header_mismatch: {rdr.fieldnames} != {exp}"
        for r in rdr:
            p = r["planet"].strip().lower()
            rows[p].append(r["date"])
    for p, ds in sorted(rows.items()):
        ds = sorted(ds)
        print(f"{p:8s} rows={len(ds)} first={ds[0]} last={ds[-1]}")

def nodes_relation(path):
    bydate = defaultdict(dict)
    with open(path, newline='') as f:
        rdr = csv.DictReader(f)
        for r in rdr:
            p = r["planet"].strip().lower()
            if p not in ("rahu","ketu"): 
                continue
            bydate[r["date"]][p] = float(r["lon_sidereal_lahiri_deg"])
    mism = 0
    for d, mp in bydate.items():
        if "rahu" in mp and "ketu" in mp:
            exp = wrap360(mp["rahu"] + 180.0)
            diff = abs(wrap360(mp["ketu"] - exp))
            if diff > 1e-6:
                mism += 1
    print("nodes_mismatch_count=", mism)

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("usage: python public_check.py <planets_csv> <nodes_csv>")
        sys.exit(2)
    planets_csv, nodes_csv = sys.argv[1], sys.argv[2]
    print("coverage (planets):"); coverage(planets_csv)
    print("\nnodes relation:"); nodes_relation(nodes_csv)

B) Windows PowerShell (nodes relation check, Rahu ↔ Ketu)

$wrap360 = { param($x) ((($x % 360) + 360) % 360) }
$nodes = Import-Csv "<nodes.csv>"
$g = $nodes | Where-Object { $_.planet -in @('rahu','ketu') } | Group-Object date
$mism=0
foreach($grp in $g){
  $rah = $grp.Group | Where-Object planet -eq 'rahu' | Select-Object -First 1
  $ket = $grp.Group | Where-Object planet -eq 'ketu' | Select-Object -First 1
  if($rah -and $ket){
    $exp  = & $wrap360 ([double]$rah.lon_sidereal_lahiri_deg + 180.0)
    $diff = [math]::Abs( & $wrap360 ( [double]$ket.lon_sidereal_lahiri_deg - $exp ) )
    if($diff -gt 1e-6){ $mism++ }
  }
}
"nodes_mismatch_count=$mism"

Tips

  • Keep frame/time conventions consistent (sidereal Lahiri; one fixed daily timestamp).
  • Use case-insensitive planet names and strict headers.
  • For daily CSVs, angles should change smoothly day-to-day; large jumps imply wrap issues.
  • Treat undefined aggregates as NaN rather than 0.

Navigation
Back: SSM-JTK – Data & Calibration — Integrity & invariants (2.11)
Next: SSM-JTK – Results to Date — Public-golden CSV checks (3.1)