Files
gps/soc/software/pps_end_to_end_validation/README.md
T

12 KiB

GPS PPS end-to-end Linux validation

This package is a deterministic, causal validation of the project timing path from synthetic GPS LNAV data to the physical PPS edge. It exists separately from pps_python, whose models concentrate on WLS noise, oscillator behavior, and adaptive-loop performance.

The validation does not use Linux wall-clock scheduling. The 1 kHz, 50 bit/s, RF-sample, PCLK, and 125 MHz events advance from synthetic timestamps, making the signed absolute-time result repeatable on a normal Linux host.

What is modeled

The model follows the current firmware and RTL conventions:

  1. It generates deterministic GPS LNAV subframes with D29*/D30* data inversion and valid parity for all ten 30-bit words.
  2. Each navigation bit is expanded to twenty 1 kHz prompt-sign samples.
  3. The bit_gen.sv 40 ms 1 -> 0 transition acquisition, majority decision, 20 ms bit_valid cadence, and registered ABS_SAMPLE behavior are applied.
  4. The tracking wrapper's chronological 2 + 300 + 8 = 310 bit window, preamble search, parity check, load_sigs, and subframe interrupt ordering are applied.
  5. nav_solve_timer is initialized from HOW Z-count and counts later bit-valid events. Navigation-solve interrupt, PPS counter capture, capture sequence, and a 32-bit RF ABS_SAMPLE wrap are exercised.
  6. Firmware reconstructs periodic TOW from the HOW/sample anchor and evaluates TOW + POSTPONE_TOW + STARTOFFSET - b/c.
  7. The timestamped 125 MHz PPS peripheral performs coarse/normal mapping, ignores synthetic CPU/APB delay as phase, acquires lock after five valid updates, and generates integer-second edges.
  8. DUT edges are compared with an independent trusted PPS. Optional DUT and trusted-output delays make board, cable, and vendor delay sign explicit.

This is a digital epoch-path validation, not an RF waveform simulator. The existing pps_python tests continue to cover WLS, oscillator, PI, adaptive quality, and holdover behavior.

Run

From soc/software:

python3 -m pps_end_to_end_validation.pps_end_to_end
python3 -m unittest pps_end_to_end_validation/test_pps_end_to_end.py

The default command executes the diagnostic matrix:

scenario                 bits  correction  reference error  DUT - trusted PPS
current-rtl/180ms            9      180 ms     -0.000032 ms      +0.000032 ms
current-rtl/160ms            9      160 ms    -20.000032 ms     +20.000032 ms
current-bit/160ms            8      160 ms     -0.000032 ms      +0.000032 ms

Positive DUT - trusted PPS means the project output is late. The default 32 ns remainder is the explicitly configured four-tick PCLK capture pipeline; it is not part of the LNAV-bit mismatch.

Run one scenario or obtain machine-readable output:

python3 -m pps_end_to_end_validation.pps_end_to_end \
  --mode current-rtl --postpone-ms 160 --json

python3 -m pps_end_to_end_validation.pps_end_to_end \
  --mode current-bit \
  --dut-output-delay-ns 12 \
  --trusted-output-delay-ns 35

Closed-loop stability and dynamic tuning

The bit-level command above isolates the absolute epoch convention. The companion stability analysis connects that result to the fixed-point firmware/RTL PI model, replays the real GDOP and tracked-satellite history in fusion/logs/gps_live_5min_20260713.csv, and synthesizes only the telemetry that the repository did not capture:

python3 -m pps_end_to_end_validation.stability_analysis
python3 -m pps_end_to_end_validation.stability_analysis \
  --update-period-ms 500
python3 -m pps_end_to_end_validation.stability_analysis \
  --update-period-ms 1000
python3 -m unittest \
  pps_end_to_end_validation/test_stability_analysis.py

Generated results are in output/stability/:

  • stability_report.md: real-data inventory, discrete poles, absolute phase wander, period jitter, dynamic parameter sweep, and recommendations;
  • parameter_sweep.csv: machine-readable results for every scenario/tuning;
  • synthetic_telemetry.csv: solve-cadence ABS_SAMPLE, REM_CODE_PHASE, corrected 125 MHz capture, HDOP/VDOP/TDOP/GDOP, C/N0, pseudorange noise, clock sigma/drift, oscillator and timing-error inputs.

The older fusion capture used by stability_analysis.py contains 989 GDOP values and 990 tracked-satellite values (eight throughout), but no PPS phase/rate, ABS_SAMPLE, C/N0, TDOP/HDOP, pseudorange residual, oscillator temperature, or trusted-edge/TIC samples. The newer live-board workflow below supplies many of those PPS status fields, while ABS_SAMPLE, REM_CODE_PHASE, temperature, and trusted-edge timing remain unavailable. The stability CSV labels replacements as synthetic; it must not be read as board telemetry.

The current acquire, precision, and robust fixed-point poles are inside the unit circle at the 200, 500, and 1000 ms supported update rates, and the stress analysis observes no unlock. It also exposes an important metrics issue: cycle-to-cycle PPS period jitter can remain below 20 ns while absolute phase moves slowly through tens of nanoseconds. A physical 20 ns maximum therefore requires a common-clock time-interval-counter capture and cable/output-delay calibration; a p95 period jitter assertion or synthetic run cannot certify it.

The sweep includes the current coefficients, quieter/faster precision loops, a robust-noise guard, a larger Doppler-drift gain, and a quality-scheduled candidate. The generated report contains the current numerical comparison. Production firmware maps the proven 200 ms discrete poles to the selected NAV period and leaves the 200 ms Q2.46 constants bit-identical. The model and firmware also scale quality/lock/settling timers in wall-clock time, convert software bias/drift gains from actual elapsed time, and extend the stale-data watchdog to max(2 s, 3 * solve period). This preserves deterministic settling and damping; it cannot preserve measurement-noise statistics when fewer WLS samples are available. Production firmware uses the current-RTL-compatible 180 ms epoch correction independently of solve cadence.

A deterministic four-seed, 180 s smoke sweep with the current profiles gave:

solve period real-geometry absolute RMS worst period step over current scenarios unlocks
200 ms 11.17 ns 16 ns 0
500 ms 15.96 ns 16 ns 0
1000 ms 25.09 ns 24 ns 0

Thus 1000 ms is linearly stable but is not recommended when the stated synthetic maximum-period-jitter target is 20 ns. Use 200 ms by default, or 500 ms when CPU load requires it. A 1000 ms solve needs hardware validation and likely oscillator-temperature/frequency prediction; pole remapping alone cannot recreate the missing measurements.

Live board telemetry replay

hardware_replay.py accepts the host-timestamped UART CSV produced by the fusion capture utility. It extracts the firmware fields that actually drive or describe PPS: raw/filtered WLS clock bias, innovation, Doppler datum drift, TDOP/GDOP/HDOP/VDOP, clock sigma, C/N0, tracked/used satellites, velocity/FDE, reference SVID, fractional capture correction, RTL phase residual, rate word, integrator, lock/profile, pulse count, and commit sequence.

Capture and replay from soc/software:

python3 fusion/serial_capture.py \
  --port /dev/ttyUSB2 --baud 460800 --duration 300 \
  --output /tmp/genesys2_pps.csv

python3 -m pps_end_to_end_validation.hardware_replay \
  --input /tmp/genesys2_pps.csv \
  --output-dir pps_end_to_end_validation/output/hardware_replay

python3 -m unittest \
  pps_end_to_end_validation.test_hardware_replay

Raw UART files are ignored by Git because [LOCATION] records contain exact coordinates. The replay output is normalized to PPS fields and deliberately omits latitude/longitude.

For an already-running board, replay imports the first logged locked filter/PI snapshot. Starting the model from reset would erase the observed integrator and create a false acquisition transient. When status is decimated, commit sequence numbers expand the trace back to the inferred solve cadence; intermediate values are explicitly marked interpolated=true.

The replay infers cadence from UART host time divided by commit-sequence advance, snapped to the 20 ms navigation-bit grid. Use --update-period-ms only when host timestamps are unreliable.

The report separates three quantities that should not be conflated:

  • direct hw_phase statistics from the board;
  • fixed-point PI reproduction and a telemetry-conditioned model replay; and
  • inferred active phase obtained by integrating rate_cmd under a constant oscillator assumption.

Only the first two are directly constrained by firmware/RTL telemetry. The third is useful for diagnosing slow wander but is not a trusted-generator measurement. hw_phase itself is a 5 Hz commit residual, not the physical PPS pin offset. A calibrated, common-clock TIC/scope capture is still required for a maximum-20 ns output claim.

20 ms diagnosis

The current tracking wrapper shifts the current bit into shift_reg_n, while its preamble/parity predicate reads the preceding shift_reg_p. On the eighth following preamble bit, the validator therefore sees only the previous seven bits. The register contains all eight after the edge, but the bit-valid edge condition has ended. Validation occurs on the ninth bit, 20 ms later.

The implemented interface is consequently:

current RTL delivery       9 * 20 ms = 180 ms
production POSTPONE_TOW    9 * 20 ms = 180 ms
signed epoch mismatch                     0 ms

former firmware value      8 * 20 ms = 160 ms
former signed mismatch                   20 ms

Production firmware now uses POSTPONE_TOW=0.180 as the current-RTL-compatible correction, consistently in both satellite-state and PPS calculations. It is 180 ms, not 190 ms. If RTL is later changed to validate a candidate window containing the current eighth bit, firmware must return to 160 ms in the same coordinated release.

The validation intentionally does not modify production RTL. Firmware changes for period-aware control are exercised by the same coefficient and timing tests; the 180 ms absolute epoch convention remains unchanged.

Other regression coverage

The unit tests verify:

  • parity-valid LNAV round-trip and rejection after one corrupted data bit;
  • 1 kHz prompt-sign to 50 bit/s generation and the registered 1 ms sample lag;
  • ninth-bit delivery in current RTL and eighth-bit delivery in the candidate implementation;
  • constant signed PPS error over successive physical edges;
  • 180 ms firmware compatibility with current RTL;
  • invariance to synthetic CPU/APB stalls when capture timestamps are used;
  • DUT-versus-trusted output-delay sign; and
  • rejection behavior explaining why firmware now compile-rejects NAV_SOLVE_TIMER=40; and
  • zero NAV-solve rejection and unchanged absolute epoch at N=10/25/50 (200/500/1000 ms).

The N=40 case divides the GPS week but not a 300-bit subframe. When the model refreshes the HOW/sample anchor after six seconds, the timer phase is half a solve period away from that anchor and the current N * 20 ms snap rejects otherwise valid solves. Firmware therefore requires N to divide both the GPS week and the 300-bit subframe. Production-safe examples are N=10, 25, and 50.

If arbitrary timer periods are required later, reconstruct elapsed time on the fundamental 20 ms navigation-bit grid instead of snapping through a refreshed HOW anchor.

Hardware confirmation

Before selecting a production correction:

  1. Probe bit_valid, the final preamble bits, preamble_ok, parity result, and load_sigs with ILA.
  2. Count whether delivery is on following bit 8 or 9 in the flashed bitstream.
  3. Measure DUT and trusted PPS with a common time-interval counter and record the sign. The diagnosis predicts a DUT edge approximately 20 ms late.
  4. Apply only nanosecond-scale output/cable calibration after the epoch mismatch is removed.