Trajectory Interchange: OEM and SPK Workflows¶
This page explains why OEM and SPK exist, when to use each format, and how to
generate them from adam_core state histories.
Format Background¶
OEM (CCSDS Orbit Ephemeris Message): human-readable interchange format for timestamped Cartesian state vectors (and optional covariance), useful for auditability and cross-tool exchange.
SPK (NAIF/SPICE kernel): binary ephemeris format optimized for SPICE ecosystems and operational mission tooling.
In adam_core, both formats are generated from Cartesian state histories
represented as Orbits rows over time.
Input Model: Orbit Seed vs Ephemeris State History¶
You can start from:
a seed orbit + propagation window, or
an already propagated state history (often called an ephemeris trajectory).
If your “ephemeris set” is already Cartesian states through time, convert that
table into Orbits and export directly.
Generate OEM from a Pre-Propagated State History¶
from adam_core.orbits import Orbits
from adam_core.orbits.oem_io import orbit_to_oem
# `propagated_orbits` must represent one object_id with multiple epochs.
propagated_orbits: Orbits = propagated_orbits.sort_by("coordinates.time")
oem_path: str = orbit_to_oem(
orbits=propagated_orbits,
output_file="apophis.oem",
originator="ADAM CORE USER",
)
print(oem_path)
Generate OEM from a Seed Orbit (Propagation Included)¶
import numpy as np
from adam_assist import ASSISTPropagator
from adam_core.orbits import Orbits
from adam_core.orbits.oem_io import orbit_to_oem_propagated
from adam_core.time import Timestamp
seed_orbit: Orbits = seed_orbit
times: Timestamp = Timestamp.from_mjd(np.arange(60200.0, 60210.0, 1.0), scale="tdb")
oem_path: str = orbit_to_oem_propagated(
orbits=seed_orbit,
output_file="apophis_propagated.oem",
times=times,
propagator_klass=ASSISTPropagator,
originator="ADAM CORE USER",
)
Read OEM Back into Orbits¶
from adam_core.orbits import Orbits
from adam_core.orbits.oem_io import orbit_from_oem
loaded_orbits: Orbits = orbit_from_oem("apophis_propagated.oem")
print(len(loaded_orbits))
Generate SPK from Orbits¶
orbits_to_spk can propagate internally if you pass propagator=....
For production products, use a high-fidelity propagator such as
adam_assist.ASSISTPropagator.
from adam_assist import ASSISTPropagator
from adam_core.orbits import Orbits
from adam_core.orbits.spice_kernel import orbits_to_spk
from adam_core.time import Timestamp
seed_orbits: Orbits = seed_orbits
start_time: Timestamp = Timestamp.from_iso8601(["2028-01-01T00:00:00"], scale="tdb")
end_time: Timestamp = Timestamp.from_iso8601(["2028-06-01T00:00:00"], scale="tdb")
target_id_map: dict[str, int] = orbits_to_spk(
orbits=seed_orbits,
output_file="objects.bsp",
start_time=start_time,
end_time=end_time,
propagator=ASSISTPropagator(),
step_days=0.25,
window_days=32.0,
kernel_type="w03",
max_processes=8,
)
print(target_id_map)
From Ephemeris-Like State Tables to OEM/SPK¶
If you already have Cartesian state rows (for example from an internal
trajectory service), build Orbits and export.
from adam_core.coordinates.cartesian import CartesianCoordinates
from adam_core.orbits import Orbits
state_history: CartesianCoordinates = state_history
export_orbits: Orbits = Orbits.from_kwargs(
orbit_id=["traj-001"] * len(state_history),
object_id=["Apophis"] * len(state_history),
coordinates=state_history,
)
# Then reuse orbit_to_oem(...) or orbits_to_spk(...).
Load Custom SPKs for Observer/Ephemeris Workflows¶
Custom kernels (for example JWST or self-generated .bsp files) can be
registered and then used by observer/ephemeris workflows.
from adam_core.observers import Observers
from adam_core.time import Timestamp
from adam_core.utils.spice import register_spice_kernel, unregister_spice_kernel
times: Timestamp = Timestamp.from_mjd([60200.0, 60200.25], scale="tdb")
register_spice_kernel("objects.bsp")
# If a SPICE body name is present (e.g. "JWST"), observer lookup can use it directly.
custom_observers: Observers = Observers.from_code("JWST", times)
# Ephemeris generation can now use these observers.
# ephemeris = propagator.generate_ephemeris(orbits, custom_observers, ...)
unregister_spice_kernel("objects.bsp")
How These Products Are Used¶
OEM: reviewable trajectory exchange, validation artifacts, and handoff between teams/tools that prefer text standards.
SPK: mission operations, SPICE-native analysis, OpenSpace pipelines, and external tools that consume NAIF kernels directly.
Practical Notes¶
OEM export requires one
object_idper file call and benefits from multi-epoch state history.SPK export uses NAIF target IDs mapped per orbit via
target_id_map.For decision-grade trajectories, propagation quality is dominated by the propagator backend and force model choices.