Skip to content

fix!: consistent nav-data field naming + CNAV-2 EOP ΔUT_GPS scale fix#62

Merged
siebc merged 3 commits into
masterfrom
sc/icd-field-naming-and-eop-fix
Jun 24, 2026
Merged

fix!: consistent nav-data field naming + CNAV-2 EOP ΔUT_GPS scale fix#62
siebc merged 3 commits into
masterfrom
sc/icd-field-naming-and-eop-fix

Conversation

@giove-a

@giove-a giove-a commented Jun 24, 2026

Copy link
Copy Markdown

Summary

Resolves the field-naming inconsistencies in #60, extends them with two more found
while auditing every nav-data struct against IS-GPS-200/705/800 and the Galileo
OS SIS ICD v2.2, and fixes a CNAV-2 EOP scale-factor bug (#61) that the audit
surfaced. All names/scales are verified against the current ICDs and cross-checked
with GNSS-SDR and RTKLIB/PocketSDR.

Three self-contained commits:

  1. refactor! — unify nav-data field names across signals
  2. fix(l1c_d)! — correct CNAV-2 EOP ΔUT_GPS per IS-GPS-800J (Fixes Wrong LSB scale factor for CNAV-2 (L1C) EOP UT1-GPS difference: 2^-24 should be 2^-23 #61)
  3. test(gpsl1c) — anchor the SF3 page-2 EOP test to a Spirent L1C reference capture

1. Field-naming unification (breaking)

Struct(s) Before After Rationale
GPSL5IData, GPSL1C_DData α0…α3, β0…β3 α_0…α_3, β_0…β_3 matches ICD subscripts + the package's dominant convention (M_0, i_0); aligns with GPSL1CAData (#60)
GPSL1CAAlmanac af0, af1 a_f0, a_f1 matches the ephemeris clock fields across all signals (#60)
GPSL1CAData svhealth sv_health IS-GPS-200 labels it identically to the sibling sv_health_sf4_25/_sf5_25; de-glued
GPSL5IData, GPSL1C_DData i_0_dot i_dot the ICDs name the inclination rate IDOT (absolute, not a delta); matches LNAV/Galileo i_dot, GNSS-SDR/RTKLIB idot, and PositionVelocityTime.jl's generic data.i_dot access

2. CNAV-2 EOP ΔUT_GPS fix (breaking — value change)

The L1C-D subframe-3 page-2 EOP field was decoded as ΔUT1 (UT1−UTC) with scale
2⁻²⁴, a pre-Rev-G reading of the ICD. IS-GPS-800J Table 3.5-5 (and IS-GPS-705J
Table 20-VII) define it as ΔUT_GPS (UT1−GPS) with scale 2⁻²³ — the rate keeps
2⁻²⁵. This brings L1C-D in line with the current ICD and with the already-correct
L5 CNAV MT32 decoder.

  • GPSL1C_DData.ΔUT1/ΔUT1_dotΔUT_GPS/ΔUT_GPS_dot
  • decoded ΔUT_GPS is now 2× the previous (incorrect) value

3. Test hardening

The SF3 page-2 EOP assertions previously checked raw * <the decoder's own LSB>
tautological for scale factors, which is exactly why the 2⁻²⁴ bug went unnoticed.
They're now anchored to a Spirent GSS L1C reference capture: the raw integers
Spirent transmitted are fed in, and the assertions are Spirent's own
reference-decoder values (an independent oracle). Confirmed it now catches the bug —
reverting the decoder to 2⁻²⁴ makes the test fail.

Breaking changes / migration

SemVer-major (public field renames + one value change). Downstream field reads:

α0..α3, β0..β3   →  α_0..α_3, β_0..β_3      (GPSL5IData, GPSL1C_DData)
af0, af1         →  a_f0, a_f1              (GPSL1CAAlmanac)
svhealth         →  sv_health              (GPSL1CAData)
i_0_dot          →  i_dot                  (GPSL5IData, GPSL1C_DData)
ΔUT1, ΔUT1_dot   →  ΔUT_GPS, ΔUT_GPS_dot   (GPSL1C_DData; value now ×2)

Downstream impact checked: PositionVelocityTime.jl and GNSSReceiver.jl read
none of the renamed fields — except that PVT's sat_position.jl already expects
data.i_dot, so the i_0_dot → i_dot rename actually aligns the CNAV structs
with PVT (no PVT change needed). No consumer reads the EOP fields.

Verification

  • Full test suite: 2011/2011 pass
  • JuliaFormatter 2.8.5: clean
  • Notations verified at the glyph level against IS-GPS-200N/705J/800J and Galileo
    OS SIS ICD v2.2; cross-referenced with GNSS-SDR and RTKLIB/PocketSDR

Release note

This is breaking — Project.toml still reads 2.1.0 and needs a 3.0.0 bump
before tagging (not done in this PR yet).

Closes #60
Fixes #61

siebc and others added 3 commits June 24, 2026 15:57
Resolves #60 and extends it with two further inconsistencies found while
auditing all nav-data structs against IS-GPS-200/705/800 and the Galileo
OS SIS ICD v2.2.

Klobuchar ionospheric coefficients (#60):
- GPSL5IData, GPSL1C_DData: α0…α3/β0…β3 -> α_0…α_3/β_0…β_3

Almanac SV-clock coefficients (#60):
- GPSL1CAAlmanac: af0/af1 -> a_f0/a_f1

SV health (D):
- GPSL1CAData: svhealth -> sv_health, matching the sibling sv_health_sf4_25/
  sv_health_sf5_25 fields; IS-GPS-200 labels all of them identically "SV Health".

Rate of inclination (E):
- GPSL5IData, GPSL1C_DData: i_0_dot -> i_dot. IS-GPS-200/705/800 name this
  IDOT (an absolute rate, not a delta), matching LNAV/Galileo i_dot; also
  aligns with PositionVelocityTime.jl's generic data.i_dot access.

BREAKING CHANGE: renamed public struct fields on GPSL1CAData, GPSL1CAAlmanac,
GPSL5IData and GPSL1C_DData. Downstream code reading the old names must update.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The L1C-D subframe-3 page-2 EOP field was decoded as ΔUT1 = UT1−UTC with
scale 2⁻²⁴, reflecting a pre-Rev-G interpretation of the ICD. IS-GPS-800J
Table 3.5-5 defines the 31-bit field as ΔUT_GPS = UT1−GPS (UT1−GPST) with
scale 2⁻²³ (the rate keeps scale 2⁻²⁵). This brings L1C-D in line with the
latest ICD and with the L5 CNAV MT32 decoder, which already uses ΔUT_GPS.

- Rename struct/parser field ΔUT1/ΔUT1_dot → ΔUT_GPS/ΔUT_GPS_dot.
- Fix scale factor 2⁻²⁴ → 2⁻²³ for the value; rate unchanged.
- Update docstring/comments and the SF3 page-2 test expectation.

Fixes #61.

BREAKING CHANGE: the GPSL1C_DData fields ΔUT1/ΔUT1_dot are renamed to
ΔUT_GPS/ΔUT_GPS_dot, and the decoded ΔUT_GPS value is now twice the
previous (incorrect) value due to the 2⁻²⁴ → 2⁻²³ scale-factor fix.
Downstream code reading data.ΔUT1 must be updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SF3 page-2 EOP assertions previously checked `raw * <the decoder's own
LSB>`, which is tautological for scale factors — it cannot detect a wrong
scale (this is how the earlier 2⁻²⁴ ΔUT_GPS bug went unnoticed).

Re-anchor the EOP fields to Spirent's L1C reference capture: feed the raw
integers Spirent actually transmitted and assert the physical values its own
reference decoder reported. The expected values now come from an independent
decoder, so a scale regression fails the test. Verified: reverting the
decoder to 2⁻²⁴ makes the new ΔUT_GPS assertion fail.

ΔUT_GPS is asserted as 2× Spirent's printed Delta_UT1, since Spirent uses the
pre-Rev-G UT1−UTC scale 2⁻²⁴ while IS-GPS-800J Table 3.5-5 defines ΔUT_GPS =
UT1−GPS at 2⁻²³ (matching the L5 MT32 golden test). GGTO stays synthetic — the
capture carries no GGTO data — to retain its scale-factor coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@giove-a giove-a changed the title ICD-consistent nav-data field naming + CNAV-2 EOP ΔUT_GPS scale fix fix!: consistent nav-data field naming + CNAV-2 EOP ΔUT_GPS scale fix Jun 24, 2026
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.67%. Comparing base (b1ad4fd) to head (26b432c).

Files with missing lines Patch % Lines
src/gps/l1ca.jl 75.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master      #62   +/-   ##
=======================================
  Coverage   97.67%   97.67%           
=======================================
  Files          10       10           
  Lines        1336     1336           
=======================================
  Hits         1305     1305           
  Misses         31       31           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@siebc
siebc merged commit 4a97100 into master Jun 24, 2026
9 checks passed
@siebc
siebc deleted the sc/icd-field-naming-and-eop-fix branch June 30, 2026 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants