016a: GPS L2C + L5 Tier-1 generators (Galois CM/CL, XA/XBI I/Q pair, NH codes) #19

Merged
ykp merged 2 commits from feat/016a-gps-l2c-l5 into main 2026-09-23 15:37:31 +02:00
Owner

First increment of backlog step 016 (docs/backlog/016-tier2-signals.md), split into
016a/b/c in the overview (docs-only recovery + split commit landed on main earlier).

Data provenance

  • L2C: IS-GPS-200N Table 3-IIa (fetched PDF) - CM/CL initial states for SV 1-37,
    baked into codes.py; generator polynomial 1112225171 (octal, degree 27) decoded
    to the recurrence c-set {0,3,4,5,6,9,11,13,16,19,21,24}
  • L5: IS-GPS-705 conventions cross-checked with gnss-sdr's validated replicas
    (XA/XBI shift functions transcribed); NH(10)/NH(20) secondary codes from the
    gnss-sdr GPS_L5.h (IS-GPS-705 values)

src/gengnss/core/codes.py

  • gps_l2c_code(prn): 27-bit Galois LFSR (uint32, mask 0445112474 octal), CM 10230
    chips / CL 767250 chips from the ICD initial states, chip-by-chip TDM into the
    1,534,500-chip 1.5 s composite
  • gps_l5_code(prn, 'I'|'Q'): XA (truncated 13-stage m-sequence, period 8190 with
    the documented truncation reset) XOR XB rotated by the per-PRN initial phase
    offset (IS-GPS-705 Table 3-V)

generator.py: QPSK I/Q pair support - L5 synthesizes I (I5 x NH10) and Q
(Q5 x NH20) in quadrature with the NH on the absolute 1 ms epoch grid; plan gains
quad_code/data_mode; chip-domain code phase unchanged

validation/harness.py: L2C (composite) and L5 (I-code + NH on the absolute
epoch grid) added to the self-test coverage

Registry: gps_l2c (composite code length 1,534,500 / 1.5 s) and gps_l5 flipped
to Tier 1; tests updated accordingly

Tests (15 new): L2C Galois-vs-gnss-sdr transcription equality, composite TDM
structure, ICD init spot checks; L5 XA period 8190, I/Q code difference, init
spot checks; generator L5 quadrature recovery (100% agreement per epoch incl.
NH), L2C recovery with the wrapped delay phase; registry assertions.

Note: the ICD's L2C 'End Shift Register State' column does not reproduce under
any tested convention (forward/reciprocal/bit-reversed Galois) - the codes follow
the gnss-sdr-validated convention instead and match the ICD INITIAL states
exactly; documented in codes.py.

Local verification: pytest 151 passed + 1 gui skip, ruff clean (Python 3.14).

Note (process): NOT to be merged by automation - awaiting manual review/merge.
Next: 016b (L1C + B1C/B2a Weil codes) or the GUI/e2e polish - maintainer's call.

First increment of backlog step 016 (docs/backlog/016-tier2-signals.md), split into 016a/b/c in the overview (docs-only recovery + split commit landed on main earlier). **Data provenance** - L2C: IS-GPS-200N Table 3-IIa (fetched PDF) - CM/CL initial states for SV 1-37, baked into codes.py; generator polynomial 1112225171 (octal, degree 27) decoded to the recurrence c-set {0,3,4,5,6,9,11,13,16,19,21,24} - L5: IS-GPS-705 conventions cross-checked with gnss-sdr's validated replicas (XA/XBI shift functions transcribed); NH(10)/NH(20) secondary codes from the gnss-sdr GPS_L5.h (IS-GPS-705 values) **`src/gengnss/core/codes.py`** - `gps_l2c_code(prn)`: 27-bit Galois LFSR (uint32, mask 0445112474 octal), CM 10230 chips / CL 767250 chips from the ICD initial states, chip-by-chip TDM into the 1,534,500-chip 1.5 s composite - `gps_l5_code(prn, 'I'|'Q')`: XA (truncated 13-stage m-sequence, period 8190 with the documented truncation reset) XOR XB rotated by the per-PRN initial phase offset (IS-GPS-705 Table 3-V) **`generator.py`**: QPSK I/Q pair support - L5 synthesizes I (I5 x NH10) and Q (Q5 x NH20) in quadrature with the NH on the absolute 1 ms epoch grid; plan gains quad_code/data_mode; chip-domain code phase unchanged **`validation/harness.py`**: L2C (composite) and L5 (I-code + NH on the absolute epoch grid) added to the self-test coverage **Registry**: gps_l2c (composite code length 1,534,500 / 1.5 s) and gps_l5 flipped to Tier 1; tests updated accordingly **Tests** (15 new): L2C Galois-vs-gnss-sdr transcription equality, composite TDM structure, ICD init spot checks; L5 XA period 8190, I/Q code difference, init spot checks; generator L5 quadrature recovery (100% agreement per epoch incl. NH), L2C recovery with the wrapped delay phase; registry assertions. **Note**: the ICD's L2C 'End Shift Register State' column does not reproduce under any tested convention (forward/reciprocal/bit-reversed Galois) - the codes follow the gnss-sdr-validated convention instead and match the ICD INITIAL states exactly; documented in codes.py. Local verification: pytest 151 passed + 1 gui skip, ruff clean (Python 3.14). Note (process): NOT to be merged by automation - awaiting manual review/merge. Next: 016b (L1C + B1C/B2a Weil codes) or the GUI/e2e polish - maintainer's call.
016a(core): GPS L2C + L5 Tier-1 generators — Galois CM/CL, XA/XBI I/Q pair, NH codes, harness extension
All checks were successful
ci / test (3.12) (push) Successful in 1m7s
ci / test (3.14) (push) Successful in 1m24s
ci / test (3.12) (pull_request) Successful in 7m27s
ci / test (3.14) (pull_request) Successful in 7m9s
bea567ee3e
ykp merged commit c2ffafaf1c into main 2026-09-23 15:37:31 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
ykp/gengnss!19
No description provided.