From 98c50502ffe00c2e4b278d35d25ec4a74b848f68 Mon Sep 17 00:00:00 2001 From: siebc <226531417+siebc@users.noreply.github.com> Date: Thu, 2 Jul 2026 20:07:31 +0000 Subject: [PATCH] fix(gpscnav): don't require T_GD for positioning readiness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `is_clock_correction_decoded` gated `is_decoding_completed_for_positioning` on T_GD, but CNAV broadcasts T_GD only in message type 30. On streams that carry the SV clock via MT31-37 without MT30, every message decodes and the ephemeris + clock polynomial complete, yet positioning never validates (and, downstream, raw_data is never promoted to data / the streaming counter never re-armed). Per IS-GPS-200 Table 30-XII (L2C) and IS-GPS-705J Table 20-XII (L5), T_GD (MT30 only) has a maximum broadcast interval of 288 s (L2C) / 144 s (L5), versus 48 s / 24 s for the clock (any of MT30-37) and the ephemeris (MT10/11) — ~6x less often. Gating a fix on T_GD can therefore stall positioning for minutes on a fully spec-compliant SV. Require only the SV clock polynomial (t_0c, a_f0/a_f1/a_f2); T_GD is a ~metre-level inter-signal correction applied downstream when present. Affects both GPS L2C and L5I (shared CNAV core). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/gps/cnav.jl | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/gps/cnav.jl b/src/gps/cnav.jl index 399593f..5f20720 100644 --- a/src/gps/cnav.jl +++ b/src/gps/cnav.jl @@ -359,6 +359,13 @@ parameter set): semi-circle quantities are converted to radians on decode # Group delay / ISC + ionosphere (message type 30, Tables 20-III / 20-IV) +These fields are carried **only** by message type 30, which is broadcast far +less often than the clock/ephemeris (max interval 288 s on L2C / 144 s on L5, +vs 48 s / 24 s — IS-GPS-200 Table 30-XII, IS-GPS-705J Table 20-XII). They may +therefore still be `nothing` even once positioning is otherwise ready: +`is_decoding_completed_for_positioning` deliberately does not wait for them, so +code that applies these corrections must handle `nothing` (treat as 0). + - `T_GD::Float64`: L1/L2 P(Y) inter-signal correction (seconds). - `ISC_L1CA,ISC_L2C,ISC_L5I5,ISC_L5Q5::Float64`: inter-signal corrections (s). - `α_0,α_1,α_2,α_3,β_0,β_1,β_2,β_3::Float64`: Klobuchar ionospheric coefficients. @@ -759,12 +766,29 @@ function is_ephemeris_decoded(data::GPSCNAVData) end function is_clock_correction_decoded(data::GPSCNAVData) - # Message types 30-37 shared clock block + message type 30 group delay + # SV clock polynomial from the shared clock block of ANY of message + # types 30-37 (`parse_clock_block`). Do NOT require T_GD here: the + # inter-signal group delay is broadcast *only* in message type 30, so + # gating positioning-readiness on it couples validation to MT30 + # specifically and stalls on CNAV streams that carry the clock via + # MT31-37 without MT30. + # + # The ICDs make T_GD structurally infrequent relative to the data needed + # for a fix. Maximum broadcast intervals (IS-GPS-200 Table 30-XII for + # L2C, IS-GPS-705J Table 20-XII for L5): + # ephemeris (MT10/11) clock (MT30-37) T_GD (MT30 only) + # L2C (IS-GPS-200) 48 s 48 s 288 s + # L5 (IS-GPS-705J) 24 s 24 s 144 s + # i.e. T_GD is guaranteed only ~6x less often than the clock on both + # signals. Requiring it would make a receiver wait up to 288 s (L2C) / + # 144 s (L5) after it already has a complete ephemeris + clock, on a + # fully spec-compliant SV. T_GD is a ~metre-level inter-signal + # correction: apply it downstream when present, but never block a fix on + # it. !isnothing(data.t_0c) && !isnothing(data.a_f0) && !isnothing(data.a_f1) && - !isnothing(data.a_f2) && - !isnothing(data.T_GD) + !isnothing(data.a_f2) end function is_decoding_completed_for_positioning(data::GPSCNAVData)