API Reference
Types
PositionVelocityTime.SatelliteState — Type
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 datasystem::AbstractGNSSSignal: GNSS system (e.g.,GPSL1CA(),GalileoE1B())code_phase::CP: Code phase measurementcarrier_doppler: Carrier Doppler frequency in Hzcarrier_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.
PositionVelocityTime.PVTSolution — Type
PVTSolutionComplete 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 atposition— 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.0°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 ofreference_system; other systems' biases aretime_correction + inter_system_biases[system].time::Union{TAIEpoch{Float64}, Nothing}: Estimated time as a TAI epochrelative_clock_drift::Float64: Relative receiver clock drift (dimensionless)dop::Union{DOP, Nothing}: Dilution of precision valuessats::Dictionary{Tuple{Symbol, Int}, SatInfo}: Maps(signal, PRN)to satellite info — position, transmit time, and post-fit residual (seeSatInfo). The signal tag (e.g.:GPSL1CA,:GalileoE1B; seeget_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 — seereference_system.reference_system::Union{GNSSSignals.TimeSystem, Nothing}: GNSS time system (e.g.GNSSSignals.GPST(),GST()) thattimeandtime_correctionare referenced to.inter_system_biases::Dict{GNSSSignals.TimeSystem, typeof(1.0m)}: For each GNSS time system other thanreference_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; seeget_band_id) and the value is shared across all constellations on that band. EachInterFrequencyBiascarries 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 (seeband_ifb_layout), so a disconnected constellation can in principle yield several references. Empty for a single-band solution. A solution then needsn ≥ 3 + M + Bsatellites forMtime systems andBextra bands.
PositionVelocityTime.SatInfo — Type
SatInfoPer-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.
PositionVelocityTime.InterFrequencyBias — Type
InterFrequencyBiasA 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 toreference— how much longer this band's receiver chain delay is than the reference band's.reference::Symbol: The reference band (e.g.:L1; seeget_band_id) whose delay is folded into the clock biases and against whichvalueis measured. Chosen per coverage component (seeband_ifb_layout), so different biases in a disconnected solution can in principle carry different references.
PVT Computation
PositionVelocityTime.calc_pvt — Function
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) -> PVTSolutionCalculate 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 ofSatelliteStatefor 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: whentrue(default), apply the automatic ionospheric correction described above. Set tofalseto skip it entirely and solve from the raw pseudoranges (e.g. for diagnostics or when an external correction is applied elsewhere).enable_tropospheric_correction: whentrue(default), apply the Saastamoinen tropospheric correction. Set tofalseto 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
Position and Velocity
PositionVelocityTime.get_LLA — Function
get_LLA(pvt::PVTSolution) -> LLAConvert the ECEF position in the PVT solution to geodetic coordinates (latitude, longitude, altitude) using the WGS84 ellipsoid.
PositionVelocityTime.calc_satellite_position — Function
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 datat: Transmission time in system time (seconds)state: ASatelliteStatecombining decoder, system, and phase measurements
Returns
An SVector{3, Float64} with the satellite position in ECEF coordinates (meters).
PositionVelocityTime.calc_satellite_position_and_velocity — Function
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 datat: Transmission time in system time (seconds)state: ASatelliteStatecombining 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).
PositionVelocityTime.get_sat_enu — Function
get_sat_enu(user_pos_ecef::ECEF, sat_pos_ecef::ECEF) -> Spherical
get_sat_enu(enu_from_ecef::ENUfromECEF, sat_pos_ecef::ECEF) -> SphericalConvert 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 coordinatesenu_from_ecef::ENUfromECEF: A precomputedENUfromECEF(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.
Atmospheric Corrections
These corrections are applied automatically by calc_pvt; they are documented here for reference and for diagnostic use.
PositionVelocityTime.select_ionospheric_correction — Function
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.
PositionVelocityTime.ionospheric_delay — Function
ionospheric_delay(correction, system, elevation, azimuth, lla, time_of_week) -> Float64Slant 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:
::Nothing→0.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).
PositionVelocityTime.tropospheric_delay — Function
tropospheric_delay(elevation, lla; humidity = 0.7) -> Float64Slant 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.
PositionVelocityTime.KlobucharParams — Type
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).
PositionVelocityTime.NTCMGParams — Type
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.
PositionVelocityTime._elevation_azimuth — Function
_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.
Dilution of Precision
The DOP values are read from the dop field of a PVTSolution, e.g. pvt.dop.GDOP.
PositionVelocityTime.DOP — Type
DOPDilution 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 DOPHDOP::Float64: Horizontal DOPTDOP::Float64: Time DOP
Utilities
PositionVelocityTime.get_sat_info — Function
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.
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_layout — Function
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).