# `Sidereon.Coordinates`
[🔗](https://github.com/neilberkman/sidereon-ex/blob/main/lib/sidereon/coordinates.ex#L1)

Coordinate frame transformations for satellite state vectors.

Supports:
- TEME → GCRS coordinate conversion with committed oracle fixtures
- GCRS → ITRS (Earth-fixed / ECEF)
- ITRS → GCRS
- ITRS → Geodetic (WGS84 lat/lon/alt)
- Geodetic → ITRS
- Topocentric (azimuth/elevation/range) from a ground station

Time-scale handling is intentionally kept behind the public datetime inputs
in this module. The Rust core and Python binding expose lower-level
`Instant`, `TimeScale`, leap-second, and UT1 metadata APIs; the Elixir public
surface does not expose those as standalone modules yet to avoid a partial
time API.

# `datetime`

```elixir
@type datetime() ::
  DateTime.t()
  | {{integer(), integer(), integer()}, {integer(), integer(), integer()}}
```

# `geodetic_input`

```elixir
@type geodetic_input() ::
  Sidereon.Geodetic.t()
  | %{latitude: number(), longitude: number(), altitude_km: number()}
  | vec3()
```

# `ground_station`

```elixir
@type ground_station() :: %{
  latitude: number(),
  longitude: number(),
  altitude_m: number()
}
```

# `state`

```elixir
@type state() :: %{position: vec3(), velocity: vec3()}
```

# `vec3`

```elixir
@type vec3() :: {number(), number(), number()}
```

# `gcrs_to_itrs`

```elixir
@spec gcrs_to_itrs(%{position: vec3()}, datetime(), keyword()) ::
  {float(), float(), float()}
```

Convert a GCRS position to ITRS (Earth-fixed / ECEF).

Accepts a map with a `:position` tuple (km) and a datetime.
Set `skyfield_compat: true` to reproduce the committed Skyfield oracle
vectors used by the validation suite. The default is sidereon's native path.

Returns `{x, y, z}` in km.

# `geodetic_to_itrs`

```elixir
@spec geodetic_to_itrs(geodetic_input()) :: {float(), float(), float()}
```

Convert WGS84 geodetic coordinates to an ITRS/ECEF position.

Accepts `%Sidereon.Geodetic{}`, a map with `:latitude`, `:longitude`, and
`:altitude_km`, or a `{latitude, longitude, altitude_km}` tuple. Latitude and
longitude are degrees; altitude is kilometres.

Returns `{x, y, z}` in km.

# `itrs_to_gcrs`

```elixir
@spec itrs_to_gcrs(vec3(), datetime()) :: {float(), float(), float()}
```

Convert an ITRS/ECEF position to GCRS.

Accepts a position tuple `{x, y, z}` in km and a datetime.

Returns `{x, y, z}` in km.

# `teme_to_gcrs`

```elixir
@spec teme_to_gcrs(state(), datetime(), keyword()) :: state()
```

Convert a TEME state vector to GCRS.

Accepts a map with `:position` and `:velocity` tuples (km and km/s),
and a datetime (either `DateTime` or `{{y,m,d},{h,m,s}}` tuple).

## Options

  * `:skyfield_compat` - when `true`, reproduces the committed Skyfield
    oracle vectors used by the validation suite. Default `false` uses
    sidereon's native path.

Returns a map with GCRS `:position` and `:velocity`.

# `to_geodetic`

```elixir
@spec to_geodetic(vec3()) :: Sidereon.Geodetic.t()
```

Convert an ITRS/ECEF position to WGS84 geodetic coordinates.

Accepts a position tuple `{x, y, z}` in km.

Returns `%{latitude: degrees, longitude: degrees, altitude_km: km}`.

# `to_topocentric`

```elixir
@spec to_topocentric(%{position: vec3()}, datetime(), ground_station(), keyword()) ::
  Sidereon.LookAngle.t()
```

Compute topocentric azimuth, elevation, and range from a ground station
to a satellite given in GCRS.

## Parameters

  - `gcrs_state` - map with `:position` tuple (km) in GCRS
  - `datetime` - observation time
  - `station` - `%{latitude: deg, longitude: deg, altitude_m: meters}`

Returns `%{azimuth: degrees, elevation: degrees, range_km: km}`.

---

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