API reference¶
orbit_formats
¶
orbit-formats: lossless round-trip across orbital state-vector and ephemeris formats.
The public surface lives here. It exposes the canonical metamodel — the typed dataclass
family downstream consumers adopt as the single format-agnostic representation — and the
read / write / convert / detect_format entry points, with format detection
and a registry the format readers and writers plug into as they land.
DEFAULT_UNITS
module-attribute
¶
DEFAULT_UNITS = UnitSpec()
The astro-tools canonical default units: kilometres, km/s, degrees, seconds.
Attitude
dataclass
¶
Attitude(
*,
metadata: Metadata,
source_native: FidelityModel | None = None,
attitude_type: str,
epochs: NDArray[datetime64],
records: NDArray[float64],
frame_a: str | None = None,
frame_b: str | None = None,
euler_rot_seq: str | None = None,
)
Bases: Canonical
An attitude history (CCSDS AEM) or single attitude (CCSDS APM).
attitude_type is one of :data:ATTITUDE_TYPES; records is an (N, k) array of
the attitude components (k fixed by the type), one row per epoch in epochs (N
rows — many for an AEM time series, one for an APM). frame_a and frame_b are the
two reference frames the rotation maps between (e.g. EME2000 → SC_BODY);
euler_rot_seq is the rotation sequence for the Euler representation (e.g. "321"),
None otherwise. Quaternion components are always stored scalar-last (Q1 Q2 Q3 QC).
columns
property
¶
The component column names for this attitude's representation.
to_dataframe
¶
Project the attitude time series to a DataFrame.
Columns Epoch (datetime64[ns]) + the attitude type's component columns
(float64) — Q1, Q2, Q3, QC for QUATERNION, ANGLE_1, ANGLE_2,
ANGLE_3 for EULER_ANGLE, SPIN_ALPHA, SPIN_DELTA, SPIN_ANGLE,
SPIN_ANGLE_VEL for SPIN — one row per epoch (many for an AEM history, one for
an APM). df.attrs carries the shared metadata spine (object_name /
central_body / time_scale / units / epoch_scales when known),
matching :meth:Ephemeris.to_dataframe, plus the attitude-specific attitude_type
and the frame_a / frame_b / euler_rot_seq tags when set. The frame pair
lives on these attrs, not the spine's single coordinate_system, so that key is
absent. No astropy objects leak — values are plain numpy.
Canonical
dataclass
¶
Canonical(
*,
metadata: Metadata,
source_native: FidelityModel | None = None,
)
Base for the federated canonical category types.
Carries the :class:Metadata spine and the optional source_native fidelity
handle. Equality compares canonical content (metadata + numeric payload) and ignores
source_native; instances are intentionally unhashable value objects.
Combined
dataclass
¶
Combined(
*,
metadata: Metadata,
source_native: FidelityModel | None = None,
messages: tuple[Canonical, ...],
message_id: str | None = None,
comments: tuple[str, ...] = (),
)
Bases: Canonical
A combined NDM: an ordered tuple of child canonical messages plus the wrapper header.
messages is the children in document order (within a message type; the XML wrapper
groups children by type, so a mixed aggregate is normalised to that grouping on write).
message_id and comments are the wrapper-level MESSAGE_ID and comments. Each
child carries its own metadata and source_native; the aggregate's metadata spine names
only the provenance. Equality is by wrapper header plus the child sequence (each compared by
its own canonical content), ignoring source_native like every other category type.
Conjunction
dataclass
¶
Conjunction(
*,
metadata: Metadata,
source_native: FidelityModel | None = None,
tca: datetime64,
miss_distance: float,
objects: tuple[ConjunctionObject, ConjunctionObject],
relative_speed: float | None = None,
relative_position: NDArray[float64] | None = None,
relative_velocity: NDArray[float64] | None = None,
)
Bases: Canonical
A two-object conjunction (CCSDS CDM): TCA, miss distance, relative state, two objects.
tca is the time of closest approach; miss_distance the separation at TCA in metres.
relative_speed (m/s), relative_position and relative_velocity (each a (3,)
RTN vector, metres and m/s) are present when the CDM carries the relative-state block, else
None. objects is the (OBJECT1, OBJECT2) pair. The metadata spine tags the
primary object and the originator; its time_scale is UTC (the CDM convention).
ConjunctionObject
dataclass
¶
ConjunctionObject(
label: str,
object_designator: str,
ref_frame: str,
state: NDArray[float64],
covariance: NDArray[float64],
object_name: str | None = None,
catalog_name: str | None = None,
international_designator: str | None = None,
)
One of the two objects in a conjunction: its identity, state, and RTN covariance.
label is the CDM slot ("OBJECT1" / "OBJECT2"); object_designator the
catalogue designator. state is the (6,) Cartesian state at TCA — X, Y, Z (km)
and X_DOT, Y_DOT, Z_DOT (km/s) — expressed in ref_frame; covariance is the
(6, 6) symmetric position/velocity covariance in the RTN frame (metre-based, axis
order R, T, N, Ṙ, Ṫ, Ṅ). The optional identity fields (object_name /
catalog_name / international_designator) are present when the CDM states them.
Ephemeris
dataclass
¶
Ephemeris(
*,
metadata: Metadata,
source_native: FidelityModel | None = None,
epochs: NDArray[datetime64],
positions: NDArray[float64],
velocities: NDArray[float64],
interpolation: str | None = None,
interpolation_degree: int | None = None,
maneuvers: tuple[Maneuver, ...] = (),
)
Bases: Canonical
A Cartesian state-vector time series.
epochs is a length-N datetime64[ns] array in the metadata's time scale;
positions and velocities are (N, 3) arrays in the length / speed units.
interpolation / interpolation_degree carry the source ephemeris's
interpolation hint. A multi-segment source (e.g. a multi-segment OEM) is concatenated
into one canonical ephemeris here; the per-segment detail is preserved on the
source_native fidelity model. maneuvers holds the burns an OCM states (empty for
any other source); each carries its own frame and Δv, and rides through the conversion
layer but is dropped — with a named warning — by a write to a format that cannot express
it.
to_dataframe
¶
Project to the gmat-run-identical state frame.
Columns Epoch (datetime64[ns]) + X, Y, Z, VX, VY, VZ (float64);
df.attrs carries object_name / central_body / coordinate_system /
time_scale (set when known), units, epoch_scales, and
interpolation / interpolation_degree when present. The projection is the
canonical edge form: provenance and the source_native handle live on the
object, not in the DataFrame. No astropy objects leak — values are plain numpy.
from_dataframe
classmethod
¶
from_dataframe(df: DataFrame) -> Ephemeris
Reconstruct an Ephemeris from a canonical state frame.
The inverse of :meth:to_dataframe over the projected fields — the round-trip
object -> to_dataframe -> from_dataframe reproduces the projected content
without drift. Also the construction path a producer (e.g. the GMAT-report
reader) uses to build an Ephemeris from a parsed table.
FidelityModel
¶
Bases: ABC
Base for a one-faithful-model-per-format representation.
A fidelity model holds every field a format defines, so a same-format write can
recover full fidelity from it and stay byte-lossless — it never down-projects.
Per-format models (the in-house KVN OEM record, the thin sgp4 element set, the GMAT
report table) live in their reader modules; this base only fixes the contract that
every model declares the format_name the lossy-conversion and writer layers key
their same-format-lossless logic off.
KeplerianElements
dataclass
¶
KeplerianElements(
semi_major_axis: float,
eccentricity: float,
inclination: float,
raan: float,
arg_periapsis: float,
true_anomaly: float,
)
Classical orbital elements at one epoch.
Angles (inclination / raan / arg_periapsis / true_anomaly) are in the
metadata's angle unit (degrees by default); semi_major_axis is in the length unit.
Conversion to and from Cartesian (given a gravitational parameter) is the conversion
layer's job — this type only holds the Keplerian representation.
Maneuver
dataclass
¶
Maneuver(
epoch_ignition: datetime64,
ref_frame: str,
duration: float = 0.0,
delta_v: NDArray[float64] | None = None,
delta_mass: float | None = None,
comments: tuple[str, ...] = (),
)
One maneuver (impulsive or finite) read from an OPM MAN_* block or an OCM man line.
epoch_ignition is the burn's ignition time and ref_frame the frame its delta_v is
expressed in (e.g. "RTN" / "EME2000") — the maneuver names its own frame rather than
borrowing the parent object's, since a burn is commonly given in RTN while the state is
inertial. duration is in seconds (0.0 ⇒ an impulsive maneuver). delta_v is the
(3,) Δv vector in km/s when the source provides it, else None (an OCM man block
that expresses, say, thrust but no Δv leaves it unset). delta_mass is the mass change in
kg (non-positive) when stated. comments are the block's leading COMMENT lines.
MeanElementSet
dataclass
¶
MeanElementSet(
*,
metadata: Metadata,
source_native: FidelityModel | None = None,
epoch: datetime64,
mean_motion: float,
eccentricity: float,
inclination: float,
raan: float,
arg_periapsis: float,
mean_anomaly: float,
bstar: float | None = None,
mean_motion_dot: float | None = None,
mean_motion_ddot: float | None = None,
mean_element_theory: str | None = None,
)
Bases: Canonical
A mean-element set (TLE / SGP4 or CCSDS OMM): mean elements at an epoch.
These are mean — not osculating — elements; turning them into an osculating state
is a propagation (an sgp4 model step), out of scope for the format layer. mean_motion
is in revolutions/day and the angles are in degrees (the TLE/OMM convention).
bstar and the mean-motion derivatives are the optional SGP4 drag terms; a pure
Keplerian mean set leaves them None. TLE-text specifics (line numbers, checksums,
classification) live on the source_native fidelity model, and the NORAD id rides
metadata.object_id.
mean_element_theory records which mean-element theory produced the elements —
:data:SGP4_MEAN_ELEMENT_THEORY for a TLE / OMM, :data:BROADCAST_MEAN_ELEMENT_THEORY
for a RINEX broadcast record, None when unknown. It is a semantic provenance tag, not
part of the numeric payload: it is excluded from equality and from the DataFrame
projection (like source_native and the off-spine metadata), and it gates conversion
to the SGP4 mean-element formats via :func:ensure_convertible_to_mean_format.
to_dataframe
¶
Project to a one-row mean-element frame, with the metadata spine on attrs.
from_dataframe
classmethod
¶
from_dataframe(df: DataFrame) -> MeanElementSet
Reconstruct a MeanElementSet from a one-row mean-element frame.
Metadata
dataclass
¶
Metadata(
object_name: str | None = None,
object_id: str | None = None,
originator: str | None = None,
reference_frame: str | None = None,
central_body: str | None = None,
time_scale: str | None = None,
units: UnitSpec = DEFAULT_UNITS,
provenance: Provenance | None = None,
)
The typed, validated tags every canonical object carries on the object itself.
A shared spine across the federated category types: object identity (object_name
/ object_id / originator), reference frame, central body, time scale, the
units the numeric fields use, and provenance. reference_frame is always tagged
and preserved; a rotation to an unsupported frame, or one requested from a canonical
form with no Cartesian state, errors rather than guessing.
Provenance
dataclass
¶
Provenance(
source_format: str | None = None,
creation_date: str | None = None,
header: str | None = None,
)
Where a canonical object came from — recorded, never reconstructed.
orbit_formats records what a source states; it does not infer the dynamics,
perturbations, or maneuvers that produced a trajectory (force-model attribution is
explicitly out of scope).
StateVector
dataclass
¶
StateVector(
*,
metadata: Metadata,
source_native: FidelityModel | None = None,
epoch: datetime64,
position: NDArray[float64],
velocity: NDArray[float64],
keplerian: KeplerianElements | None = None,
maneuvers: tuple[Maneuver, ...] = (),
)
Bases: Canonical
A single Cartesian state at one epoch, with optional Keplerian elements.
position and velocity are length-3 arrays in the metadata's length / speed
units; keplerian is an optional parallel representation populated by the
conversion layer. maneuvers holds the burns an OPM states (empty for any other
source); each carries its own frame and Δv, and rides through the conversion layer
but is dropped — with a named warning — by a write to a format that cannot express it.
:meth:to_dataframe projects to the one-row canonical state frame.
to_dataframe
¶
Project to a one-row state frame (Epoch + X, Y, Z, VX, VY, VZ).
Same schema and attrs as :meth:Ephemeris.to_dataframe, with a single row.
from_dataframe
classmethod
¶
from_dataframe(df: DataFrame) -> StateVector
Reconstruct a StateVector from a one-row canonical state frame.
Tracking
dataclass
¶
Tracking(
*,
metadata: Metadata,
source_native: FidelityModel | None = None,
participants: tuple[str, ...],
observations: tuple[TrackingObservation, ...],
)
Bases: Canonical
A tracking-data set (CCSDS TDM): its participants and a flat observation sequence.
participants is the ordered tuple of tracking participants (the TDM PARTICIPANT_1 …
PARTICIPANT_5 — ground stations and the tracked spacecraft); observations is the
full sequence of :class:TrackingObservation triples, in file order, concatenated across
every segment of a multi-segment message. The metadata spine carries the originator and the
time scale; everything format-specific rides on source_native.
TrackingObservation
dataclass
¶
One tracking observation: its measurement type, epoch, and scalar value.
observation_type is the CCSDS TDM observation keyword (e.g. "RANGE",
"DOPPLER_INSTANTANEOUS", "ANGLE_1"); epoch is the measurement time; value is
the scalar reading in the units the segment's metadata declares (range units, angle type,
and so on live on the source_native model, not on each observation).
ConversionCapability
dataclass
¶
ConversionCapability(
source_format: str,
target_format: str,
source_form: str,
target_form: str,
kind: ConversionKind,
)
The classification of one (source_format, target_format) conversion.
ConversionKind
¶
Bases: Enum
How a (source, target) format pair converts — the structural classification.
The first three are supported; the last three are unsupported and name why. See
:attr:ConversionCapability.supported.
AmbiguousFormatError
¶
Bases: FormatDetectionError
More than one signature matched and no tiebreaker resolved it.
candidates holds the matching format ids so a caller can disambiguate by passing
an explicit format=.
FormatDetectionError
¶
Bases: OrbitFormatsError
Auto-detection could not settle on a single format.
FrameRotationUnsupportedError
¶
Bases: OrbitFormatsError
A requested frame rotation could not be performed.
This is raised either because one side names a reference frame outside the
supported set, or because the canonical form carries no Cartesian state to
rotate (mean elements, for instance, would first require a propagation).
source_frame and target_frame name the two frames involved.
IncompatibleMeanElementTheoryError
¶
Bases: UnsupportedConversionError
A mean-element set whose theory the target mean-element format cannot represent.
Source and target share the mean-elements canonical form, so the form-keyed graph
would pass the object straight through — but the theory behind the elements differs.
A GNSS broadcast-navigation set (quasi-Keplerian parameters referenced to the time of
ephemeris in an Earth-fixed datum, evaluated by the constellation's user algorithm) is
not an SGP4/TEME mean-element set: writing it as a TLE or OMM would relabel numbers that
mean different things. A faithful conversion would have to propagate the broadcast model
and refit SGP4 elements — a propagation plus an orbit fit, both out of scope — so the
conversion is refused rather than producing a syntactically valid but physically wrong
message. source_theory is the source set's theory; target_format the refused
target. Subclasses :class:UnsupportedConversionError, so an existing
except UnsupportedConversionError still catches it.
MalformedSourceError
¶
Bases: OrbitFormatsError
Recognised as a known format, but its content could not be parsed.
The format id is settled (detected or supplied) — this is not a detection failure —
but the bytes are broken for that format: a TLE with an invalid checksum, a record
missing a required line, internally inconsistent fields. Distinct from
:class:UnsupportedFormatError (the format is fine, the operation is unavailable).
MissingOptionalDependencyError
¶
Bases: OrbitFormatsError
A feature behind an optional extra was used without its dependency installed.
orbit-formats keeps heavy or niche backends behind optional extras so the base install
stays lightweight and fully permissive. Reaching for such a feature — SPK read/write,
which needs spiceypy from the [spk] extra — without the extra installed raises
this, naming the pip install that resolves it. dependency is the missing import
name and extra the extra that provides it.
OrbitFormatsError
¶
Bases: Exception
Base class for every error orbit-formats raises deliberately.
UnknownFormatError
¶
Bases: FormatDetectionError
No signature matched, or an explicitly requested format id is not recognised.
Covers both "I read the bytes and nothing matched" and "you passed format='foo'
and 'foo' is not a format I know".
UnsupportedConversionError
¶
Bases: OrbitFormatsError
No available conversion from a source object to the requested target format.
source_form is the source object's canonical form, target_format the
requested format id, and target_form the canonical form that format expects.
UnsupportedFormatError
¶
Bases: OrbitFormatsError
A recognised format whose requested operation is not available.
Raised when no reader or writer is registered for an otherwise-known format, or when a write targets a read-only format.
Source
dataclass
¶
A resolved input: raw bytes, plus the origin path/name when one was given.
data is the (possibly prefix-truncated, for detection) raw content. path is
set only when the input was a filesystem path; name is a display name used for
extension hints and error messages. Readers consume a Source rather than a raw
path so the same reader works for a file and an in-memory buffer.
retain is the caller's opt-in (via :func:~orbit_formats.read's
retain_source) to keep the raw bytes on the fidelity model for a verbatim,
byte-identical same-format re-emit. It defaults to False so an ordinary read holds
no extra copy; a reader that supports verbatim retention (e.g. the OEM reader) consults
this flag to decide whether to stash the source bytes.
UnitSpec
dataclass
¶
The physical units a canonical object's numeric fields are expressed in.
Plain unit strings — not astropy objects — so the canonical schema stays
astropy-free on the way out. length applies to position components, speed to
velocity components, angle to orbital angles, and time to the time base of
rates and durations.
DroppedField
dataclass
¶
One piece of information a conversion could not carry, and why.
name is the field that was lost or approximated; reason says why the target
could not hold it. The pair is the structured unit every lossy warning is built from.
DroppedFieldWarning
¶
Bases: LossyConversionWarning
A field the target format structurally cannot represent.
The value is dropped outright, not approximated — the archetype is a covariance matrix written to a format with no covariance block.
LossyConversionWarning
¶
LossyConversionWarning(
message: str, *, dropped: Sequence[DroppedField]
)
Bases: UserWarning
Base for every warning a lossy conversion emits — catch this for the whole family.
Carries the dropped information as structured :class:DroppedField records on
dropped. A lossy warning that names nothing is a contradiction in terms, so
construction requires at least one dropped field.
MissingFieldWarning
¶
Bases: LossyConversionWarning
A canonical field the source did not provide, filled with NaN rather than fabricated.
The canonical form has a slot the source format left unpopulated — the archetype is a
GMAT report (or an SP3 file) that lists position but no velocity. The slot is filled
with NaN, never a guessed value, and this warning names exactly which fields are absent
so a caller can decide whether the gap matters. Distinct from
:class:DroppedFieldWarning, where the target cannot hold a value the source had;
here the source omitted a value the canonical form has room for.
ModelApproximationWarning
¶
ModelApproximationWarning(
*,
source_kind: str,
target_kind: str,
fields: Sequence[str],
model: str | None = None,
)
Bases: LossyConversionWarning
A cross-category conversion that introduces a model step.
Projecting mean elements (a TLE / OMM) toward a state crosses the mean-element semantics: the result is model-dependent, not an exact restatement of the source.
PrecisionLossWarning
¶
Bases: LossyConversionWarning
A value narrowed to fit a target format's field width or numeric precision.
The value survives but some digits do not — e.g. an epoch written to a TLE's fixed column width.
read
¶
read(
source: SourceInput,
*,
format: str | None = None,
retain_source: bool = False,
) -> Canonical
Read source into the appropriate canonical subtype.
source is a path or in-memory buffer; an explicit format= overrides detection.
Raises :class:~orbit_formats.errors.UnsupportedFormatError if the resolved format
has no registered reader, and the detection errors otherwise (see
:func:~orbit_formats.detect.detect_format).
Set retain_source=True to keep the raw source bytes on the result's
source_native fidelity model, so a later :func:write back to the same format
reproduces the input byte-for-byte. It defaults to False — an ordinary read
holds no extra copy, and a same-format write is then content-lossless (every field
preserved, canonically formatted) rather than byte-identical. Only formats with a
verbatim-capable fidelity model (the OEM reader today) honour it.
write
¶
write(
obj: Canonical,
destination: str | PathLike[str],
*,
format: str | None = None,
) -> None
Write obj to destination in the given (or extension-inferred) format.
The target format comes from an explicit format= or, failing that, the
destination's extension. For a format with more than one notation — CCSDS OEM in KVN
vs XML — the destination extension also selects the notation (.oem → KVN, .xml
→ XML); since .xml does not name a single NDM message, writing one needs an explicit
format= (e.g. write(eph, "sat.xml", format="ccsds-oem")). Raises
:class:~orbit_formats.errors.UnsupportedFormatError for a read-only target or one
with no registered writer, and :class:~orbit_formats.errors.UnknownFormatError if
no format can be resolved.
capability_matrix
¶
capability_matrix() -> tuple[ConversionCapability, ...]
The capability of every (source, writable target) pair, in catalog order.
Rows range over every known format (anything readable can be a source); columns over the
writable formats (only those can be a conversion destination). This is the authoritative
table docs/conversion-matrix.md documents and the doc-parity test asserts against.
conversion_capability
¶
conversion_capability(
source_format: str, target_format: str
) -> ConversionCapability
Classify converting source_format to target_format (both format ids).
Returns the structural :class:ConversionKind and a :attr:~ConversionCapability.reason,
derived from the canonical form each format prefers, the registered cross-form edges, and the
mean-element theory rule — the same facts :func:orbit_formats.convert.graph.route and
:func:orbit_formats.api.convert act on. Raises
:class:~orbit_formats.errors.UnknownFormatError for an unknown format id.
The result describes the routing a :func:~orbit_formats.api.convert call would take; it
does not consider whether the target is writable (that is a :func:~orbit_formats.api.write
concern), so a read-only target still classifies by form. :func:capability_matrix lists only
writable targets, the ones that are conversion destinations.
detect_format
¶
Return the format id of source (a path or in-memory buffer).
Reads a header prefix and resolves the format from its content signature, falling back
to the file extension. Raises :class:~orbit_formats.errors.UnknownFormatError if
nothing matches and :class:~orbit_formats.errors.AmbiguousFormatError if several
signatures tie. To force a known format instead of detecting, pass format= to
:func:~orbit_formats.read (etc.), which validate it via
:func:~orbit_formats.formats.normalize_format.
normalize_format
¶
Validate and canonicalise an explicit format id (lowercased, trimmed).
This is the counterpart to detection: where :func:orbit_formats.detect.detect_format
works out what a source is, this validates a format id a caller already supplied. A notation
synonym ("3le" for "tle") resolves to its catalog id. Raises
:class:~orbit_formats.errors.UnknownFormatError if it is not a known format.
register_reader
¶
Register reader as the reader for format_id (must be a catalogued format).
register_writer
¶
Register writer for format_id (must be a catalogued, writable format).
warn_lossy
¶
warn_lossy(
warning: LossyConversionWarning, *, stacklevel: int = 1
) -> None
Emit warning through the standard warnings machinery — the one sanctioned seam.
Every converter and writer that drops information routes through here, so the loss is
always catchable as a :class:LossyConversionWarning and the no-silent-loss contract
has a single point to assert against. stacklevel counts frames above the caller of
:func:warn_lossy, so the warning is attributed to the conversion, not to this helper.