Skip to content

Repository files navigation

Astro

Library to help working with SPICE and ERFA libraries

Inner solar system with the main asteroid belt, true scale The nine planets, radially compressed

Real osculating orbits at a real epoch, drawn from JPL DE442 ephemerides by examples/orbits.livemd.

Rotating 3D map of the 100 nearest stellar systems, centered on Sol

The nearby-star map is generated by examples/stars.livemd from published catalog astrometry propagated through Astro.Star.

Installation

It is a bit more complicated then normal lib so pay attention:

  • use Elixir 1.16+ on Erlang/OTP 25+
  • instal ERFA library
  • use x86_64-linux; CSPICE N0067 is bundled, so compilation does not download the toolkit. The included Nix flake dev shell supplies the C toolchain and ERFA on NixOS.
  • add ex_astro to mix.exs; req is optional and only needed for the mix astro.kernels downloader
  def deps do
    [
      ...
      {:ex_astro, "~> 0.3"},
      {:req, "~> 0.7"},
      ...
    ]
  end
  • download SPICE kernels into your application's priv/kernels/; applications load configured paths when they start
mix astro.kernels

Kernels

Configure kernels that should load when the application starts. Resolve them with Application.app_dir/2 in config/runtime.exs (replace :my_app with your application) so the paths do not depend on the working directory of a release. The examples below need at least the leap-second kernel, planetary constants, the DE442 ephemeris, and gm_de440.tpc, which supplies the gravitational parameters Astro.Orbit.osculating/4 reads when no :mu is given:

config :ex_astro,
  spice_kernels:
    for path <- [
          "priv/kernels/lsk/naif0012.tls",
          "priv/kernels/pck/pck00011.tpc",
          "priv/kernels/pck/gm_de440.tpc",
          "priv/kernels/spk/planets/de442.bsp"
        ],
        do: Application.app_dir(:my_app, path)

Kernels downloaded after startup can be managed at runtime:

:ok = Astro.Kernel.load("/path/to/kernel.bsp")
{:ok, paths} = Astro.Kernel.loaded()
:ok = Astro.Kernel.unload("/path/to/kernel.bsp")

Missing configured files log a warning instead of preventing application startup, so they can be downloaded and loaded later. Kernel mutations are atomic against the library's single CSPICE pool and safe while other Astro calls are running.

mix astro.kernels skips kernels that already exist, except pck/earth_latest_high_prec.bpc. NAIF regenerates that high-precision Earth orientation kernel about twice a week, extending the measured data and the prediction that follows it, so every run downloads it again. Rerun the task regularly and restart the application (or Astro.Kernel.unload/1 and load/1 the file) to use the new data.

Every download must start with the SPICE ID word of its kernel type and, except for the two kernels NAIF replaces in place (pck/earth_latest_high_prec.bpc and lsk/latest_leapseconds.tls), match the SHA-256 pinned in Astro.Kernel.Catalog. A download that fails either check is discarded and reported; kernels already on disk are not re-hashed.

Time API

Astro.Time represents Julian Dates as two-part tuples {jd1, jd2} rather than a single float. This follows ERFA/SOFA conventions and preserves much more precision for time-scale conversions.

iex> jd = Astro.Time.to_julian_date(~N[2000-01-01 12:00:00])
iex> jd
{2451544.5, 0.5}
iex> Astro.Time.day2sec(jd)
0.0

Ephemeris and Orbit APIs

Astro.Ephemeris looks up SPICE body states as Astro.State structs: position in km and velocity in km/s. Astro.Orbit converts states to named osculating elements and back, and adds propagation, derived quantities, anomaly calculations, and perifocal geometry:

# UTC timestamp -> SPICE ephemeris time
et = Astro.Time.to_et(~U[2026-08-14 00:00:00Z])

# state of the Earth-Moon barycenter relative to the Sun
{:ok, %Astro.State{position: {x, y, z}, velocity: {vx, vy, vz}}, light_time_s} =
  Astro.Ephemeris.spkezr("3", et, "ECLIPJ2000", "NONE", "10")

# osculating orbit of Earth around the Sun in the ecliptic frame
{:ok, orbit} =
  Astro.Orbit.osculating("3", "10", et, frame: "ECLIPJ2000")

semi_major_axis_km = Astro.Orbit.semi_major_axis(orbit)
period_seconds = Astro.Orbit.period(orbit)

Astro.Support handles the metadata around these calls: body name/ID translation (bodn2c/1, bodc2n/1), scalar gravitational parameters (gm/1), general kernel-pool constants (bodvcd/2, bodvrd/2), and SPK file inspection (spkobj/1).

Star Catalog API

Astro.Star propagates caller-supplied Gaia, Hipparcos, and other star-catalog entries between two-part TDB Julian Date epochs. It also converts catalog coordinates to and from six-element BCRS position/velocity vectors. Catalog data is not bundled with the library.

iex> Astro.Star.starpv(ra, dec, pm_ra, pm_dec, parallax, radial_velocity)
{:ok, [x, y, z, vx, vy, vz]}

Examples

The examples/ directory contains Livebooks that explain and run the library together:

  • orbits.livemd - draws the SVG orbit diagrams above from JPL ephemerides
  • stars.livemd - draws the rotating 3D map of the 100 nearest stellar systems

Open them in Livebook. The notebooks download their data on first run and write the SVG snapshots next to themselves.

About

Astrophysics in Elixir

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages