SpaceDust

SpaceDust is a comprehensive astrodynamics library for Elixir, providing tools for satellite tracking, orbital mechanics, coordinate transformations, and ground-based observation calculations.

Features

Installation

Add space_dust to your list of dependencies in mix.exs:

def deps do
  [
    {:space_dust, "~> 0.2.0"}
  ]
end

Quick Start

Satellite Tracking Example

Track a geostationary satellite from a ground station:

alias SpaceDust.Ingest.Celestrak
alias SpaceDust.Utils.Tle
alias SpaceDust.State.{TEMEState, GeodeticState}
alias SpaceDust.State.Transforms
alias SpaceDust.Observations

# Fetch latest TLE for a satellite (NORAD catalog number)
{:ok, tle} = Celestrak.pullLatestTLE("40425")  # GALAXY-16

# Define ground observer location (Denver, CO)
observer = GeodeticState.new(39.7392, -104.9903, 1.6)  # lat, lon, alt (km)

# Propagate satellite to current time
epoch = DateTime.utc_now()
{position, velocity} = Tle.getRVatTime(tle, epoch)

# Convert from TEME to ECI J2000
teme_state = TEMEState.new(epoch, position, velocity)
eci_state = Transforms.teme_to_eci(teme_state)

# Calculate observation angles from ground station
az_el = Observations.compute_az_el(observer, eci_state)

IO.puts("Azimuth: #{az_el.azimuth * 180 / :math.pi()}")
IO.puts("Elevation: #{az_el.elevation * 180 / :math.pi()}")
IO.puts("Range: #{az_el.range} km")
IO.puts("Above Horizon: #{Observations.AzEl.above_horizon?(az_el)}")

Modules

SpaceDust.Time

Time system representations and conversions between different astronomical time scales.

Module Description
SpaceDust.Time.UTC Coordinated Universal Time (civil time standard)
SpaceDust.Time.TAI International Atomic Time (continuous, no leap seconds)
SpaceDust.Time.TT Terrestrial Time (modern astronomical standard)
SpaceDust.Time.JulianDate Julian Date and Modified Julian Date
SpaceDust.Time.GMST Greenwich Mean Sidereal Time
SpaceDust.Time.GPS GPS Time
SpaceDust.Time.Transforms Conversions between time systems

Example: Time Conversions

alias SpaceDust.Time.{UTC, Transforms, JulianDate}

# Create UTC time from ISO8601 string
utc = UTC.from_iso8601!("2026-01-06T12:00:00Z")

# Convert to other time systems
tai = Transforms.utc_to_tai(utc)
tt = Transforms.utc_to_tt(utc)
jd = Transforms.utc_to_jd(utc)

# Get Modified Julian Date
mjd = UTC.to_mjd(utc)

# Calculate Julian centuries since J2000.0
centuries = JulianDate.julian_centuries_j2000(jd)

SpaceDust.State

State vector representations in various coordinate frames.

Module Description
SpaceDust.State.ECIState Earth-Centered Inertial J2000 frame
SpaceDust.State.TEMEState True Equator Mean Equinox frame (SGP4 output)
SpaceDust.State.ECEFState Earth-Centered Earth-Fixed frame
SpaceDust.State.GeodeticState Geodetic coordinates (lat/lon/alt on WGS84)
SpaceDust.State.KeplerianElements Classical orbital elements
SpaceDust.State.Transforms Coordinate frame transformations

Example: Coordinate Transformations

alias SpaceDust.State.{ECIState, TEMEState, KeplerianElements, Transforms}

# Create an ECI state vector
eci = ECIState.new(
  ~U[2026-01-06 12:00:00Z],
  {7000.0, 0.0, 0.0},      # position [km]
  {0.0, 7.5, 0.0}          # velocity [km/s]
)

# Convert to Keplerian elements
kepler = Transforms.eci_to_keplerian(eci)
IO.puts("Semi-major axis: #{kepler.semi_major_axis} km")
IO.puts("Eccentricity: #{kepler.eccentricity}")
IO.puts("Orbital period: #{KeplerianElements.period(kepler)} seconds")

# Convert to ECEF (rotating frame)
ecef = Transforms.eci_to_ecef(eci)

SpaceDust.Observations

Angular observation calculations for ground-based tracking.

Module Description
SpaceDust.Observations Main observation computation functions
SpaceDust.Observations.RaDec Right Ascension / Declination observations
SpaceDust.Observations.AzEl Azimuth / Elevation observations

Example: Observation Calculations

alias SpaceDust.State.{ECIState, GeodeticState}
alias SpaceDust.Observations

# Ground observer
observer = GeodeticState.new(40.0, -105.0, 1.6)  # Denver, CO

# Satellite position in ECI
satellite = ECIState.new(
  ~U[2026-01-06 12:00:00Z],
  {-41851.0, -5169.0, 11.7},
  {0.377, -3.066, 0.0}
)

# Compute topocentric observation angles
az_el = Observations.compute_az_el(observer, satellite)
ra_dec = Observations.compute_ra_dec(observer, satellite)

# Check visibility
if Observations.AzEl.above_horizon?(az_el) do
  IO.puts("Satellite is visible!")
  IO.puts("Look direction: #{Observations.AzEl.compass_direction(az_el)}")
end

# With angular rates
az_el_rates = Observations.compute_az_el(observer, satellite, include_rates: true)

SpaceDust.Bodies

Celestial body parameters and position calculations.

Module Description
SpaceDust.Bodies.Earth Earth parameters, precession, nutation
SpaceDust.Bodies.Sun Solar position in ECI frame
SpaceDust.Bodies.Moon Lunar position in ECI frame
SpaceDust.Bodies.Barycenter Earth-Moon barycenter calculations

Example: Celestial Body Positions

alias SpaceDust.Bodies.{Sun, Moon}

epoch = ~U[2026-01-06 12:00:00Z]

# Get Sun position in ECI (meters)
sun_pos = Sun.eci_position(epoch)

# Get Moon position in ECI (meters)
moon_pos = Moon.eci_position(epoch)

# Check if satellite is in Earth's shadow
satellite_eci = ECIState.new(epoch, {7000.0, 0.0, 0.0}, {0.0, 7.5, 0.0})
in_shadow = Sun.in_earth_shadow?(epoch, satellite_eci)

SpaceDust.Data

Reference data tables and Earth Orientation Parameters.

Module Description
SpaceDust.Data.EOP Earth Orientation Parameters retrieval
SpaceDust.Data.EOPCache High-performance EOP cache with interpolation
SpaceDust.Data.IAU1980 IAU 1980 nutation coefficients
SpaceDust.Data.LeapSecond Leap second table

The EOP data is sourced from IERS finals.all and includes predictions extending approximately one year into the future, ensuring continuous operation without warnings for near-future propagations.

SpaceDust.Ingest

API clients for external data sources.

Module Description
SpaceDust.Ingest.Celestrak Celestrak TLE retrieval

Example: Fetching TLEs

alias SpaceDust.Ingest.Celestrak

# Fetch by NORAD catalog number
{:ok, tle} = Celestrak.pullLatestTLE("25544")  # ISS
IO.puts("Epoch: #{tle.epoch}")
IO.puts("Inclination: #{tle.inclinationDeg}")
IO.puts("Mean Motion: #{tle.meanMotion} rev/day")

SpaceDust.Utils

Utility functions and constants.

Module Description
SpaceDust.Utils.Constants Physical and mathematical constants
SpaceDust.Utils.Tle TLE parsing and SGP4 propagation
SpaceDust.Utils.TwoLineElementSet TLE data structure

Example: TLE Propagation

alias SpaceDust.Utils.Tle
alias SpaceDust.State.TEMEState

# Parse TLE lines directly
line1 = "1 25544U 98067A   26006.50000000  .00016717  00000-0  10270-3 0  9002"
line2 = "2 25544  51.6400 208.1200 0001234  85.0000 275.0000 15.48919100123456"

{:ok, tle} = Tle.parseTLE(line1, line2)

# Propagate to a specific time
epoch = ~U[2026-01-06 18:00:00Z]
{position, velocity} = Tle.getRVatTime(tle, epoch)

# Create TEME state for further transformations
teme = TEMEState.new(epoch, position, velocity)

SpaceDust.Math

Mathematical operations optimized for astrodynamics calculations.

Module Description
SpaceDust.Math.Vector 3D vector operations
SpaceDust.Math.Matrix 3x3 matrix operations
SpaceDust.Math.Functions Polynomial evaluation, angle utilities

Performance

SpaceDust uses several optimizations for high-performance calculations:

Data Sources

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome. Please open an issue or submit a pull request on GitHub.

Links