Orbis.GNSS.Ephemeris (Orbis v0.17.0)

Copy Markdown View Source

A unified satellite-ephemeris sampling surface over precise SP3 products and broadcast navigation messages.

sample/3 evaluates a list of satellites across an epoch range at a fixed step and returns a tidy per-satellite, per-epoch table of ECEF position and satellite clock bias. The call is identical whether the source is an Orbis.GNSS.SP3 precise product or an Orbis.GNSS.Broadcast navigation product, so a caller can swap precise for broadcast transparently; the dispatch is on the handle's type.

This is Elixir orchestration over the existing per-satellite evaluators (Orbis.GNSS.SP3.position/3 and Orbis.GNSS.Broadcast.position/3); no orbit math lives here. The underlying products are parsed once into resource handles and reused across every cell — no file is re-read per epoch.

The broadcast models follow IS-GPS-200 (GPS LNAV), the Galileo OS-SIS-ICD, and the BeiDou BDS-SIS-ICD; precise products are SP3-c / SP3-d (IGS).

Frame, time, and sign conventions

  • Frame: position is ITRF/IGS-realization ECEF, in meters (x_m, y_m, z_m), the same frame both products evaluate in.
  • Time: every epoch is interpreted in GPS time (GPST). No leap-second shifting is applied to the supplied epochs; the broadcast evaluator maps GPST onto each system's own scale (BDT for BeiDou, UTC-referenced for GLONASS) internally.
  • Clock sign: clock_s is the satellite clock offset in seconds, with a positive value meaning the satellite clock is ahead of system time. The pseudorange geometric correction is therefore range + c * clock_s. The SP3 and broadcast paths share this convention (the broadcast value is the clock-polynomial total including the relativistic eccentricity term and the broadcast group delay).

Gaps are explicit

A cell whose satellite has no valid ephemeris at its epoch carries status: :no_ephemeris and nil position/clock fields. The sampler never extrapolates beyond a product's validity: an SP3 epoch outside the file's span, or a broadcast epoch outside any record's fit interval, is reported as a gap, not filled.

Example

{:ok, sp3} = Orbis.GNSS.SP3.load("igs.sp3")

rows =
  Orbis.GNSS.Ephemeris.sample(sp3, ["G01", "E11"], %{
    from: ~N[2020-06-25 00:00:00],
    to: ~N[2020-06-25 01:00:00],
    step_s: 300
  })

[%Orbis.GNSS.Ephemeris.Row{} = row | _] = rows
row.satellite_id   # "G01"
row.status         # :ok | :no_ephemeris
row.x_m            # ITRF/IGS ECEF X, meters (nil on a gap)

Summary

Types

A parsed ephemeris source: a precise SP3 product or a broadcast product.

The sampling window: a from/to epoch pair (inclusive of from, and of to when it falls on a step boundary) and a positive step_s in seconds.

Functions

Sample sat_ids across the window from a precise or broadcast source.

Types

source()

@type source() :: Orbis.GNSS.SP3.t() | Orbis.GNSS.Broadcast.t()

A parsed ephemeris source: a precise SP3 product or a broadcast product.

window()

@type window() :: %{
  from: NaiveDateTime.t(),
  to: NaiveDateTime.t(),
  step_s: pos_integer()
}

The sampling window: a from/to epoch pair (inclusive of from, and of to when it falls on a step boundary) and a positive step_s in seconds.

Functions

sample(source, sat_ids, map)

@spec sample(source(), [String.t()], window()) :: [Orbis.GNSS.Ephemeris.Row.t()]

Sample sat_ids across the window from a precise or broadcast source.

source is a loaded Orbis.GNSS.SP3 or Orbis.GNSS.Broadcast handle — the same call shape for both. sat_ids is a list of canonical RINEX tokens ("G01", "E11", "C06", "R07"). window is a map with :from, :to (NaiveDateTime in GPST), and :step_s (a positive integer number of seconds).

Returns a flat list of Orbis.GNSS.Ephemeris.Row structs, one per satellite-epoch cell, in sat_ids order then ascending epoch. A cell with no valid ephemeris carries status: :no_ephemeris and nil values; the handle is reused for every cell, so no file is re-read.

Raises ArgumentError for a non-positive step or a window with to before from.