# `Sidereon.GNSS.Broadcast`
[🔗](https://github.com/neilberkman/sidereon-ex/blob/main/lib/sidereon/gnss/broadcast.ex#L1)

A parsed RINEX broadcast-navigation product (GPS LNAV, Galileo I/NAV+F/NAV,
BeiDou D1/D2, GLONASS).

Holds the broadcast Keplerian elements and clock terms as a resource handle,
the broadcast-ephemeris counterpart to `Sidereon.GNSS.SP3`. Pass a handle to
`Sidereon.GNSS.Positioning.solve/4` to position from broadcast ephemeris instead
of a precise SP3 product. The navigation file is parsed exactly once; the
parsed product is held as a reference, not re-parsed per call.

Parsing covers RINEX 3.x and 4.xx files: GPS, Galileo, and BeiDou records
(including BeiDou geostationary satellites), GPS/QZSS CNAV-family records, and
GLONASS (a PZ-90.11 state-vector model propagated by Runge-Kutta integration
rather than Keplerian elements). Other constellations in a mixed file are
skipped.

The orbit and clock models follow IS-GPS-200 (GPS LNAV), the Galileo OS-SIS-ICD
(I/NAV + F/NAV), and the BeiDou BDS-SIS-ICD (D1/D2), parsed from RINEX 3.x/4.xx
navigation records.

## Epochs

`position/3` interprets the query epoch in GPS time (GPST). A `NaiveDateTime` or
`{{year, month, day}, {hour, minute, second}}` is converted to a continuous
second-of-J2000 via `Sidereon.GNSS.Time`; the crate maps that onto each system's
own time scale (BDT for BeiDou, UTC-referenced for GLONASS) before selecting the
governing record. No leap-second shifting is applied to the supplied epoch.

# `message_preference`

```elixir
@type message_preference() :: :legacy | :modern
```

# `nav_message`

```elixir
@type nav_message() ::
  :gps_lnav
  | :gps_cnav
  | :gps_cnav2
  | :qzss_cnav
  | :qzss_cnav2
  | :galileo_inav
  | :galileo_fnav
  | :beidou_d1
  | :beidou_d2
```

# `t`

```elixir
@type t() :: %Sidereon.GNSS.Broadcast{handle: reference()}
```

# `cnav_ura_ned`

```elixir
@spec cnav_ura_ned(
  Sidereon.GNSS.Broadcast.CnavParameters.t(),
  Sidereon.GNSS.Broadcast.WeekTow.t()
) ::
  float() | nil
```

Time-dependent CNAV URA_NED bound in metres.

# `cnav_ura_nominal`

```elixir
@spec cnav_ura_nominal(integer()) :: float() | nil
```

Nominal CNAV URA in metres for a URA ED/NED0 index.

# `encode_nav`

```elixir
@spec encode_nav(t()) :: String.t()
```

Serialize the usable GPS, Galileo, and BeiDou broadcast records to RINEX 3
navigation text.

Round-trips with `parse/1`: re-parsing the output reconstructs the same
records. The text covers the records held by the parsed product (the default
SPP policy output exposed by `records/1`); GLONASS state-vector records are not
serialized.

# `glonass_record_count`

```elixir
@spec glonass_record_count(t()) :: non_neg_integer()
```

Number of healthy GLONASS state-vector records held by the parsed product.

# `glonass_records`

```elixir
@spec glonass_records(t()) :: [Sidereon.GNSS.Broadcast.GlonassRecord.t()]
```

Healthy GLONASS broadcast state-vector records in file order.

# `iono_corrections`

```elixir
@spec iono_corrections(t()) :: Sidereon.GNSS.Broadcast.IonoCorrections.t()
```

Broadcast ionosphere coefficients parsed from the NAV header.

Returns GPS and BeiDou Klobuchar-8 coefficient sets when present, otherwise
`nil` for each missing set.

# `leap_seconds`

```elixir
@spec leap_seconds(t()) :: float() | nil
```

GPS minus UTC leap seconds from the NAV header, if present.

# `load`

```elixir
@spec load(String.t()) :: {:ok, t()} | {:error, term()}
```

Parse a RINEX 3.x or 4.xx navigation file from disk.

Returns `{:ok, %Sidereon.GNSS.Broadcast{}}` or `{:error, reason}`. The file
is read and parsed once; the parsed product is held as a resource handle.

# `load!`

```elixir
@spec load!(String.t()) :: t()
```

Like `load/1` but raises on failure.

# `message_preference`

```elixir
@spec message_preference(t()) :: message_preference()
```

GPS/QZSS record-family preference used by mixed LNAV/CNAV selection.

# `parse`

```elixir
@spec parse(
  String.t(),
  keyword()
) :: {:ok, t()} | {:error, term()}
```

Parse an in-memory RINEX 3.x or 4.xx navigation text buffer into a handle.

# `position`

```elixir
@spec position(t(), String.t(), NaiveDateTime.t() | tuple()) ::
  {:ok, Sidereon.GNSS.Broadcast.State.t()} | {:error, term()}
```

Evaluate the broadcast state of satellite `sat_id` at `epoch`.

`sat_id` is the canonical RINEX token, e.g. `"G01"` (GPS PRN 1), `"E12"`,
`"C30"`, `"R07"`. `epoch` is a `NaiveDateTime` or a
`{{year, month, day}, {hour, minute, second}}` tuple, interpreted in GPS time.

Returns `{:ok, %Sidereon.GNSS.Broadcast.State{}}` with the ECEF position (meters)
and satellite clock offset (seconds), `{:error, :no_ephemeris}` when no
broadcast record covers that satellite at that epoch (the validity window has no
match; this is **not** extrapolated), or `{:error, reason}` for a malformed
satellite token or a non-integer-second tuple epoch.

Evaluating the same satellite across a window reuses the parsed handle; the
navigation file is never re-read.

# `record_count`

```elixir
@spec record_count(t()) :: non_neg_integer()
```

Number of usable GPS, Galileo, and BeiDou records held by the parsed product.

# `records`

```elixir
@spec records(t()) :: [Sidereon.GNSS.Broadcast.Record.t()]
```

Usable GPS, Galileo, and BeiDou broadcast records in file order.

The returned records are the core store's default SPP policy output: healthy
GPS LNAV, Galileo I/NAV, and BeiDou D1/D2 records. Galileo F/NAV and unhealthy
satellites are not included in this accessor.

# `records_detailed`

```elixir
@spec records_detailed(t()) :: [Sidereon.GNSS.Broadcast.DetailedRecord.t()]
```

Detailed GPS, Galileo, BeiDou, and CNAV-family records in file order.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
