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:
- It generates deterministic GPS LNAV subframes with D29*/D30* data inversion and valid parity for all ten 30-bit words.
- Each navigation bit is expanded to twenty 1 kHz prompt-sign samples.
- The
bit_gen.sv40 ms1 -> 0transition acquisition, majority decision, 20 msbit_validcadence, and registered ABS_SAMPLE behavior are applied. - The tracking wrapper's chronological
2 + 300 + 8 = 310bit window, preamble search, parity check,load_sigs, and subframe interrupt ordering are applied. nav_solve_timeris 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.- Firmware reconstructs periodic TOW from the HOW/sample anchor and evaluates
TOW + POSTPONE_TOW + STARTOFFSET - b/c. - 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.
- 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/160ms 9 160 ms -20.000032 ms +20.000032 ms
current-bit/160ms 8 160 ms -0.000032 ms +0.000032 ms
current-rtl/180ms 9 180 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 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: 5 Hz 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 real capture 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 generated CSV labels its 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 ms update rate, 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. No production constants are changed by the analysis.
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
firmware POSTPONE_TOW 8 * 20 ms = 160 ms
signed epoch mismatch 20 ms
With the current bitstream, POSTPONE_TOW=0.180 is a firmware-only workaround
and must be used consistently by both satellite-state and PPS calculations.
It is 180 ms, not 190 ms. The preferred permanent correction is to validate a
candidate window containing the current eighth bit and retain the intended
160 ms firmware compensation.
The validation intentionally does not modify production RTL or firmware.
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 for the firmware-permitted
NAV_SOLVE_TIMER=40case.
The N=40 case divides the GPS week but not a 300-bit subframe. When firmware
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. Production N=10 divides 300 and is unaffected.
If general timer periods are required, reconstruct elapsed time on the
fundamental 20 ms navigation-bit grid. A narrower compatibility restriction is
to accept only periods for which 300 % N == 0.
Hardware confirmation
Before selecting a production correction:
- Probe
bit_valid, the final preamble bits,preamble_ok, parity result, andload_sigswith ILA. - Count whether delivery is on following bit 8 or 9 in the flashed bitstream.
- 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.
- Apply only nanosecond-scale output/cable calibration after the epoch mismatch is removed.