Sidereon (Sidereon v0.8.0)

Copy Markdown View Source

Satellite toolkit for Elixir. SGP4 orbit propagation, coordinate transformations, and ground station pass prediction.

Summary

Functions

Compute Doppler shift for a satellite-ground link.

Check whether a satellite is in Earth's shadow (eclipse) at a given time.

Compute geodetic coordinates (lat/lon/alt) for a satellite at a given time.

Compute the look angle (azimuth/elevation/range) from a ground station to a satellite at a given time.

Compute the angle between satellite nadir and the Moon direction.

Parse a Two-Line Element set.

Predict visible passes of a satellite over a ground station.

Propagate orbital elements to a specific datetime, returning TEME position and velocity.

Compute the angle between satellite nadir and the Sun direction.

Convert a TEME state vector to GCRS (Geocentric Celestial Reference System).

Types

gcrs_state()

@type gcrs_state() :: %{position: vec3(), velocity: vec3()}

ground_station()

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

vec3()

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

Functions

doppler(tle, datetime, station, frequency_hz)

@spec doppler(Sidereon.Elements.t(), DateTime.t(), ground_station(), number()) ::
  {:ok,
   %{range_rate_km_s: float(), doppler_hz: float(), doppler_ratio: float()}}
  | {:error, term()}

Compute Doppler shift for a satellite-ground link.

Propagates the TLE, transforms to GCRS, and computes the range rate and Doppler shift at the given carrier frequency.

The station is a map: %{latitude: deg, longitude: deg, altitude_m: meters}.

Returns {:ok, %{range_rate_km_s: float, doppler_hz: float, doppler_ratio: float}}.

Example

station = %{latitude: 40.0, longitude: -74.0, altitude_m: 0.0}
{:ok, d} = Sidereon.doppler(tle, datetime, station, 437.0e6)
d.doppler_hz  # => ~10_000.0

eclipse(tle, datetime, ephemeris)

@spec eclipse(Sidereon.Elements.t(), DateTime.t(), Sidereon.Ephemeris.t()) ::
  {:ok, :sunlit | :penumbra | :umbra} | {:error, term()}

Check whether a satellite is in Earth's shadow (eclipse) at a given time.

Propagates the TLE, transforms to GCRS, fetches the Sun position from the ephemeris, and returns the eclipse status.

Returns {:ok, :sunlit | :penumbra | :umbra} or {:error, reason}.

Example

{:ok, eph} = Sidereon.Ephemeris.load("de421.bsp")
{:ok, status} = Sidereon.eclipse(tle, datetime, eph)

geodetic(tle, datetime)

@spec geodetic(Sidereon.Elements.t(), DateTime.t()) ::
  {:ok, Sidereon.Geodetic.t()} | {:error, term()}

Compute geodetic coordinates (lat/lon/alt) for a satellite at a given time.

Propagates the TLE, transforms TEME -> GCRS -> ITRS, and converts to WGS84.

Returns {:ok, %{latitude: deg, longitude: deg, altitude_km: km}}.

Example

{:ok, tle} = Sidereon.parse_tle(line1, line2)
{:ok, geo} = Sidereon.geodetic(tle, datetime)
geo.latitude  # => 51.23

look_angle(tle, datetime, station)

@spec look_angle(Sidereon.Elements.t(), DateTime.t(), ground_station()) ::
  {:ok, Sidereon.LookAngle.t()} | {:error, term()}

Compute the look angle (azimuth/elevation/range) from a ground station to a satellite at a given time.

The station is a map: %{latitude: deg, longitude: deg, altitude_m: meters}.

Returns {:ok, %{azimuth: deg, elevation: deg, range_km: km}}.

Example

station = %{latitude: 40.0, longitude: -74.0, altitude_m: 0.0}
{:ok, look} = Sidereon.look_angle(tle, datetime, station)
look.elevation  # => 25.7

moon_angle(satellite_gcrs_position, moon_position_from_earth)

@spec moon_angle(vec3(), vec3()) :: float()

Compute the angle between satellite nadir and the Moon direction.

See Sidereon.Angles.moon_angle/2 for details.

parse_tle(line1, line2)

@spec parse_tle(String.t(), String.t()) ::
  {:ok, Sidereon.Elements.t()} | {:error, String.t()}

Parse a Two-Line Element set.

Returns {:ok, %Sidereon.Elements{}} or {:error, reason}.

Examples

iex> {:ok, el} = Sidereon.parse_tle(
...>   "1 25544U 98067A   18184.80969102  .00001614  00000-0  31745-4 0  9993",
...>   "2 25544  51.6414 295.8524 0003435 262.6267 204.2868 15.54005638121106"
...> )
iex> el.catalog_number
"25544"

predict_passes(tle, ground_station, start_time, end_time, opts \\ [])

Predict visible passes of a satellite over a ground station.

See Sidereon.Passes.predict/5 for full documentation.

propagate(tle, datetime)

Propagate orbital elements to a specific datetime, returning TEME position and velocity.

Returns {:ok, %Sidereon.TemeState{}} with position in km and velocity in km/s, or {:error, reason}.

Examples

iex> {:ok, el} = Sidereon.parse_tle(
...>   "1 25544U 98067A   18184.80969102  .00001614  00000-0  31745-4 0  9993",
...>   "2 25544  51.6414 295.8524 0003435 262.6267 204.2868 15.54005638121106"
...> )
iex> {:ok, teme} = Sidereon.propagate(el, ~U[2018-07-04 00:00:00Z])
iex> {x, _y, _z} = teme.position
iex> x > 3000 and x < 4000
true

sun_angle(satellite_gcrs_position, sun_position_from_earth)

@spec sun_angle(vec3(), vec3()) :: float()

Compute the angle between satellite nadir and the Sun direction.

See Sidereon.Angles.sun_angle/2 for details.

teme_to_gcrs(teme_state, datetime, opts \\ [])

@spec teme_to_gcrs(
  Sidereon.TemeState.t() | gcrs_state(),
  DateTime.t() | tuple(),
  keyword()
) ::
  gcrs_state()

Convert a TEME state vector to GCRS (Geocentric Celestial Reference System).

Set skyfield_compat: true to reproduce the committed Skyfield oracle vectors used by the validation suite. The default is sidereon's native path.

Example

gcrs = Sidereon.teme_to_gcrs(teme, datetime)
gcrs = Sidereon.teme_to_gcrs(teme, datetime, skyfield_compat: true)