Manage and propagate satellite constellations.
Build a constellation from parsed TLEs and propagate all satellites to a given time. Useful for visibility computations over a shared fleet.
Examples
constellation = Sidereon.Constellation.from_tles("custom", tles)
constellation.count
# Propagate all satellites to now
positions = Sidereon.Constellation.propagate_all(constellation, DateTime.utc_now())
Enum.each(positions, fn {norad_id, pos} ->
IO.puts("#{norad_id}: #{inspect(pos)}")
end)
# Find visible satellites from a ground station
{:ok, visible} = Sidereon.Constellation.visible_from(constellation, station, datetime)
Summary
Functions
Create a constellation from a list of TLEs.
Compute WGS84 sub-satellite ground tracks for every satellite over a shared epoch grid.
Compute topocentric look-angle arcs from a ground station for every satellite over a shared epoch grid.
Predict passes over a ground station for every satellite within a time window.
Propagate all satellites to a given time.
Find satellites visible from a ground station at a given time.
Types
@type batch_error() :: {:invalid_satellites, invalid_satellites()} | {:invalid_option, term()} | {:invalid_field, atom(), term()} | {:nif_error, String.t()}
@type fleet_pass() :: %{ satellite_index: non_neg_integer(), catalog_number: String.t(), pass: Sidereon.Pass.t() }
@type t() :: %Sidereon.Constellation{ count: non_neg_integer(), name: String.t(), satellites: [Sidereon.Elements.t()] }
@type visible_error() :: {:invalid_satellites, invalid_satellites()} | {:nif_error, String.t()}
Functions
@spec from_tles(String.t(), [Sidereon.Elements.t()]) :: t()
Create a constellation from a list of TLEs.
Examples
constellation = Sidereon.Constellation.from_tles("custom", tles)
@spec ground_tracks(t(), [DateTime.t()], keyword()) :: {:ok, [[Sidereon.Geodetic.t()]]} | {:error, batch_error()}
Compute WGS84 sub-satellite ground tracks for every satellite over a shared epoch grid.
Returns one track per satellite in fleet order: element i is a list of
%Sidereon.Geodetic{} (latitude/longitude in degrees, ellipsoidal altitude in
km) for satellites[i], one per datetime. Each point is reduced
TEME -> GCRS -> ITRS -> WGS84 geodetic by the engine's transforms. A satellite
whose element set fails SGP4 initialization yields an empty track, keeping the
result index-aligned. This is the batched companion to Sidereon.ground_track/3.
Options
:opsmode- SGP4 operation mode,:afspc(default) or:improved.
Examples
times = for s <- 0..600//60, do: DateTime.add(DateTime.utc_now(), s, :second)
{:ok, tracks} = Sidereon.Constellation.ground_tracks(constellation, times)
hd(tracks) |> hd() |> Map.get(:latitude)
@spec look_angle_arcs(t(), map(), [DateTime.t()], keyword()) :: {:ok, [[Sidereon.LookAngle.t()]]} | {:error, batch_error()}
Compute topocentric look-angle arcs from a ground station for every satellite over a shared epoch grid.
Returns one arc per satellite in fleet order (the constellation's
satellites order): element i is a list of %Sidereon.LookAngle{} for
satellites[i], one per datetime. A satellite whose element set fails SGP4
initialization yields an empty arc, so the result stays index-aligned with the
constellation. This is the batched companion to Sidereon.look_angle/4.
Options
:opsmode- SGP4 operation mode,:afspc(default) or:improved. Each satellite is built with this opsmode, so the arcs are consistent withvisible_from/4andpasses/5computed under the same opsmode.
Examples
times = for s <- 0..600//60, do: DateTime.add(DateTime.utc_now(), s, :second)
{:ok, arcs} = Sidereon.Constellation.look_angle_arcs(constellation, station, times)
hd(arcs) |> hd() |> Map.get(:elevation)
@spec passes(t(), map(), DateTime.t(), DateTime.t(), keyword()) :: {:ok, [fleet_pass()]} | {:error, batch_error()}
Predict passes over a ground station for every satellite within a time window.
Returns a flat list of passes across the whole constellation, each tagged with
the fleet-order :satellite_index and the :catalog_number of the satellite
it belongs to:
{:ok, [%{
satellite_index: non_neg_integer(),
catalog_number: String.t(),
pass: %Sidereon.Pass{}
}]}Passes are emitted satellite by satellite in fleet order, each satellite's
passes ordered by rise time. A satellite whose element set fails SGP4
initialization contributes no passes (its fleet index is still consumed, so
indices match the constellation order). This is the constellation companion to
Sidereon.Passes.predict/5.
Options
:min_elevation- minimum peak elevation in degrees to keep a pass (default0.0); likeSidereon.Passes.predict/5, rise/set always reference the 0-degree horizon:step_seconds- coarse propagation step in seconds (default60):opsmode- SGP4 operation mode,:afspc(default) or:improved
Examples
start_dt = DateTime.utc_now()
end_dt = DateTime.add(start_dt, 86_400, :second)
{:ok, passes} = Sidereon.Constellation.passes(constellation, station, start_dt, end_dt)
hd(passes).satellite_index
@spec propagate_all(t(), DateTime.t()) :: [ {String.t() | nil, {:ok, Sidereon.TemeState.t()} | {:error, term()}} ]
Propagate all satellites to a given time.
Returns a list of {catalog_number, {:ok, teme_state}} or
{catalog_number, {:error, reason}} tuples.
Examples
results = Sidereon.Constellation.propagate_all(constellation, ~U[2024-07-04 00:00:00Z])
for {id, {:ok, teme}} <- results do
IO.puts("#{id}: #{inspect(teme.position)}")
end
@spec visible_from(t(), map(), DateTime.t(), keyword()) :: {:ok, [visible_satellite()]} | {:error, visible_error()}
Find satellites visible from a ground station at a given time.
Returns satellites above min_elevation degrees, sorted by elevation
(highest first).
Options
:min_elevation- minimum elevation in degrees (default: 10.0):opsmode- SGP4 operation mode,:afspc(default) or:improved. Each satellite is built with this opsmode, so visibility is consistent with the look angle and passes computed under the same opsmode.
Examples
{:ok, visible} = Sidereon.Constellation.visible_from(constellation, station, datetime)
for sat <- visible do
IO.puts("#{sat.catalog_number}: el=#{sat.elevation}° range=#{sat.range_km} km")
end