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
15 changes: 15 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ code; this file is for naming and meaning only.
non-systematic convolutional code (G1 = 171₈, G2 = 133₈), convolved
*continuously* across message boundaries (no tail bits). Message = 300 bits
= 600 channel symbols at 100 sps = 6 seconds (IS-GPS-705).
- **GPS L2C** (`GPSL2CM`) — 25 bps CNAV broadcast on the L2 CM (civil-moderate)
code; the time-multiplexed L2 CL code is a dataless pilot. The CNAV message
is *bit-for-bit identical* to GPS L5I's (IS-GPS-200N §30 ≡ IS-GPS-705J
§20.3.3): same preamble, FEC, CRC-24Q, message-type layouts, π and `TOW × 6`.
Only the signal layer differs — 25 bps → 50 sps, 300-bit message = 600
symbols = 12 seconds — which is purely time-domain, so the symbol-domain
decoder is the same 600-symbol message / 616-symbol sync window. Shares the
GPS CNAV core (`src/gps/cnav.jl`) and the `GPSCNAVData` container with L5I;
the only decode difference is that `is_sat_healthy` reports the L2 health bit
(MT10 bit 53) instead of the L5 bit (54). `GNSSDecoderState(::GPSL2CM, prn)`
maps here (`GPSL2CL` is the pilot); `GPSL2CMDecoderState(prn)` is the
equivalent direct constructor.
- **Galileo E1B** (`GalileoE1B`) — 250 sps I/NAV nominal pages over a K=7
rate-1/2 convolutional code plus 30×8 block interleaver. Page = 250 channel
symbols = 1 second. Two consecutive pages (even+odd) carry one word.
Expand Down Expand Up @@ -112,3 +124,6 @@ lives in the `cache`.
domain (the FEC runs continuously across messages), so each sync attempt
Viterbi-decodes the buffered 616-symbol window and requires the preamble at
both ends of the decoded 308-bit window plus a clean CRC-24Q.
- **L2C**: identical to L5I — the same shared GPS CNAV `try_sync`
Viterbi-decodes the 616-symbol window and gates on preamble + CRC-24Q. (The
symbol rate, 50 vs 100 sps, does not enter the symbol-domain sync.)
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ DataStructures = "0.18, 0.19"
Dictionaries = "0.4"
DocStringExtensions = "0.6, 0.7, 0.8, 0.9"
Downloads = "1"
GNSSSignals = "2.3"
GNSSSignals = "2.6"
Random = "1"
Test = "1"
Tracking = "2"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ magnitudes need not be normalized.
Currently implemented:
* GPS L1 C/A (LNAV)
* GPS L1C-D (CNAV-2)
* GPS L2C (CNAV)
* GPS L5I (CNAV)
* Galileo E1B (I/NAV)
* Galileo E5a (F/NAV)
Expand Down
23 changes: 16 additions & 7 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ GalileoE1BDecoderState
GalileoE5aDecoderState
GPSL1C_DDecoderState
GPSL5IDecoderState
GPSL2CMDecoderState
```

## Decoding
Expand Down Expand Up @@ -84,14 +85,22 @@ GPSL1C_DMidiAlmanac
GPSL1C_DDifferentialCorrection
```

### GPS L5I
### GPS CNAV (shared by L5I and L2C)

GPS L5I and GPS L2C carry the identical CNAV message, so they share the decoded
[`GPSCNAVData`](@ref) container (and its almanac/correction records) and one
constants struct, [`GPSCNAVConstants`](@ref GNSSDecoder.GPSCNAVConstants). The
per-signal constants are type aliases that fix its signal tag; they differ only
in which signal-health bit [`is_sat_healthy`](@ref) reports.

```@docs
GNSSDecoder.GPSCNAVConstants
GNSSDecoder.GPSL5IConstants
GPSL5IData
GPSL5IReducedAlmanac
GPSL5IMidiAlmanac
GPSL5IClockDifferentialCorrection
GPSL5IEphemerisDifferentialCorrection
GPSL5IIntegritySupportMessage
GNSSDecoder.GPSL2CMConstants
GPSCNAVData
GPSCNAVReducedAlmanac
GPSCNAVMidiAlmanac
GPSCNAVClockDifferentialCorrection
GPSCNAVEphemerisDifferentialCorrection
GPSCNAVIntegritySupportMessage
```
27 changes: 26 additions & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ A Julia package for decoding GNSS (Global Navigation Satellite System) navigatio

- **GPS L1 C/A**: Decodes the 50 bps LNAV data stream from GPS L1 civil signals
- **GPS L1C-D**: Decodes the 100 sps CNAV-2 data stream from the modernized GPS L1C signal's data component
- **GPS L2C**: Decodes the 50 sps CNAV data stream from the GPS L2 CM (civil-moderate) signal component
- **GPS L5I**: Decodes the 100 sps CNAV data stream from the GPS L5 in-phase signal component
- **Galileo E1B**: Decodes the 250 bps I/NAV data stream from Galileo E1B Open Service signals

Expand Down Expand Up @@ -128,7 +129,31 @@ julia> state.prn
1

julia> typeof(state)
GNSSDecoderState{GPSL5IData, GNSSDecoder.GPSL5IConstants, GNSSDecoder.GPSL5ICache}
GNSSDecoderState{GPSCNAVData, GNSSDecoder.GPSCNAVConstants{:GPSL5I}, GNSSDecoder.GPSCNAVCache}

julia> state = decode(state, Float32[+1, -1, +1, +1, -1, -1, -1, -1, -1, +1], 10); # Decode 10 soft symbols

julia> GNSSDecoder.num_bits_buffered(state)
10
```

### GPS L2C Decoding

GPS L2C broadcasts the *same* CNAV message as GPS L5I (IS-GPS-200N §30), on the
L2 CM component at 50 sps. Decoding therefore reuses the shared GPS CNAV core,
and decoded fields land in the same [`GPSCNAVData`](@ref) container; only the
health check differs (it reports the L2 signal-health bit):

```jldoctest l2c_example
julia> using GNSSDecoder

julia> state = GPSL2CMDecoderState(1); # Initialize decoder for PRN 1

julia> state.prn
1

julia> typeof(state)
GNSSDecoderState{GPSCNAVData, GNSSDecoder.GPSCNAVConstants{:GPSL2CM}, GNSSDecoder.GPSCNAVCache}

julia> state = decode(state, Float32[+1, -1, +1, +1, -1, -1, -1, -1, -1, +1], 10); # Decode 10 soft symbols

Expand Down
29 changes: 19 additions & 10 deletions src/GNSSDecoder.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ export decode,
GPSL1C_DMidiAlmanac,
GPSL1C_DDifferentialCorrection,
GPSL5IDecoderState,
GPSL5IData,
GPSL5IReducedAlmanac,
GPSL5IMidiAlmanac,
GPSL5IClockDifferentialCorrection,
GPSL5IEphemerisDifferentialCorrection,
GPSL5IIntegritySupportMessage,
GPSCNAVData,
GPSCNAVReducedAlmanac,
GPSCNAVMidiAlmanac,
GPSCNAVClockDifferentialCorrection,
GPSCNAVEphemerisDifferentialCorrection,
GPSCNAVIntegritySupportMessage,
GPSL2CMDecoderState,
GalileoE1BDecoderState,
GalileoE5aDecoderState,
is_sat_healthy,
Expand Down Expand Up @@ -66,9 +67,17 @@ include("galileo/e5a.jl")
# `decode` framework hooks (`try_sync`, `decode_syncro_sequence`, …).
include("gps/l1c_d.jl")

# GPS L5I (CNAV) decoder. Included after `gps/l1ca.jl` (reuses its `UInt320`
# packed-word type) and after `gps/l1c_d.jl` (reuses the `_deque_slice` and
# `_merge_keyed` helpers), and consumes `crc24q` from the shared utilities
# above.
# Shared GPS CNAV core (the 300-bit message broadcast identically on GPS L5I,
# IS-GPS-705J §20.3, and GPS L2C, IS-GPS-200N §30): FEC, window-Viterbi sync,
# CRC-24Q, and all per-message-type parsing, plus the shared `GPSCNAVData`
# container. Consumes only shared utilities included above — `crc24q` and the
# `UInt320` / `_merge_keyed` primitives in `gnss.jl` — so it is independent of
# the sibling signal files.
include("gps/cnav.jl")

# GPS L5I and GPS L2C signal layers — each a thin wrapper over `gps/cnav.jl`
# (their own `*Constants` type, decoder-state constructor, and `is_sat_healthy`
# health-bit selection). Included after the shared core they consume.
include("gps/l5.jl")
include("gps/l2c.jl")
end
22 changes: 22 additions & 0 deletions src/gnss.jl
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,25 @@ function decode(
end
return state
end

# ---- Shared GPS decoder primitives ------------------------------------------
#
# Signal-agnostic primitives used by more than one GPS signal decoder. They
# live here (a shared file included before every signal) rather than in a
# per-signal file so that no signal decoder has to be included after another
# just to borrow them.

# Packed-word integer type shared across the GPS decoders: a GPS L1 C/A
# subframe, a GPS L1C-D subframe, and a GPS CNAV message (GPS L5I / L2C) each
# pack into a single `UInt320` — 300 data bits plus up to 8 trailing sync bits.
# `BitIntegers.@define_integers` also defines the signed companion `Int320`.
BitIntegers.@define_integers 320

"""
Insert/overwrite `value` keyed by `key` in a (possibly `nothing`) `Dictionary`, returning the updated copy.
"""
function _merge_keyed(dict::Union{Nothing,Dictionary{Int,V}}, key::Int, value::V) where {V}
out = isnothing(dict) ? Dictionary{Int,V}() : copy(dict)
set!(out, key, value)
return out
end
Loading
Loading