API Reference

Types

PositionVelocityTime.SatelliteStateType
SatelliteState{CP<:Real,D<:GNSSDecoderState,S<:AbstractGNSSSignal}

Combines the GNSS decoder state with code and carrier phase measurements for a single satellite.

Fields

  • decoder::GNSSDecoderState: GNSS decoder state containing decoded navigation data
  • system::AbstractGNSSSignal: GNSS system (e.g., GPSL1CA(), GalileoE1B())
  • code_phase::CP: Code phase measurement
  • carrier_doppler: Carrier Doppler frequency in Hz
  • carrier_phase::CP: Carrier phase measurement (default: 0.0)

Constructors

SatelliteState(; decoder, system, code_phase, carrier_doppler, carrier_phase=0.0)
SatelliteState(decoder, system, sat_state)

The second constructor extracts code phase, carrier Doppler, and carrier phase from a Tracking satellite state (Tracking.TrackedSat). It is provided by a package extension that is loaded automatically once Tracking is available, so Tracking is only a weak dependency of this package.

source
PositionVelocityTime.PVTSolutionType
PVTSolution

Complete Position, Velocity, and Time solution from GNSS measurements.

Fields

  • position::ECEF: User position in ECEF coordinates (meters)
  • velocity::ECEF: User velocity in ECEF coordinates (m/s)
  • course_over_ground::typeof(1.0°): Horizontal direction of travel (degrees), the azimuth of the velocity vector in the local East-North-Up frame at position — measured clockwise from true North and wrapped to [0, 360)°, following the GNSS course-over-ground (COG) convention. Derived from the velocity alone, so this is the direction of motion, not vehicle heading (which a single-antenna receiver cannot observe). The vertical (Up) velocity component is ignored. when the horizontal velocity is zero (stationary or purely vertical), where course is undefined.
  • time_correction::typeof(1.0m): Estimated receiver clock bias of the reference GNSS (meters). For a multi-GNSS solution this is the bias of reference_system; other systems' biases are time_correction + inter_system_biases[system].
  • time::Union{TAIEpoch{Float64}, Nothing}: Estimated time as a TAI epoch
  • relative_clock_drift::Float64: Relative receiver clock drift (dimensionless)
  • dop::Union{DOP, Nothing}: Dilution of precision values
  • sats::Dictionary{Tuple{Symbol, Int}, SatInfo}: Maps (signal, PRN) to satellite info — position, transmit time, and post-fit residual (see SatInfo). The signal tag (e.g. :GPSL1CA, :GalileoE1B; see get_signal_id) keeps the same PRN apart both across constellations (GPS PRN 5 vs Galileo E05) and across signals of one constellation (a satellite tracked on GPS L1 C/A and L5 yields two entries sharing a PRN). Receiver-clock grouping is by time system, not signal — see reference_system.
  • reference_system::Union{GNSSSignals.TimeSystem, Nothing}: GNSS time system (e.g. GNSSSignals.GPST(), GST()) that time and time_correction are referenced to.
  • inter_system_biases::Dict{GNSSSignals.TimeSystem, typeof(1.0m)}: For each GNSS time system other than reference_system, the offset of that system's time scale relative to the reference system's (meters) — the inter-system bias. This is a system / space-segment effect, not a receiver one (this receiver has no inter-system hardware bias): it is the GNSS system-time offset, so the Galileo entry equals −c · Δt_systems, Δt_systems = GST − GPST (the GGTO). It is estimated directly from the geometry whenever observable (no broadcast error); the broadcast GGTO is used to derive it only as a fallback (the GGTO-aided collapse), as that broadcast value may be erroneous. Reference-independent (the difference of two entries is the offset between those two systems); empty for a single-system solution.
  • inter_frequency_biases::Dict{Symbol, InterFrequencyBias}: For each frequency band other than the reference band, the receiver inter-frequency bias relative to it — the differential hardware delay of that band's RF chain, estimated as an extra unknown when satellites are processed on more than one band. The key is the band (e.g. :L1, :L5; see get_band_id) and the value is shared across all constellations on that band. Each InterFrequencyBias carries both the bias (in metres) and the reference band it is measured against — the anchor whose delay is folded into the clock biases — so each reported bias's anchor is explicit rather than implicit. The reference is chosen per coverage component (see band_ifb_layout), so a disconnected constellation can in principle yield several references. Empty for a single-band solution. A solution then needs n ≥ 3 + M + B satellites for M time systems and B extra bands.
source
PositionVelocityTime.SatInfoType
SatInfo

Per-satellite information attached to a PVTSolution (one entry per satellite used in the fix).

Fields

  • position::ECEF: Satellite ECEF position at transmit time (metres).
  • time::Float64: Satellite transmit time (system time of week, seconds).
  • residual::typeof(1.0m): Post-fit least-squares pseudorange residual (metres) — the modeled minus the (atmosphere-corrected) measured pseudorange. A per-satellite fit-quality / outlier indicator.
source
PositionVelocityTime.InterFrequencyBiasType
InterFrequencyBias

A receiver inter-frequency bias attached to a PVTSolution: the differential hardware delay of one frequency band's RF chain, together with the band it is measured against.

Fields

  • value::typeof(1.0m): The bias (metres), relative to reference — how much longer this band's receiver chain delay is than the reference band's.
  • reference::Symbol: The reference band (e.g. :L1; see get_band_id) whose delay is folded into the clock biases and against which value is measured. Chosen per coverage component (see band_ifb_layout), so different biases in a disconnected solution can in principle carry different references.
source

PVT Computation

PositionVelocityTime.calc_pvtFunction
calc_pvt(states::AbstractVector{<:SatelliteState},
         prev_pvt::PVTSolution = PVTSolution();
         approximate_year::Integer = year(now(UTC)),
         enable_ionospheric_correction::Bool = true,
         enable_tropospheric_correction::Bool = true) -> PVTSolution

Calculate Position, Velocity, and Time (PVT) from GNSS satellite measurements.

Satellites from different GNSS and frequency bands may be combined. Because each constellation references its broadcasts to its own system time, one receiver clock bias is estimated per GNSS time system; and because the receiver's RF chain delays each band differently, one receiver inter-frequency bias is estimated per frequency band beyond a reference band (shared across constellations on that band; see get_band_id). The state vector is therefore [x, y, z, tc₁, …, tc_M, ifb₁, …, ifb_B] for M distinct time systems and B extra bands. Position and time are found by least squares; velocity and clock drift are solved from carrier Doppler.

A solution requires n ≥ 3 + M + B healthy satellites (each system needs at least one satellite, and a system contributing a single satellite spends it entirely on that system's clock bias). When that condition fails but both GPS and Galileo are present and the Galileo message carries the GGTO (Galileo–GPS Time Offset), the Galileo clock bias is collapsed onto GPS using the broadcast offset, which makes a 4-satellite GPS+Galileo fix possible. Estimating an independent bias is preferred whenever the geometry allows it, since it avoids the GGTO's own broadcast error.

Unless disabled via enable_ionospheric_correction, the ionospheric delay is corrected automatically using only the coefficients decoded from the navigation messages. A single model is chosen for the whole solve and applied to every satellite: NTCM-G if Galileo Effective Ionisation Level coefficients have been decoded (the more accurate model), otherwise the Klobuchar model if GPS L1 α/β have been decoded, otherwise no correction. See select_ionospheric_correction and ionospheric_delay.

Unless disabled via enable_tropospheric_correction, the tropospheric delay is corrected with the blind Saastamoinen model (no broadcast coefficients needed). See tropospheric_delay.

Arguments

  • states: Vector of SatelliteState for observed satellites. Each (signal, PRN) pair must appear at most once — a receiver produces one measurement per signal per satellite, and a duplicate would enter the least-squares solve twice.
  • prev_pvt: Previous PVT solution used as initial guess (default: origin)

Keyword Arguments

  • approximate_year: Calendar year of the observation, used to resolve the GPS L1 C/A 1024-week rollover ambiguity (legacy LNAV broadcasts only a 10-bit week number, so the receiver needs external information to determine which 1024-week cycle the recording is in). Anything within ±9 years of the actual observation date works. Defaults to the current UTC year, which is correct for live signals; for processing archived recordings, pass the rough year of the recording.
  • enable_ionospheric_correction: when true (default), apply the automatic ionospheric correction described above. Set to false to skip it entirely and solve from the raw pseudoranges (e.g. for diagnostics or when an external correction is applied elsewhere).
  • enable_tropospheric_correction: when true (default), apply the Saastamoinen tropospheric correction. Set to false to skip it.

Returns

A PVTSolution containing position, velocity, time, DOP values, and satellite information. Returns prev_pvt if too few healthy satellites are available to solve the constellation (including the GGTO fallback) or if the GDOP is negative.

Throws

  • ArgumentError: If fewer than 4 satellite states are provided
source

Position and Velocity

PositionVelocityTime.get_LLAFunction
get_LLA(pvt::PVTSolution) -> LLA

Convert the ECEF position in the PVT solution to geodetic coordinates (latitude, longitude, altitude) using the WGS84 ellipsoid.

source
PositionVelocityTime.calc_satellite_positionFunction
calc_satellite_position(decoder::GNSSDecoder.GNSSDecoderState, t)
calc_satellite_position(state::SatelliteState)

Calculate the satellite ECEF position from orbital parameters at time t.

The first method takes a decoder state and explicit time. The second method computes the corrected transmission time from the SatelliteState automatically.

Arguments

  • decoder: GNSS decoder state containing ephemeris data
  • t: Transmission time in system time (seconds)
  • state: A SatelliteState combining decoder, system, and phase measurements

Returns

An SVector{3, Float64} with the satellite position in ECEF coordinates (meters).

source
PositionVelocityTime.calc_satellite_position_and_velocityFunction
calc_satellite_position_and_velocity(decoder::GNSSDecoder.GNSSDecoderState, t)
calc_satellite_position_and_velocity(state::SatelliteState)

Calculate the satellite ECEF position and velocity from orbital parameters at time t.

Uses Keplerian orbital mechanics with perturbation corrections (harmonic corrections for argument of latitude, radius, and inclination) to propagate the satellite ephemeris.

Arguments

  • decoder: GNSS decoder state containing ephemeris data
  • t: Transmission time in system time (seconds)
  • state: A SatelliteState combining decoder, system, and phase measurements

Returns

A named tuple (position, velocity) where each is an SVector{3, Float64} in ECEF coordinates (meters and m/s respectively).

source
PositionVelocityTime.get_sat_enuFunction
get_sat_enu(user_pos_ecef::ECEF, sat_pos_ecef::ECEF) -> Spherical
get_sat_enu(enu_from_ecef::ENUfromECEF, sat_pos_ecef::ECEF) -> Spherical

Convert satellite position to East-North-Up (ENU) spherical coordinates (azimuth and elevation) relative to the user position.

Arguments

  • user_pos_ecef::ECEF: User position in ECEF coordinates
  • enu_from_ecef::ENUfromECEF: A precomputed ENUfromECEF(user_pos_ecef, wgs84) transform. Pass this form to reuse one transform across all satellites that share a user position (one geodetic conversion per epoch instead of per satellite).
  • sat_pos_ecef::ECEF: Satellite position in ECEF coordinates

Returns

Spherical coordinates containing azimuth and elevation of the satellite as seen from the user position.

source

Atmospheric Corrections

These corrections are applied automatically by calc_pvt; they are documented here for reference and for diagnostic use.

PositionVelocityTime.select_ionospheric_correctionFunction
select_ionospheric_correction(states) -> Union{KlobucharParams,NTCMGParams,Nothing}

Scan all (healthy) satellite decoders and pick the single ionospheric correction to apply to the whole solve. NTCM-G is preferred whenever Galileo coefficients are available (it is the more accurate model); otherwise Klobuchar is used if GPS coefficients are available; otherwise nothing (no correction). The coefficients are global to a constellation, so the first decoder that carries each set is used.

source
PositionVelocityTime.ionospheric_delayFunction
ionospheric_delay(correction, system, elevation, azimuth, lla, time_of_week) -> Float64

Slant ionospheric group delay in metres for one satellite (system is the satellite's GNSS, used for its carrier frequency), using the constellation-wide correction returned by select_ionospheric_correction:

  • ::Nothing0.0 (no coefficients were decoded).
  • KlobucharParams → Klobuchar model (IS-GPS-200, Fig. 20-4).
  • NTCMGParams → NTCM-G model.

The line of sight is given by the satellite elevation/azimuth (radians) and the user geodetic position lla (a Geodesy.LLA); time_of_week is the measurement's system time of week in seconds (GPS time for GPS/Klobuchar, GST for Galileo/NTCM-G — they coincide to within the sub-microsecond GGTO, immaterial here). The geometry is taken precomputed — and shared across satellites and with tropospheric_delay — so a whole-epoch correction does the user geodetic conversion only once. Derive the geometry from ECEF with LLAfromECEF(wgs84)(user) and _elevation_azimuth(ENUfromECEF(user, wgs84), sat).

source
PositionVelocityTime.tropospheric_delayFunction
tropospheric_delay(elevation, lla; humidity = 0.7) -> Float64

Slant tropospheric group delay in metres for a satellite at elevation (radians) seen from the user geodetic position lla (a Geodesy.LLA), using the Saastamoinen model driven by a standard atmosphere. The delay is non-dispersive (frequency independent). humidity is the relative humidity (0…1) used for the wet component. Returns 0.0 when the satellite is at or below the horizon or the user height is outside the model's valid range (−100 m … 10 km).

The geometry is taken precomputed so a whole-epoch correction shares the user geodetic conversion and the elevation with ionospheric_delay.

source
PositionVelocityTime.KlobucharParamsType
KlobucharParams(α_0, α_1, α_2, α_3, β_0, β_1, β_2, β_3)

The eight Klobuchar ionospheric coefficients decoded from a GPS L1 navigation message, in IS-GPS-200 SI units (seconds and seconds·semicircle⁻ⁿ). The field names mirror GNSSDecoder.GPSL1CAData (α_0…α_3, β_0…β_3).

source
PositionVelocityTime.NTCMGParamsType
NTCMGParams(a_i0, a_i1, a_i2, week_number::Integer)

The broadcast Galileo Effective Ionisation Level coefficients a_i0/a_i1/a_i2 (sfu, sfu/deg, sfu/deg²) decoded from an E1B navigation message, together with the GST week number needed to derive the day of year and universal time for NTCM-G.

source
PositionVelocityTime._elevation_azimuthFunction
_elevation_azimuth(enu_from_ecef::ENUfromECEF, sat_position) -> (elevation, azimuth)

Elevation and azimuth (radians) of sat_position (ECEF) in the local East-North-Up frame defined by enu_from_ecef = ENUfromECEF(user_position, wgs84). Azimuth is measured clockwise from North. The transform is taken precomputed so it can be built once per user position and reused across satellites.

source

Dilution of Precision

The DOP values are read from the dop field of a PVTSolution, e.g. pvt.dop.GDOP.

PositionVelocityTime.DOPType
DOP

Dilution of Precision (DOP) values describing the geometric quality of the satellite constellation used for a PVT solution.

Fields

  • GDOP::Float64: Geometric DOP (overall quality)
  • PDOP::Float64: Position DOP (3D position quality)
  • VDOP::Float64: Vertical DOP
  • HDOP::Float64: Horizontal DOP
  • TDOP::Float64: Time DOP
source

Utilities

PositionVelocityTime.get_sat_infoFunction
get_sat_info(pvt_solution::PVTSolution, signal::Symbol, prn::Integer) -> Union{SatInfo,Nothing}

Return the SatInfo (position, transmit time and post-fit residual) of the satellite with the given prn on GNSS signal (e.g. :GPSL1CA, :GalileoE1B; see get_signal_id), or nothing if that satellite was not used in the fix. The signal tag is required because the same PRN can belong to different constellations or be tracked on several signals; see the sats field of PVTSolution.

source

Multi-GNSS Classification

When calc_pvt combines constellations and bands it classifies each satellite by three keys, all provided by GNSSSignals (3.3+) and read from a satellite's ranging signal: GNSSSignals.get_time_system (a GNSSSignals.TimeSystem, i.e. GPST()/GST()) drives receiver-clock grouping (one bias per time system); GNSSSignals.get_band_id (e.g. :L1, :L5) drives inter-frequency-bias grouping (one bias per band); and GNSSSignals.get_signal_id (e.g. :GPSL1CA) is the per-signal identity used in the sats key of PVTSolution. This package forwards each of the latter two to a SatelliteState.

The receiver inter-frequency biases and their reference bands (reported per InterFrequencyBias) are laid out from the constellation × band coverage graph:

PositionVelocityTime.band_ifb_layoutFunction
band_ifb_layout(systems, bands) -> (ifb_indices, extra_bands, reference_bands, num_components)

Lay out the receiver inter-frequency biases from the (constellation × band) coverage graph. Two bands share a coverage component iff some constellation is tracked on both (directly or through a chain of shared constellations); within a component the clock/IFB split has a single gauge freedom, so one reference band per component (the most-populated, ties by first appearance) is fixed and an IFB column is created only for the other bands of that component. This estimates exactly the observable IFBs — a band that is the sole band of its component (its constellation lives only there) gets none, its delay folding into that constellation's clock — so the resulting design matrix is always full rank.

ifb_indices[j] is satellite j's IFB column (1…length(extra_bands)), or 0 for a per-component reference band; extra_bands[i] is the band of IFB column i (ordered deterministically) and reference_bands[i] is the reference band of that column's coverage component (the anchor its IFB is measured against); num_components is the number of coverage components (1 ⇔ the graph is connected).

source