kshana 0.25.0

Open, reproducible PNT-resilience simulator with quantum-sensor performance models
Documentation
#!/usr/bin/env python3
"""Generate src/fes2004_data.rs from tools/fes2004_8const.dat (reproducible, bit-for-bit).

Each FES2004 row carries a Doodson number (e.g. 255.555). The six Doodson digits encode the
multipliers of the fundamental arguments (tau, s, h, p, N', ps) with a +5 offset on digits 2..6.
This script converts each Doodson number to its integer multiplier vector at generation time, so
the Rust runtime only has to dot the (small) multiplier vector with the live arguments.

Usage: python3 tools/gen_fes2004.py tools/fes2004_8const.dat src/fes2004_data.rs
"""
import sys


def doodson_to_mult(dood: str):
    """255.555 -> [tau, s-5, h-5, p-5, N'-5, ps-5] integer multipliers."""
    left, right = dood.split(".")
    digits = left.zfill(3) + right
    assert len(digits) == 6 and digits.isdigit(), f"non-standard Doodson number {dood!r}"
    d = [int(ch) for ch in digits]
    return [d[0], d[1] - 5, d[2] - 5, d[3] - 5, d[4] - 5, d[5] - 5]


def main(src: str, out: str) -> None:
    rows = []
    for line in open(src):
        line = line.rstrip("\n")
        if not line.strip() or line.lstrip().startswith("#"):
            continue
        f = line.split()
        dood, darw, n, m = f[0], f[1], int(f[2]), int(f[3])
        cp, sp, cm, sm = f[4], f[5], f[6], f[7]  # keep raw tokens -> bit-for-bit f64 literals
        mult = doodson_to_mult(dood)
        rows.append((darw, dood, mult, n, m, cp, sp, cm, sm))

    with open(out, "w") as o:
        o.write("// SPDX-License-Identifier: AGPL-3.0-only\n")
        o.write("// @generated by tools/gen_fes2004.py from tools/fes2004_8const.dat — do not edit by hand.\n")
        o.write("// FES2004 ocean-tide geopotential coefficients (IERS Conventions 2010, Ch.6), the 8\n")
        o.write("// dominant tidal constituents (M2 S2 N2 K2 K1 O1 P1 Q1) truncated to degree n<=4.\n")
        o.write("// Coefficients are in units of 1e-11. Each entry:\n")
        o.write("//   (Doodson multipliers [tau, s, h, p, N', ps], n, m, DelC+, DelS+, DelC-, DelS-).\n")
        o.write("\n")
        o.write("/// One FES2004 ocean-tide row: (Doodson multipliers, n, m, DelC+, DelS+, DelC-, DelS-).\n")
        o.write("pub type OceanTideRow = ([i8; 6], u8, u8, f64, f64, f64, f64);\n")
        o.write("\n")
        o.write("#[rustfmt::skip]\n")
        o.write("pub static FES2004: &[OceanTideRow] = &[\n")
        for (darw, dood, mult, n, m, cp, sp, cm, sm) in rows:
            mv = ",".join(str(x) for x in mult)
            o.write(f"    ([{mv}], {n}, {m}, {cp}, {sp}, {cm}, {sm}), // {darw} {dood}\n")
        o.write("];\n")
    print(f"wrote {out}: {len(rows)} constituent-coefficient rows")


if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2])