Skip to content

tests: host-side Si5351 continued-fraction synthesis math harness (#133)#134

Draft
ringof wants to merge 5 commits into
mainfrom
claude/133_startadcfp
Draft

tests: host-side Si5351 continued-fraction synthesis math harness (#133)#134
ringof wants to merge 5 commits into
mainfrom
claude/133_startadcfp

Conversation

@ringof

@ringof ringof commented May 24, 2026

Copy link
Copy Markdown
Owner

Summary

Groundwork for issue #133 (continued-fraction Si5351 multisynth selection + STARTADCFP). This PR adds only a host-side, hardware-free test harness to validate the best-rational-approximation math before any firmware change is made — no firmware files are touched and no shipping behavior changes.

tests/si5351_math_test.c:

  • Ports rational_approximation() verbatim from the fventuri/DFC-transceiver reference.
  • Packs the resulting a + b/c into the real Si5351 P1/P2/P3 register fields and decodes them back, so the register packing is exercised, not just the math.
  • Faithfully reproduces the current integer algorithm from SDDC_FX3/driver/Si5351.c (si5351aSetFrequencyA) for side-by-side comparison.
  • Reconstructs the synthesized frequency and reports error in Hz/ppm.

Coverage: common ADC rates, constructed exactly-synthesizable rates, fractional/ppb targets, and a 20,000-point deterministic (fixed-seed) random sweep over 2–129.6 MHz.

tests/Makefile: adds a si5351_math_test target (links only libm) and a make check-math convenience target; .gitignore ignores the binary.

Results (current run)

  • New algorithm: 0 Hz error on all common ADC rates; current integer algorithm is off by up to ~2.86 Hz (100 MHz).
  • Exactly-synthesizable rates: exact to <1e-9 Hz.
  • Fractional targets (incl. 1 ppb offset): resolved to a few mHz — unrepresentable by the integer STARTADC today.
  • Random sweep: new solved 20000/20000, mean |err| 0.00002 ppm vs current 0.019 ppm; worst 0.212 vs 0.237 ppm. This is the "always comes up with a solution" guarantee requested in the issue thread.

Test plan

  • make -C tests check-math → expect 1..31 and # 31/31 passed, exit 0.
  • make -C tests fx3_cmd still builds (independent of the new target).
  • Reviewer sanity-checks the ported algorithm against the upstream reference.

Not in this PR (follow-ups for #133)

  • Firmware port into SDDC_FX3/driver/Si5351.c (generalize the integer-only output divider; double-precision core).
  • New STARTADCFP vendor command (8-byte IEEE-754 LE double) + STARTADC becoming a thin integer wrapper.
  • Host (fx3_cmd.c) + docs/api.md updates and fw_test.sh integration.

Open questions are tracked in the issue comment: opcode choice, out-of-range failure semantics, whether CLK2 gets the same treatment, and the confirmed supported frequency window.

https://claude.ai/code/session_018Yua5VMUvZHY7XYeyF2KaL


Generated by Claude Code

Adds a standalone, hardware-free TAP harness (tests/si5351_math_test.c)
to validate a best-rational-approximation (continued fraction) algorithm
for Si5351 multisynth register selection ahead of any firmware change
(issue #133).

The harness ports rational_approximation() from the fventuri/DFC-transceiver
reference, packs results into the real Si5351 P1/P2/P3 register fields and
decodes them back (exercising the packing, not just the math), reproduces
the current integer algorithm from SDDC_FX3/driver/Si5351.c for comparison,
and reports synthesized-frequency error in Hz/ppm. Coverage: common ADC
rates, exactly-synthesizable rates, fractional/ppb targets, and a 20,000-
point deterministic random sweep over 2-129.6 MHz.

Adds a `si5351_math_test` build target and `make check-math` to the tests
Makefile, and ignores the new binary. No firmware files touched.

https://claude.ai/code/session_018Yua5VMUvZHY7XYeyF2KaL
claude added 4 commits May 24, 2026 02:42
Companion to si5351_math_test.c (issue #133). Sweeps requested output
frequency 10 kHz-135 MHz at the 27 MHz reference and characterizes the
synthesis error of the continued-fraction algorithm against both the
current integer algorithm and a brute-force hardware optimum that
exhaustively searches every feedback denominator c in [1, 2^20-1].

Findings: the continued-fraction result is bit-identical to the
brute-force optimum across the sweep (the algorithm is optimal; residual
error is the chip's fractional-N resolution limit), ~46% of random
frequencies synthesize exactly, and the only algorithm-vs-hardware gap
(~0.4 ppm worst case) comes from the reference routine's epsilon=1e-5
early-exit on near-integer feedback ratios -- a tunable software limit,
not a hardware one.

Adds `si5351_mc` build target and `make mc-plot` (emits scatter + CDF
PNGs via gnuplot). Generated CSV/PNG/binary are gitignored and
reproducible.

https://claude.ai/code/session_018Yua5VMUvZHY7XYeyF2KaL
Committed snapshots of the Monte-Carlo scatter and CDF plots (generated
by `make -C tests mc-plot`) so they can be referenced from the issue
thread. The tests/ outputs remain gitignored/reproducible; these are
intentional documentation snapshots.

https://claude.ai/code/session_018Yua5VMUvZHY7XYeyF2KaL
Extends the harness (issue #133) to measure an exact integer/rational
continued-fraction solver with semiconvergents head-to-head against the
floating-point reference, plus a spur-aware divider search.

Adds two analyses and plots:
- feedback-level epsilon probe: drives the feedback fractional part from
  1e-8 to 1e-2 and compares float-CF vs exact-rational vs the exhaustive
  optimum. Shows the float routine's epsilon=1e-5 early-out leaves up to
  ~0.3 ppm where an exact fraction exists (denom <= 2^20-1), while the
  exact-rational solver reaches 0.
- chosen feedback denominator distribution: single divider vs spur-aware
  divider search. Integer-PLL (b==0) is ~0% for arbitrary rates on a
  27 MHz reference; spur-aware search trims the median denominator only
  modestly. The dominant spectral lever (integer even output divider) is
  already used regardless.

Generated CSV/PNG are gitignored; plot snapshots committed under
docs/assets/issue-133/ for the discussion thread.

https://claude.ai/code/session_018Yua5VMUvZHY7XYeyF2KaL
Benchmarks three solver strategies (issue #133) to quantify the cost of a
thorough divider search on the FX3: a lightweight single-divider exact
solve, a full search with the straddling output divider computed directly,
and a full search that scans D=8..2048 linearly (the ka9q-style form).

Findings: the linear D scan (~81k inner iterations/solve) is the only heavy
part and is avoidable by computing D = floor(pll_target/(fout*R)) directly,
which yields an identical solution ~20x faster. Even the unoptimized scan
estimates to ~tens of ms on the FX3 -- well under the ~2 s EP0 handler
budget and smaller than the ~100 ms PLL-lock poll already performed.

Adds a `si5351_bench` target and `make bench`; binary is gitignored.

https://claude.ai/code/session_018Yua5VMUvZHY7XYeyF2KaL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants