JPL/NAIF SPK (DAF .bsp) ephemeris kernel reader.
Computes positions and velocities of solar system bodies, spacecraft, and minor planets from JPL SPK/BSP kernels (DE421, DE440, Horizons exports, etc.).
The kernel is parsed once into a loaded handle by load/1; querying never
re-reads the file. Reading and evaluation are delegated to
sidereon_core::astro::spk, the validated SPK reader shared by the rest of the
engine and by the other language bindings. It evaluates SPK segment types 2
(Chebyshev position), 3 (Chebyshev state), and 21 (Extended Modified Difference
Arrays), so DE-series planetary kernels and Horizons spacecraft / asteroid
kernels are all supported through the same code path.
Example
{:ok, eph} = Sidereon.Ephemeris.load("de421.bsp")
# Position and velocity at an ephemeris epoch (TDB seconds past J2000):
{:ok, state} = Sidereon.Ephemeris.state(eph, :moon, :earth, 0.0)
state.position_km
state.velocity_km_s
# Convenience: position only, from a DateTime or Julian Date (TDB):
{:ok, {x, y, z}} = Sidereon.Ephemeris.position(eph, :sun, :earth, ~U[2024-01-01 12:00:00Z])
# The parsed segment table:
Sidereon.Ephemeris.segments(eph)Bodies
Bodies may be given as atoms (:ssb / :solar_system_barycenter, :mercury,
:venus, :earth_moon_barycenter / :emb, :mars, :jupiter, :saturn,
:uranus, :neptune, :pluto, :sun, :moon, :earth) or as raw NAIF
integer codes. Integer codes pass straight through to the reader, which is how
spacecraft and minor-planet kernels are queried (e.g. 20000433 for 433 Eros).
Summary
Types
One parsed SPK segment descriptor.
A body-to-center state from a kernel query.
A loaded SPK kernel handle.
Functions
The DAF internal file name recorded in the kernel header.
Load and parse an SPK/BSP ephemeris file into a handle.
Like load/1 but raises on failure.
Parse an SPK/BSP ephemeris kernel from an in-memory byte buffer.
Compute the position of target relative to observer at the given time.
Like position/4 but raises on failure.
The kernel's parsed segment descriptors, in DAF summary order.
Query the state of target relative to center at ephemeris epoch
et_seconds (TDB seconds past J2000).
Like state/4 but raises on failure.
Types
@type epoch() :: DateTime.t() | NaiveDateTime.t() | float() | integer()
@type load_error() :: {:file_error, File.posix()} | {:invalid_path, term()} | {:parse_error, term()}
@type position_error() :: state_error() | {:invalid_datetime, term()}
@type segment() :: %{ name: String.t(), target: integer(), center: integer(), frame: integer(), data_type: integer(), start_et: float(), stop_et: float(), start_address: integer(), end_address: integer() }
One parsed SPK segment descriptor.
@type state() :: %{ target: integer(), center: integer(), position_km: vec3(), velocity_km_s: vec3() | nil, frame: integer() }
A body-to-center state from a kernel query.
@type t() :: %Sidereon.Ephemeris{handle: reference()}
A loaded SPK kernel handle.
Functions
The DAF internal file name recorded in the kernel header.
@spec load(term()) :: {:ok, t()} | {:error, load_error()}
Load and parse an SPK/BSP ephemeris file into a handle.
The file is read and parsed exactly once; the returned handle holds the parsed
kernel and is passed to state/4, segments/1, and position/4. Returns
{:ok, ephemeris} or {:error, reason} when the file cannot be read or parsed.
Example
{:ok, eph} = Sidereon.Ephemeris.load("/path/to/de421.bsp")
Like load/1 but raises on failure.
Parse an SPK/BSP ephemeris kernel from an in-memory byte buffer.
Returns {:ok, ephemeris} or {:error, {:parse_error, reason}}.
Compute the position of target relative to observer at the given time.
A position-only convenience over state/4 that accepts a calendar epoch.
Returns {:ok, {x, y, z}} in km in the J2000/ICRF reference frame, or
{:error, reason}.
The target and observer are body atoms (see module docs) or NAIF integer
codes. The datetime can be a DateTime, a NaiveDateTime, or a Julian Date
(TDB) as a float.
Examples
{:ok, {x, y, z}} = Sidereon.Ephemeris.position(eph, :moon, :earth, datetime)
# Raw NAIF code (433 Eros) against a Horizons kernel:
{:ok, {x, y, z}} = Sidereon.Ephemeris.position(eph, 20_000_433, :sun, jd_tdb)
Like position/4 but raises on failure.
The kernel's parsed segment descriptors, in DAF summary order.
Each entry is a map with :name, :target, :center, :frame,
:data_type, :start_et, :stop_et, :start_address, and :end_address.
Coverage epochs (:start_et / :stop_et) are ephemeris (TDB) seconds past
J2000.
Query the state of target relative to center at ephemeris epoch
et_seconds (TDB seconds past J2000).
This is the primary query, matching the other language bindings: it resolves
and chains segments as needed and returns both position and velocity. Returns
{:ok, state} where state is a map with :target, :center,
:position_km (a {x, y, z} tuple, km), :velocity_km_s (a {vx, vy, vz}
tuple in km/s, or nil when the resolved path runs through a position-only
type-2 segment), and :frame (the NAIF reference-frame id, J2000/ICRF for
standard kernels).
target and center are body atoms (see module docs) or NAIF integer codes.
Returns {:error, {:invalid_body, body}} for an unknown atom,
{:error, {:unknown_body, code}} when a body is absent from the kernel,
{:error, {:no_segment_path, target, center}} when no segment chain connects
them, and {:error, {:nif_error, reason}} when a chain exists but none covers
the epoch or the path needs an unsupported segment type.
Example
{:ok, state} = Sidereon.Ephemeris.state(eph, :moon, :earth, 0.0)
Like state/4 but raises on failure.