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.
Solve the WGS84 direct geodesic problem.
Solve the WGS84 inverse geodesic problem.
Compute geodetic coordinates (lat/lon/alt) for a satellite at a given time.
Compute the ground track (sub-satellite points) for a satellite over a list of times.
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.
Compute ocean tide loading station displacement in metres, ECEF.
Parse a Two-Line Element set.
Parse a multi-record TLE file (CelesTrak / Space-Track style) into named satellites.
Predict visible passes of a satellite over a ground station.
Propagate orbital elements to a specific datetime, returning TEME position and velocity.
Compute solid-earth pole tide station displacement in metres, ECEF.
Compute solid-earth tide station displacement in metres, ECEF.
Compute the angle between satellite nadir and the Sun direction.
Compute Sun and Moon ECEF vectors for UTC Unix microsecond epochs.
Compute Sun and Moon ECI vectors for UTC Unix microsecond epochs.
Convert a TEME state vector to GCRS (Geocentric Celestial Reference System).
Fixed inter-system time offset to - from, in seconds, for the atomic scales.
Leap-aware inter-system time offset to - from, in seconds, at utc_jd.
Types
Functions
@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
@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)
See Sidereon.GNSS.Constellation.from_celestrak_json_lenient/2.
See Sidereon.GNSS.Constellation.from_celestrak_omm_lenient/2.
@spec geodesic_direct(number(), number(), number(), number()) :: {:ok, Sidereon.Geodesic.direct_result()} | {:error, Sidereon.Geodesic.GeodesicError.t()}
Solve the WGS84 direct geodesic problem.
See Sidereon.Geodesic.direct/4 for details.
@spec geodesic_inverse(number(), number(), number(), number()) :: {:ok, Sidereon.Geodesic.inverse_result()} | {:error, Sidereon.Geodesic.GeodesicError.t()}
Solve the WGS84 inverse geodesic problem.
See Sidereon.Geodesic.inverse/4 for details.
@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
@spec ground_track(Sidereon.Elements.t(), [DateTime.t()], keyword()) :: {:ok, [Sidereon.Geodetic.t()]} | {:error, term()}
Compute the ground track (sub-satellite points) for a satellite over a list of times.
For each datetime the satellite is propagated and reduced through
TEME -> GCRS -> ITRS -> WGS84 geodetic, yielding the point on the ellipsoid
directly beneath the satellite. This is the batch companion to
Sidereon.geodetic/2.
Options
:opsmode- SGP4 operation mode,:afspc(default) or:improved. The satellite is built with this opsmode, so the track is consistent withSidereon.geodetic/2andSidereon.predict_passes/5under the same opsmode.
Returns {:ok, [%Sidereon.Geodetic{}]} (one per datetime, in order) or
{:error, reason}.
Example
now = DateTime.utc_now()
times = for s <- 0..600//60, do: DateTime.add(now, s, :second)
{:ok, track} = Sidereon.ground_track(tle, times)
hd(track).latitude # => 12.34
@spec look_angle(Sidereon.Elements.t(), DateTime.t(), ground_station(), keyword()) :: {: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}}.
Options
:opsmode- SGP4 operation mode,:afspc(default) or:improved. The satellite is built with this opsmode, so the look angle is consistent withSidereon.predict_passes/5run under the same opsmode.
Example
station = %{latitude: 40.0, longitude: -74.0, altitude_m: 0.0}
{:ok, look} = Sidereon.look_angle(tle, datetime, station)
look.elevation # => 25.7
Compute the angle between satellite nadir and the Moon direction.
See Sidereon.Angles.moon_angle/2 for details.
@spec ocean_tide_loading( vec3(), integer(), integer(), integer(), number(), [[number()]], [[number()]] ) :: {:ok, vec3()} | {:error, term()}
Compute ocean tide loading station displacement in metres, ECEF.
@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"
@spec parse_tle_file(String.t()) :: {:ok, %{ satellites: [%{name: String.t(), tle: Sidereon.Elements.t()}], skipped: non_neg_integer() }}
Parse a multi-record TLE file (CelesTrak / Space-Track style) into named satellites.
Handles bare two-line sets, three-line name+line1+line2 sets, and CelesTrak
0 NAME markers, tolerating blank lines and CRLF. Returns
{:ok, %{satellites: [%{name: name, tle: %Sidereon.Elements{}}], skipped: n}},
where each tle is ready for propagate/2, look_angle/3, etc., and skipped
counts records that failed SGP4 initialization.
Examples
iex> text = """
...> ISS (ZARYA)
...> 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, %{satellites: [sat], skipped: 0}} = Sidereon.parse_tle_file(text)
iex> sat.name
"ISS (ZARYA)"
@spec predict_passes( Sidereon.Elements.t(), Sidereon.Passes.ground_station(), DateTime.t(), DateTime.t(), keyword() ) :: {:ok, [Sidereon.Pass.t()]} | {:error, Sidereon.Passes.predict_error()}
Predict visible passes of a satellite over a ground station.
See Sidereon.Passes.predict/5 for full documentation.
@spec propagate(Sidereon.Elements.t(), DateTime.t()) :: {:ok, Sidereon.TemeState.t()} | {:error, Sidereon.SGP4.propagation_error()}
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
@spec solid_earth_pole_tide( vec3(), integer(), integer(), integer(), number(), number(), number() ) :: {:ok, vec3()} | {:error, term()}
Compute solid-earth pole tide station displacement in metres, ECEF.
@spec solid_earth_tide( vec3(), integer(), integer(), integer(), number(), vec3(), vec3() ) :: {:ok, vec3()} | {:error, term()}
Compute solid-earth tide station displacement in metres, ECEF.
Compute the angle between satellite nadir and the Sun direction.
See Sidereon.Angles.sun_angle/2 for details.
Compute Sun and Moon ECEF vectors for UTC Unix microsecond epochs.
Compute Sun and Moon ECI vectors for UTC Unix microsecond epochs.
@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)
Fixed inter-system time offset to - from, in seconds, for the atomic scales.
See Sidereon.GNSS.Time.timescale_offset/2 for the scale names and the
epoch-required/unsupported error cases.
Leap-aware inter-system time offset to - from, in seconds, at utc_jd.