Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ reset_decoder_state
is_sat_healthy
```

## Positioning Readiness

Pair [`is_decoding_completed_for_positioning`](@ref) with [`is_sat_healthy`](@ref)
to gate use of a satellite in a fix: the first confirms the required navigation
data set has been decoded and validated, the second that the satellite is
broadcasting healthy. See the docstring for what it deliberately does *not*
gate on (ephemeris freshness, second-order corrections, the alert flag).

```@docs
is_decoding_completed_for_positioning
```

## Signal Metadata

`GNSSSignals.get_data_frequency` is extended for [`GNSSDecoderState`](@ref): it
Expand Down
1 change: 1 addition & 0 deletions src/GNSSDecoder.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export decode,
GalileoE1BDecoderState,
GalileoE5aDecoderState,
is_sat_healthy,
is_decoding_completed_for_positioning,
GNSSDecoderState,
reset_decoder_state
# v2 shared utilities — see issue #36. Used directly by issue #37 (Galileo
Expand Down
38 changes: 38 additions & 0 deletions src/gnss.jl
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,44 @@ function deques_equal(a::CircularDeque{T}, b::CircularDeque{T}) where {T}
return true
end

"""
$(TYPEDSIGNATURES)

Report whether a decoder has recovered the minimum navigation data a
positioning engine needs from this satellite: a time of week, a full ephemeris
(orbit) set, and the SV clock-correction polynomial (plus, on the signals that
carry it in the same required set, the broadcast week number and single-band
group delay). Dispatches on the validated [`data`](@ref GNSSDecoderState) field,
so it only becomes `true` once the required message set has passed CRC/parity
**and** the cross-subframe issue-of-data consistency check that promotes
`raw_data` to `data`.

This is the readiness gate a receiver (e.g. `PositionVelocityTime.jl`) should
pair with [`is_sat_healthy`](@ref): whenever this returns `true`, the health
field `is_sat_healthy` inspects is guaranteed to have been decoded, so the two
can be checked together without a separate `nothing` guard.

!!! note "What this deliberately does *not* gate on"

A `true` here means the *data set* is complete and self-consistent — it is a
necessary condition for using the SV in a fix, not a blanket guarantee that
no further judgement is required:

- **Ephemeris freshness.** Only presence is checked, not age. The decoder
has no notion of "now", so the consumer must still reject ephemerides
outside their fit interval (`fit_interval` / `t_oe` age).
- **Second-order corrections.** Group delay / inter-signal corrections
(`T_GD`, `ISC_*`) beyond the single required band, Klobuchar
ionosphere, and UTC parameters are intentionally excluded because they
are broadcast far less often; apply them when present and treat
`nothing` as zero rather than waiting for them.
- **Alert flag.** `is_sat_healthy` reflects the broadcast health bits
only; a receiver that wants to honour the L1 C/A alert flag (or
equivalent) must check it separately.
"""
is_decoding_completed_for_positioning(state::GNSSDecoderState) =
is_decoding_completed_for_positioning(state.data)

"Soft-symbol buffer accessor — the per-signal cache stores it as `soft_buffer`."
soft_buffer(state::GNSSDecoderState) = state.cache.soft_buffer

Expand Down
6 changes: 6 additions & 0 deletions test/gps_l2cm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ end
@test state_a.constants isa GNSSDecoder.GPSL2CMConstants
@test isnothing(state_a.num_bits_after_valid_syncro_sequence)
@test !state_a.is_shifted_by_180_degrees
# A fresh decoder has no validated data, so the exported
# state-forwarding readiness predicate is `false`.
@test !is_decoding_completed_for_positioning(state_a)
end

@testset "Shares the L5I CNAV decode path" begin
Expand Down Expand Up @@ -315,6 +318,9 @@ end
@test isnothing(d.ism)

@test GNSSDecoder.is_decoding_completed_for_positioning(d)
# Exported state-forwarding method: dispatches on the validated `data`,
# so it agrees with the `data`-typed check above.
@test is_decoding_completed_for_positioning(state)
@test is_sat_healthy(state)
end

Expand Down
Loading