Skip to content

Schemas

The schemas module holds wire-format message types: the catalog/observation interchange shapes and the CCSDS Navigation Data Messages.

CCSDS Navigation Data Messages

keplime parses and serializes three CCSDS message types in both the KVN (KEY = value) and XML (NDM/XML) encodings, mapping each to and from keplime's native types:

  • OMM — Orbit Mean-Elements Message (CCSDS 502.0-B). Round-trips losslessly to a TLE via OMM.to_tle / OMM.from_tle. Carries the SGP4 / SGP4-XP mean elements and catalog metadata (OMMMetadata, OMMMeanElements, OMMTLEParameters).
  • OEM — Orbit Ephemeris Message (CCSDS 502.0-B). Maps one Ephemeris per segment via OEM.to_ephemerides / OEM.from_ephemeris, with optional covariance records (OEMSegment, OEMMetadata, OEMCovarianceRecord).
  • CDM — Conjunction Data Message (CCSDS 508.0-B). CDM.from_conjunction builds a standard-conformant message from a computed encounter (miss distance, RTN relative state, RTN object covariances); the object metadata (CDMObjectData, CDMRelativeMetadata, CDMODParameters, CDMAdditionalParameters) is then filled in before emit.

Each message exposes a uniform codec surface:

Method Purpose
from_kvn(text) Parse the KVN encoding.
from_xml(text) Parse the XML (NDM/XML) encoding.
from_str_any(text) Parse either encoding, sniffing XML versus KVN.
from_file(path) Read a file, sniffing the encoding by content.
to_kvn() Serialize to KVN.
to_xml() Serialize to XML.

Units

In-memory structs hold keplime-native units (angles in radians, lengths in km); CCSDS unit conversion (degrees, meters) happens only at the read/write boundary. The shared ODM header is NDMHeader; CDM carries its own CDMHeader and uses UTC for all epochs.

The CCSDS enums — MeanElementTheory, InterpolationMethod, CDMManeuverable, CDMCovarianceMethod — are documented under Enums.

API reference

schemas

CDM

A CCSDS Conjunction Data Message.

Build the emit path with from_conjunction, customize the object metadata, then to_kvn. Parse external CDMs with from_kvn / from_file.

Attributes:

Name Type Description
header CDMHeader

CDM header.

relative_metadata CDMRelativeMetadata

Conjunction geometry and Pc.

object1 CDMObjectData

Object 1 data.

object2 CDMObjectData

Object 2 data.

header property

CDM header.

from_conjunction(header, tca, state1, state2, covariance1, covariance2, collision_probability=None) staticmethod

Builds a CDM from a computed conjunction (the emit path).

Both states must be at TCA in the same inertial frame. The miss distance, relative speed, and object-1-RTN relative position/velocity are computed; each covariance is converted to RTN against its own object's state. Object metadata is filled with neutral defaults.

Parameters:

Name Type Description Default
header CDMHeader

The CDM header.

required
tca ModifiedJulianDate

Time of closest approach [UTC].

required
state1 CartesianState

Object 1 state at TCA [inertial; km, km/s].

required
state2 CartesianState

Object 2 state at TCA [same frame].

required
covariance1 CovarianceMatrix

Object 1 covariance.

required
covariance2 CovarianceMatrix

Object 2 covariance.

required
collision_probability float | None

Optional Pc to record.

None

Returns:

Type Description
CDM

A CDM ready for to_kvn.

Raises:

Type Description
ValueError

If the states are not in the same frame, a state lacks a velocity, a covariance cannot be rotated to RTN, or the frame has no CDM label.

from_file(path) staticmethod

Reads a CDM from a KVN file.

Parameters:

Name Type Description Default
path str

Path to a KVN CDM file.

required

Returns:

Type Description
CDM

The parsed CDM.

Raises:

Type Description
ValueError

If the file cannot be read or fails to parse.

from_kvn(text) staticmethod

Parses a CDM from its KVN text encoding.

Parameters:

Name Type Description Default
text str

The CDM KVN document.

required

Returns:

Type Description
CDM

The parsed CDM.

Raises:

Type Description
ValueError

On any syntax error, missing required keyword, bad epoch, unsupported frame, or inconsistent covariance.

from_str_any(text) staticmethod

Parses a CDM from either encoding, sniffing XML versus KVN.

Parameters:

Name Type Description Default
text str

A CDM in XML or KVN.

required

Returns:

Type Description
CDM

The parsed CDM.

Raises:

Type Description
ValueError

On any parse failure.

from_xml(text) staticmethod

Parses a CDM from its XML (NDM/XML) encoding.

Parameters:

Name Type Description Default
text str

The CDM XML document.

required

Returns:

Type Description
CDM

The parsed CDM.

Raises:

Type Description
ValueError

On malformed XML or any parse-contract violation.

to_kvn()

Serializes this CDM to its KVN text encoding.

Returns:

Type Description
str

The CDM as a KVN string (trailing newline).

Raises:

Type Description
ValueError

If an object's state frame has no CCSDS emit label or a covariance is not at least 6x6.

to_xml()

Serializes this CDM to its XML (NDM/XML) encoding.

Returns:

Type Description
str

The CDM as an XML string (with prolog, trailing newline).

Raises:

Type Description
ValueError

If an object's state frame has no CCSDS emit label or a covariance is not at least 6x6.

CDMAdditionalParameters

CDM additional object-property parameters block (all optional; m-based).

Attributes:

Name Type Description
area_pc float | None

AREA_PC [m**2].

cd_area_over_mass float | None

CD_AREA_OVER_MASS [m**2/kg].

cr_area_over_mass float | None

CR_AREA_OVER_MASS [m**2/kg].

thrust_acceleration float | None

THRUST_ACCELERATION [m/s**2].

sedr float | None

SEDR [W/kg].

__init__()

Creates an empty additional-parameters block; set fields as needed.

CDMHeader

CDM header block (all CDM epochs are UTC).

Attributes:

Name Type Description
comments list[str]

COMMENT lines.

message_version str

CCSDS_CDM_VERS (e.g. "1.0").

creation_date ModifiedJulianDate

CREATION_DATE [UTC].

originator str

ORIGINATOR.

message_for str | None

MESSAGE_FOR.

message_id str

MESSAGE_ID.

creation_date property

CREATION_DATE [UTC].

message_version property

CCSDS_CDM_VERS value, verbatim (e.g. "1.0").

__init__(message_version, creation_date, originator, message_id, comments=..., message_for=None)

Creates a CDM header.

Parameters:

Name Type Description Default
message_version str

CCSDS_CDM_VERS ("1.0").

required
creation_date ModifiedJulianDate

CREATION_DATE [UTC].

required
originator str

ORIGINATOR.

required
message_id str

MESSAGE_ID.

required
comments list[str]

Optional header comments (default empty).

...
message_for str | None

Optional MESSAGE_FOR.

None

CDMODParameters

CDM orbit-determination parameters block (all optional).

Attributes:

Name Type Description
time_lastob_start ModifiedJulianDate | None

TIME_LASTOB_START.

time_lastob_end ModifiedJulianDate | None

TIME_LASTOB_END.

recommended_od_span float | None

RECOMMENDED_OD_SPAN [days].

actual_od_span float | None

ACTUAL_OD_SPAN [days].

obs_available int | None

OBS_AVAILABLE.

obs_used int | None

OBS_USED.

tracks_available int | None

TRACKS_AVAILABLE.

tracks_used int | None

TRACKS_USED.

residuals_accepted float | None

RESIDUALS_ACCEPTED [%].

weighted_rms float | None

WEIGHTED_RMS.

__init__()

Creates an empty OD-parameters block; set fields as needed.

CDMObjectData

CDM per-object data: metadata, OD/additional parameters, state, covariance.

The state, covariance, and reference frame are set by CDM.from_conjunction; the metadata fields are settable.

Attributes:

Name Type Description
object_designator str

OBJECT_DESIGNATOR.

catalog_name str

CATALOG_NAME.

object_name str

OBJECT_NAME.

international_designator str

INTERNATIONAL_DESIGNATOR.

object_type str | None

OBJECT_TYPE.

operator_organization str | None

OPERATOR_ORGANIZATION.

ephemeris_name str

EPHEMERIS_NAME.

covariance_method CDMCovarianceMethod

COVARIANCE_METHOD.

maneuverable CDMManeuverable

MANEUVERABLE.

reference_frame ReferenceFrame

REF_FRAME of the state.

od_parameters CDMODParameters | None

OD-parameters block.

additional_parameters CDMAdditionalParameters | None

Additional block.

state CartesianState

State at TCA [km, km/s].

covariance CovarianceMatrix

RTN (Relative) covariance [km**2].

covariance property

RTN (Relative) covariance [km**2].

reference_frame property

REF_FRAME of the state.

state property

State at TCA [km, km/s].

CDMRelativeMetadata

CDM relative metadata: the conjunction geometry and Pc.

The geometry fields are computed by CDM.from_conjunction; the screening and Pc-method fields are settable.

Attributes:

Name Type Description
comments list[str]

COMMENT lines.

tca ModifiedJulianDate

TCA [UTC].

miss_distance float

MISS_DISTANCE [km].

relative_speed float | None

RELATIVE_SPEED [km/s].

collision_probability float | None

COLLISION_PROBABILITY.

collision_probability_method str | None

COLLISION_PROBABILITY_METHOD.

miss_distance property

MISS_DISTANCE [km].

relative_speed property

RELATIVE_SPEED [km/s], or None.

tca property

TCA [UTC].

NDMHeader

Common ODM header block shared by OEM and OMM messages.

CDM carries its own header. Construct one to supply to OMM.from_tle. The ODM v3 optional fields (message_id, classification) are None for v1/v2 messages.

Attributes:

Name Type Description
comments list[str]

COMMENT lines at the top of the message.

message_version str

CCSDS_<MSG>_VERS value, verbatim (e.g. "2.0").

creation_date ModifiedJulianDate

CREATION_DATE [UTC].

originator str

ORIGINATOR.

message_id str | None

MESSAGE_ID (ODM v3).

classification str | None

CLASSIFICATION (ODM v3).

__init__(message_version, creation_date, originator, comments=..., message_id=None, classification=None)

Creates an ODM header.

Parameters:

Name Type Description Default
message_version str

CCSDS_<MSG>_VERS value (e.g. "2.0").

required
creation_date ModifiedJulianDate

CREATION_DATE [UTC].

required
originator str

ORIGINATOR.

required
comments list[str]

Optional header comments (default empty).

...
message_id str | None

Optional MESSAGE_ID (ODM v3).

None
classification str | None

Optional CLASSIFICATION (ODM v3).

None

OEM

A CCSDS Orbit Ephemeris Message.

One or more segments, each a tabulated trajectory. Parse with from_kvn / from_file, build from an Ephemeris with from_ephemeris, and map each segment back to an Ephemeris with to_ephemerides.

Attributes:

Name Type Description
header NDMHeader

Common ODM header.

segments list[OEMSegment]

One or more ephemeris segments.

header property

Common ODM header.

segments property

One or more ephemeris segments.

covariance_at_epoch(epoch)

Returns the covariance record nearest at-or-before epoch.

Parameters:

Name Type Description Default
epoch ModifiedJulianDate

The query epoch.

required

Returns:

Type Description
OEMCovarianceRecord | None

The nearest record at-or-before epoch, or None if every

OEMCovarianceRecord | None

record is after it.

from_ephemeris(ephemeris, frame, time_system, header, object_id) staticmethod

Builds a single-segment OEM from an Ephemeris.

States are emitted verbatim, each converted from the ephemeris's native TEME to frame (ITRF EOP-guarded). Acceleration columns are emitted iff every state carries acceleration.

Parameters:

Name Type Description Default
ephemeris Ephemeris

The source ephemeris (TEME).

required
frame ReferenceFrame

Frame to express the emitted states in.

required
time_system TimeSystem

Time system to tag the emitted epochs with.

required
header NDMHeader

The ODM header to attach.

required
object_id str

The international designator for OBJECT_ID.

required

Returns:

Type Description
OEM

A single-segment OEM.

Raises:

Type Description
ValueError

If the ephemeris has no states, frame has no CCSDS label, or a state cannot be converted to frame.

from_file(path) staticmethod

Reads an OEM from a KVN file.

Parameters:

Name Type Description Default
path str

Path to a KVN OEM file.

required

Returns:

Type Description
OEM

The parsed OEM.

Raises:

Type Description
ValueError

If the file cannot be read or fails to parse.

from_kvn(text) staticmethod

Parses an OEM from its KVN text encoding.

Parameters:

Name Type Description Default
text str

The OEM KVN document.

required

Returns:

Type Description
OEM

The parsed OEM.

Raises:

Type Description
ValueError

On any syntax error, missing required keyword, bad epoch or state row, or unsupported frame / time system.

from_str_any(text) staticmethod

Parses an OEM from either encoding, sniffing XML versus KVN.

Parameters:

Name Type Description Default
text str

An OEM in XML or KVN.

required

Returns:

Type Description
OEM

The parsed OEM.

Raises:

Type Description
ValueError

On any parse failure.

from_xml(text) staticmethod

Parses an OEM from its XML (NDM/XML) encoding.

Parameters:

Name Type Description Default
text str

The OEM XML document.

required

Returns:

Type Description
OEM

The parsed OEM.

Raises:

Type Description
ValueError

On malformed XML or any parse-contract violation.

to_ephemerides()

Maps each segment to an Ephemeris (states converted to TEME).

Returns:

Type Description
list[Ephemeris]

One Ephemeris per segment, in order.

Raises:

Type Description
ValueError

If a segment's center is not EARTH, a state cannot be converted to TEME (including ITRF epochs outside EOP coverage), or a segment has no states.

to_kvn()

Serializes this OEM to its KVN text encoding.

Returns:

Type Description
str

The OEM as a KVN string (trailing newline).

Raises:

Type Description
ValueError

If a segment or covariance frame has no CCSDS emit label.

to_xml()

Serializes this OEM to its XML (NDM/XML) encoding.

Returns:

Type Description
str

The OEM as an XML string (with prolog, trailing newline).

Raises:

Type Description
ValueError

If a segment or covariance frame has no CCSDS emit label.

OEMCovarianceRecord

One OEM covariance record: a 6x6 covariance at an epoch.

Attributes:

Name Type Description
epoch ModifiedJulianDate

The epoch the covariance applies at.

covariance CovarianceMatrix

The 6x6 covariance [km-based units].

covariance property

The 6x6 covariance [km-based units].

epoch property

The epoch the covariance applies at.

OEMMetadata

OEM segment metadata block.

Attributes:

Name Type Description
comments list[str]

COMMENT lines in the segment.

object_name str

OBJECT_NAME.

object_id str

OBJECT_ID.

center_name str

CENTER_NAME; only EARTH maps to an Ephemeris.

reference_frame ReferenceFrame

REF_FRAME of the states.

ref_frame_epoch ModifiedJulianDate | None

REF_FRAME_EPOCH.

time_system TimeSystem

TIME_SYSTEM.

start_time ModifiedJulianDate

START_TIME.

useable_start_time ModifiedJulianDate | None

USEABLE_START_TIME.

useable_stop_time ModifiedJulianDate | None

USEABLE_STOP_TIME.

stop_time ModifiedJulianDate

STOP_TIME.

interpolation tuple[InterpolationMethod, int] | None

INTERPOLATION method and INTERPOLATION_DEGREE.

center_name property

CENTER_NAME; only EARTH maps to an Ephemeris.

comments property

COMMENT lines in the segment.

interpolation property

INTERPOLATION method and INTERPOLATION_DEGREE, or None.

object_id property

OBJECT_ID.

object_name property

OBJECT_NAME.

ref_frame_epoch property

REF_FRAME_EPOCH, or None.

reference_frame property

REF_FRAME of the states.

start_time property

START_TIME.

stop_time property

STOP_TIME.

time_system property

TIME_SYSTEM.

useable_start_time property

USEABLE_START_TIME, or None.

useable_stop_time property

USEABLE_STOP_TIME, or None.

OEMSegment

One OEM segment: metadata, tabulated states, and covariance records.

Attributes:

Name Type Description
metadata OEMMetadata

Segment metadata.

states list[CartesianState]

Tabulated states in the segment's frame.

covariance_records list[OEMCovarianceRecord]

Covariance records.

covariance_records property

Covariance records, in epoch order.

metadata property

Segment metadata.

states property

Tabulated states in the segment's frame [km, km/s].

OMM

A CCSDS Orbit Mean-Elements Message.

Carries one set of mean orbital elements (the SGP4 elements a TLE encodes) plus catalog metadata. Parse with from_kvn / from_file, build from a TLE with from_tle, and convert back with to_tle. Angles in the mean-element block are radians; MEAN_MOTION stays in rev/day.

Attributes:

Name Type Description
header NDMHeader

Common ODM header.

metadata OMMMetadata

Object identity, frame, time system, theory.

mean_elements OMMMeanElements

Mean orbital elements at epoch.

tle_parameters OMMTLEParameters | None

SGP4/SGP4-XP catalog and force-model parameters, when present.

covariance CovarianceMatrix | None

Optional ODM covariance block.

user_defined list[tuple[str, str]]

USER_DEFINED_* keys preserved verbatim.

covariance property

Optional ODM covariance block.

header property

Common ODM header.

mean_elements property

Mean orbital elements at epoch.

metadata property

Object identity, frame, time system, theory.

tle_parameters property

SGP4/SGP4-XP catalog and force-model parameters, when present.

user_defined property

USER_DEFINED_* keys preserved verbatim.

from_file(path) staticmethod

Reads an OMM from a KVN file.

Parameters:

Name Type Description Default
path str

Path to a KVN OMM file.

required

Returns:

Type Description
OMM

The parsed OMM.

Raises:

Type Description
ValueError

If the file cannot be read or fails to parse.

from_kvn(text) staticmethod

Parses an OMM from its KVN text encoding.

Parameters:

Name Type Description Default
text str

The OMM KVN document.

required

Returns:

Type Description
OMM

The parsed OMM.

Raises:

Type Description
ValueError

On any syntax error, unsupported version, missing required keyword, unit mismatch, bad epoch, or unsupported frame / time system / mean-element theory.

from_str_any(text) staticmethod

Parses an OMM from either encoding, sniffing XML versus KVN.

Parameters:

Name Type Description Default
text str

An OMM in XML or KVN.

required

Returns:

Type Description
OMM

The parsed OMM.

Raises:

Type Description
ValueError

On any parse failure.

from_tle(tle, header) staticmethod

Builds an OMM from a NORAD TLE.

Parameters:

Name Type Description Default
tle TLE

The source TLE.

required
header NDMHeader

The ODM header to attach.

required

Returns:

Type Description
OMM

The equivalent OMM (TEME / UTC / EARTH).

Raises:

Type Description
ValueError

If the conversion fails.

from_xml(text) staticmethod

Parses an OMM from its XML (NDM/XML) encoding.

Parameters:

Name Type Description Default
text str

The OMM XML document.

required

Returns:

Type Description
OMM

The parsed OMM.

Raises:

Type Description
ValueError

On malformed XML or any parse-contract violation.

to_kvn()

Serializes this OMM to its KVN text encoding.

Returns:

Type Description
str

The OMM as a KVN string (trailing newline).

Raises:

Type Description
ValueError

If the metadata or covariance frame has no CCSDS emit label.

to_tle()

Converts this OMM to a NORAD TLE.

Returns:

Type Description
TLE

A TLE at the OMM epoch in TEME.

Raises:

Type Description
ValueError

If tle_parameters is absent, the elements give a semi-major axis but no mean motion, the center is not EARTH, the frame is not TEME, the time system is not UTC, or the ephemeris type is not 0/2/4.

to_xml()

Serializes this OMM to its XML (NDM/XML) encoding.

Returns:

Type Description
str

The OMM as an XML string (with prolog, trailing newline).

Raises:

Type Description
ValueError

If the metadata or covariance frame has no CCSDS emit label.

OMMMeanElements

OMM mean orbital elements at epoch.

Exactly one of mean_motion / semi_major_axis is set. Angles are radians; mean_motion is rev/day.

Attributes:

Name Type Description
epoch ModifiedJulianDate

EPOCH in the metadata time system.

mean_motion float | None

MEAN_MOTION [rev/day].

semi_major_axis float | None

SEMI_MAJOR_AXIS [km].

eccentricity float

ECCENTRICITY [dimensionless; 0..1).

inclination float

INCLINATION [rad].

ascending_node float

RA_OF_ASC_NODE [rad].

argument_of_periapsis float

ARG_OF_PERICENTER [rad].

mean_anomaly float

MEAN_ANOMALY [rad].

gm float | None

GM [km3/s2].

argument_of_periapsis property

ARG_OF_PERICENTER [rad].

ascending_node property

RA_OF_ASC_NODE [rad].

eccentricity property

ECCENTRICITY [dimensionless; 0..1).

epoch property

EPOCH in the metadata time system.

gm property

GM [km3/s2], or None.

inclination property

INCLINATION [rad].

mean_anomaly property

MEAN_ANOMALY [rad].

mean_motion property

MEAN_MOTION [rev/day], or None.

semi_major_axis property

SEMI_MAJOR_AXIS [km], or None.

OMMMetadata

OMM metadata block: object identity, frame, time system, theory.

Attributes:

Name Type Description
comments list[str]

COMMENT lines in the metadata block.

object_name str

OBJECT_NAME.

object_id str

OBJECT_ID (COSPAR designator, e.g. 1998-067A).

center_name str

CENTER_NAME; only EARTH converts to a TLE.

reference_frame ReferenceFrame

REF_FRAME; TEME for SGP4.

time_system TimeSystem

TIME_SYSTEM.

mean_element_theory MeanElementTheory

MEAN_ELEMENT_THEORY.

center_name property

CENTER_NAME; only EARTH converts to a TLE.

comments property

COMMENT lines in the metadata block.

mean_element_theory property

MEAN_ELEMENT_THEORY.

object_id property

OBJECT_ID (COSPAR international designator).

object_name property

OBJECT_NAME.

reference_frame property

REF_FRAME; TEME for SGP4.

time_system property

TIME_SYSTEM.

OMMTLEParameters

OMM optional SGP4/SGP4-XP catalog and force-model parameters.

SGP4 uses bstar; SGP4-XP uses bterm and agom. Per ADR 0020, mean_motion_dot/mean_motion_ddot are round-trip only — propagation does not read them for GP/XP.

Attributes:

Name Type Description
ephemeris_type int

EPHEMERIS_TYPE (0/2 SGP4, 4 SGP4-XP).

classification Classification

CLASSIFICATION_TYPE (U/C/S).

norad_cat_id int

NORAD_CAT_ID.

element_set_no int

ELEMENT_SET_NO.

rev_at_epoch int

REV_AT_EPOCH.

bstar float | None

BSTAR [1/Earth-radii] — SGP4.

bterm float | None

BTERM [m**2/kg] — SGP4-XP.

mean_motion_dot float

MEAN_MOTION_DOT [rev/day**2].

mean_motion_ddot float | None

MEAN_MOTION_DDOT [rev/day**3] — SGP4.

agom float | None

AGOM [m**2/kg] — SGP4-XP.

agom property

AGOM [m**2/kg] — SGP4-XP only, or None.

bstar property

BSTAR [1/Earth-radii] — SGP4 drag term, or None.

bterm property

BTERM [m**2/kg] — SGP4-XP drag term, or None.

classification property

CLASSIFICATION_TYPE (U/C/S).

element_set_no property

ELEMENT_SET_NO.

ephemeris_type property

EPHEMERIS_TYPE (0/2 SGP4, 4 SGP4-XP).

mean_motion_ddot property

MEAN_MOTION_DDOT [rev/day**3] — SGP4 only, or None.

mean_motion_dot property

MEAN_MOTION_DOT [rev/day**2]; round-trip only.

norad_cat_id property

NORAD_CAT_ID.

rev_at_epoch property

REV_AT_EPOCH.

audit_citra_doa(citra_elsets, citra_observations, latest_span, full_span, expected_limits=...)

Summarize direction-of-arrival (TDOA) residual stats per DOA antenna.

Grouping, dedupe, the count fields, the per-sensor windowing rule, and expected_limits semantics match audit_citra_optical. Per-sensor keys are the DOA primary_antenna_id. Per-sensor stat-block fields: tdoa_bias / tdoa_noise [s] and ingest_delay_bias / ingest_delay_noise [s]. angular / angular_rate are not exposed at this level because DOA observations don't carry pointing data; the unified antenna block (audit_citra_sensors) surfaces them when a sister radar observation provides them.

Parameters:

Name Type Description Default
citra_elsets list[dict[str, object]]

List of CITRA elset dicts (same per-element shape as the elset arg of get_citra_residuals).

required
citra_observations list[dict[str, object]]

List of CITRA DOA observation dicts.

required
latest_span TimeSpan

Width of the short window applied to sensors that appear in expected_limits. Acts as a cap. Must satisfy latest_span <= full_span.

required
full_span TimeSpan

Minimum calibration history required for sensors that do not appear in expected_limits. Sensors with less than full_span of data are omitted from the output; sensors that meet the minimum are calibrated over their entire sample set (no cap).

required
expected_limits list[dict[str, object]] | None

Optional list of per-sensor limit dicts. See audit_citra_optical for the full shape.

...

Returns:

Type Description
list[dict[str, object]]

List of per-sensor stat blocks (one per DOA antenna that

list[dict[str, object]]

produced data and met the calibration minimum, or that is listed

list[dict[str, object]]

in expected_limits). Each block contains the common fields

list[dict[str, object]]

documented on audit_citra_optical plus DOA-specific

list[dict[str, object]]

measurement fields: tdoa_bias / tdoa_noise [s] and

list[dict[str, object]]

ingest_delay_bias / ingest_delay_noise [s]. Angular

list[dict[str, object]]

fields are not included because DOA observations do not carry

list[dict[str, object]]

pointing data.

Raises:

Type Description
ValueError

latest_span is greater than full_span; or a CITRA record fails to parse.

Per-satellite construction, ephemeris caching, or residual computation failures are logged at WARN and the offending satellite is dropped from the audit; they do not raise.

audit_citra_optical(citra_elsets, citra_observations, latest_span, full_span, expected_limits=...)

Summarize optical residual stats per telescope.

Each optical observation is attributed to an elset by matching the observation's satellite_id to the elset's satellite_id. Input elsets that share a satellite_id are deduped by first occurrence; later duplicates are silently dropped. Each observation also carries a sensor identifier (telescope_id for optical), used to break the residual stats out per sensor. Observations whose satellite_id is None are reported as uncorrelated_observation_count under the sensor that produced them and contribute no residual samples. A sensor that produces no usable bias / noise values across any field — e.g., uncorrelated tracks only, with no upstream creation_epoch to populate ingest_delay — is omitted from the output entirely.

Per-sensor window selection driven by expected_limits:

  • Sensors with an entry in expected_limits are evaluated over the short window [S_last - latest_span, S_last], and the per-sensor status is checked against the supplied bias / noise bounds. latest_span is a cap — even a newly-active sensor with little data still gets a block.
  • Sensors without an entry in expected_limits (the un-characterized ones) are calibrated over every sample the sensor has produced. full_span here is a minimum required calibration history: if the sensor's S_last - S_first span is less than full_span, the sensor is omitted from the output entirely — there isn't enough baseline to calibrate against. There is no cap on the long-baseline path: a 36-hour query against a 12-hour minimum runs over the full 36 hours.

S_last is the sensor's own latest sample, so a sensor whose data ends earlier than another's still gets evaluated against its own recent activity.

Parameters:

Name Type Description Default
citra_elsets list[dict[str, object]]

List of CITRA elset dicts (same per-element shape as the elset arg of get_citra_residuals).

required
citra_observations list[dict[str, object]]

List of CITRA optical observation dicts.

required
latest_span TimeSpan

Width of the short window applied to sensors that appear in expected_limits. Acts as a cap. Must satisfy latest_span <= full_span.

required
full_span TimeSpan

Minimum calibration history required for sensors that do not appear in expected_limits. Sensors with less than full_span of data are omitted from the output; sensors that meet the minimum are calibrated over their entire sample set (no cap).

required
expected_limits list[dict[str, object]] | None

Optional list of per-sensor limit dicts. Each entry is {"sensor_id": <sensor_uuid>, "<field>_bias": float, "<field>_noise": float, ...} (other keys are silently ignored, so the prior audit's output list can be passed back in directly). Missing bias/noise keys mean "do not check that field". Sensors absent from the list go on the calibration (full_span minimum) path with status = "nominal".

...

Returns:

Type Description
list[dict[str, object]]

List of per-sensor stat blocks ([{...}, {...}]). Each

list[dict[str, object]]

block contains:

list[dict[str, object]]
  • id (str): fresh UUIDv4 identifying this audit record (generated per call). Use this when persisting audit history.
list[dict[str, object]]
  • sensor_id (str): the telescope / antenna UUID this audit block is for. Match on this when correlating across runs or providing limits.
list[dict[str, object]]
  • sensor_type (str): "telescope" or "antenna".
list[dict[str, object]]
  • audit_type (str): "trend" when this sensor was listed in expected_limits (short window, status-checking path) or "baseline" when it wasn't (calibration path, full sample set).
list[dict[str, object]]
  • uncorrelated_observation_count (int): observations from this sensor in the window with no satellite_id.
list[dict[str, object]]
  • correlated_observation_count (int): observations from this sensor in the window matched to a deduped elset.
list[dict[str, object]]
  • observed_satellite_count (int): distinct correlated satellite_id values produced by this sensor in the window. Uncorrelated tracks contribute zero.
list[dict[str, object]]
  • first_epoch (str | None): earliest sample epoch within this sensor's chosen window, ISO 8601 UTC.
list[dict[str, object]]
  • last_epoch (str | None): the sensor's own latest sample epoch, ISO 8601 UTC. Anchors the window thresholds.
list[dict[str, object]]
  • time_bias [s], time_noise [s], angular_bias [deg], angular_noise [deg] (great-circle separation between observed and expected pointing direction), angular_rate_bias [deg/s], angular_rate_noise [deg/s] (L2 magnitude with the standard cos(dec) scaling on the RA-rate component), ingest_delay_bias [s], ingest_delay_noise [s] (creation_epoch - epoch when the upstream observation carried a creation_epoch): each is either a float (bias is the signed sample median, noise is a robust 1-σ (1.4826 * MAD), always >= 0) or None when the field's sample pool in the window is empty.
list[dict[str, object]]
  • <field>_status (str, one per measurement-quality field): "nominal" or "degraded" for that field. Flagged "degraded" when the sensor is listed in expected_limits, the field's pool is non-empty, and either drift bound is exceeded:

  • |new_bias| > |expected_<field>_bias| + 2 * expected_<field>_noise

  • new_noise > 2 * expected_<field>_noise

Both bounds use expected_<field>_noise (an observed robust 1-σ) as the measurement-uncertainty unit, so casual run-to-run variance doesn't trip the check. If the caller didn't supply <field>_noise, neither bound fires. Empty pools, unlisted fields, and sensors absent from expected_limits all stay "nominal". ingest_delay is informational metadata and does not get a _status key; ingest_delay_* entries in expected_limits are ignored.

list[dict[str, object]]
  • status (str): aggregate, "nominal" or "degraded". "degraded" iff any per-field <field>_status is "degraded" — use the per-field keys to find which one tripped.

Raises:

Type Description
ValueError

latest_span is greater than full_span; or a CITRA record fails to parse.

Per-satellite construction, ephemeris caching, or residual computation failures are logged at WARN and the offending satellite is dropped from the audit; they do not raise.

audit_citra_radar(citra_elsets, citra_observations, latest_span, full_span, expected_limits=...)

Summarize radar residual stats per radar antenna.

Grouping, dedupe, the count fields, the per-sensor windowing rule, and expected_limits semantics match audit_citra_optical. Per-sensor keys are the radar antenna_id. Per-sensor stat-block fields: range_bias / range_noise [km], range_rate_bias / range_rate_noise [km/s], angular_bias / angular_noise [deg], angular_rate_bias / angular_rate_noise [deg/s], and ingest_delay_bias / ingest_delay_noise [s]. Radar observations may carry RA/Dec independently of monostatic vs. bistatic geometry; the angular pools draw from any radar observation that has them. Observations whose measurement field is missing still count toward correlated_observation_count but contribute no samples to that field's pool.

Parameters:

Name Type Description Default
citra_elsets list[dict[str, object]]

List of CITRA elset dicts (same per-element shape as the elset arg of get_citra_residuals).

required
citra_observations list[dict[str, object]]

List of CITRA radar observation dicts.

required
latest_span TimeSpan

Width of the short window applied to sensors that appear in expected_limits. Acts as a cap. Must satisfy latest_span <= full_span.

required
full_span TimeSpan

Minimum calibration history required for sensors that do not appear in expected_limits. Sensors with less than full_span of data are omitted from the output; sensors that meet the minimum are calibrated over their entire sample set (no cap).

required
expected_limits list[dict[str, object]] | None

Optional list of per-sensor limit dicts. See audit_citra_optical for the full shape.

...

Returns:

Type Description
list[dict[str, object]]

List of per-sensor stat blocks (one per radar antenna that

list[dict[str, object]]

produced data and met the calibration minimum, or that is listed

list[dict[str, object]]

in expected_limits). Each block contains the common fields

list[dict[str, object]]

documented on audit_citra_optical plus radar-specific

list[dict[str, object]]

measurement fields: range_bias / range_noise [km],

list[dict[str, object]]

range_rate_bias / range_rate_noise [km/s], angular_bias

list[dict[str, object]]

/ angular_noise [deg], angular_rate_bias /

list[dict[str, object]]

angular_rate_noise [deg/s], and ingest_delay_bias /

list[dict[str, object]]

ingest_delay_noise [s].

Raises:

Type Description
ValueError

latest_span is greater than full_span; or a CITRA record fails to parse.

Per-satellite construction, ephemeris caching, or residual computation failures are logged at WARN and the offending satellite is dropped from the audit; they do not raise.

audit_citra_sensors(citra_elsets, sensors, observations, latest_span, full_span, expected_limits=...)

Audit a fleet of CITRA sensors per sensor.

Unified entry point. Optical observations are aggregated per telescope_id (each block tagged "sensor_type": "telescope"); radar and DOA observations are aggregated per antenna identifier (antenna_id for radar, primary_antenna_id for DOA) and each block tagged "sensor_type": "antenna". Telescope and antenna blocks are returned in a single flat list — split downstream by the sensor_type key if you need per-type views.

Only sensors that produced at least one usable bias / noise value appear in the output. Catalog sensors listed in sensors but with no observations are omitted regardless of whether they appear in expected_limits, and sensors whose observations all fail to populate a bias / noise pool (e.g., uncorrelated tracks only, with no upstream creation_epoch) are likewise omitted. Callers that need to detect silent or content-free sensors must cross-reference the input catalog themselves. Per-sensor windowing and expected_limits semantics match audit_citra_optical.

Parameters:

Name Type Description Default
citra_elsets list[dict[str, object]]

List of CITRA elset dicts.

required
sensors dict[str, list[dict[str, object]]]

{"telescopes": [<telescope_dict>, ...], "antennas": [<antenna_dict>, ...]}. Each sensor dict must carry a string id; richer schema fields are accepted and ignored. Missing top-level keys default to empty lists.

required
observations dict[str, list[dict[str, object]]]

{"optical": [...], "radar": [...], "doa": [...]}. Each list is the same shape as the observation arg of the per-type audit functions. Missing keys default to empty lists.

required
latest_span TimeSpan

Width of the short window applied to sensors that appear in expected_limits. Acts as a cap. Must satisfy latest_span <= full_span.

required
full_span TimeSpan

Minimum calibration history required for sensors that do not appear in expected_limits. Sensors with less than full_span of data are omitted from the output; sensors that meet the minimum are calibrated over their entire sample set (no cap).

required
expected_limits list[dict[str, object]] | None

Optional list of per-sensor limit dicts. Each entry is {"sensor_id": <sensor_uuid>, "<field>_bias": float, ...}; non-bias/noise keys (counts, epochs, status, type, ...) are silently ignored, so the prior audit's output list can be passed back in directly. Sensors absent from this list go on the calibration (full_span minimum) path.

...

Returns:

Type Description
list[dict[str, object]]

Flat list of per-sensor stat blocks

list[dict[str, object]]

(``[{"id": , "sensor_id": ,

list[dict[str, object]]

"sensor_type": "telescope" | "antenna", "audit_type":

list[dict[str, object]]

"trend" | "baseline", ...}, ...]``).

list[dict[str, object]]

Telescope blocks carry the optical fields documented on

list[dict[str, object]]

audit_citra_optical; antenna blocks carry the union

list[dict[str, object]]

of radar + DOA fields (range_*, range_rate_*,

list[dict[str, object]]

angular_*, angular_rate_*, tdoa_*,

list[dict[str, object]]

ingest_delay_* — each as a flat _bias / _noise

list[dict[str, object]]

pair, plus _status for every measurement-quality field).

Raises:

Type Description
ValueError

latest_span is greater than full_span; or a CITRA record fails to parse.

Per-satellite construction, ephemeris caching, or residual computation failures are logged at WARN and the offending satellite is dropped from the audit; they do not raise.

citra_data_to_observations(citra_observations, calibrations=..., telescope_limits=..., antenna_limits=...)

Convert a CITRA observation envelope into keplime observations.

Records are converted and returned in the canonical order optical, radar, doa. Measurement noise is resolved per observation in two stages: a per-sensor calibration override, then a per-sensor-type limit clamp.

When calibrations is supplied, each converted observation whose sensor id matches a calibration's sensor_id has its present noise fields replaced (angular fields given in degrees, converted to radians). The override is purely per-sensor, matched by id — there is no per-modality logic. An observation whose sensor has no matching calibration keeps the noise reported on its record, so to leave a sensor's noise untouched (for example a trusted telescope) simply omit it from calibrations.

The resolved noise is then bounded to the supplied SensorCalibrationLimits for the observation's sensor type — optical observations use telescope_limits, radar and DOA observations use antenna_limits. Clamping runs for every observation whether or not a calibration matched, so unphysical record noise (e.g. milli-arcsecond optical, kilometre-scale range) is corrected even with no calibration. A None limits argument skips clamping for that sensor type.

Parameters:

Name Type Description Default
citra_observations dict[str, list[dict[str, object]]]

{"optical": [...], "radar": [...], "doa": [...]} mapping. Each list holds CITRA observation dicts for that type; missing keys default to empty lists.

required
calibrations list[dict[str, object]] | None

Optional list of per-sensor noise-override mappings. Each must carry sensor_id and may carry any of angular_noise [deg], range_noise [km], range_rate_noise [km/s], angular_rate_noise [deg/s], tdoa_noise [s], fdoa_noise [Hz]; unknown keys (e.g. baseline biases) are ignored. Defaults to None (no overrides).

...
telescope_limits SensorCalibrationLimits | None

Optional SensorCalibrationLimits applied to optical (telescope) observations. Defaults to None.

...
antenna_limits SensorCalibrationLimits | None

Optional SensorCalibrationLimits applied to radar and DOA (antenna) observations. Defaults to None.

...

Returns:

Type Description
list[Observation]

Observations in the TEME frame, in optical -> radar -> doa order.

Raises:

Type Description
ValueError

If a record is missing a required field, an epoch cannot be parsed, or a sensor location cannot be propagated to its epoch.

dnd_postgres_df_to_observations(dataframe)

Converts DND PostgreSQL observation rows into keplime observations.

The input is expected to be the Polars DataFrame returned by pl.read_database_uri for the DND observation query. Required columns are id, epoch, sensor_id, sensor_latitude, sensor_longitude, and sensor_altitude. Optional columns are right_ascension, declination, range, visual_magnitude, radar_cross_section, provider_id, and dnd_id.

Angles and sensor geodetic coordinates are interpreted in radians. Range and sensor altitude are interpreted in km. sensor_id is stored on Observation.sensor.id, provider_id is stored on Observation.expected_satellite_id, dnd_id is stored on Observation.observed_satellite_id, and radar_cross_section is stored on Observation.radar_cross_section. Converted sensors use the package default measurement noise constants: angular observations receive 0.0005 deg (stored in radians), range observations receive 0.1 km, and absent measurement types leave their corresponding noise fields unset. Polars datetime epochs are read from their Arrow-backed integer storage and interpreted as UTC Unix timestamps. String epochs are parsed directly.

Parameters:

Name Type Description Default
dataframe Any

Polars dataframe returned by the DND PostgreSQL query.

required

Returns:

Type Description
list[Observation]

Converted Observation objects in

list[Observation]

input row order.

Raises:

Type Description
TypeError

If dataframe is not a Polars dataframe.

ValueError

If a row is missing a required column, a required value is null, an epoch cannot be parsed, or sensor coordinates cannot be propagated.

dnd_postgres_elset_df_to_constellation(dataframe, start, end, step, purge_on_fail=..., reference_frame=...)

Builds a constellation with stitched ephemeris from a DND elset query.

The input is a Polars DataFrame with the required columns dnd_id, elset_line_1, and elset_line_2, plus an optional name column. Rows are grouped by dnd_id (each dnd_id becomes one satellite); elset_line_1 / elset_line_2 are parsed as a two-line element set and the epoch is read from the TLE itself. When present, name (taken to be constant per dnd_id) sets each satellite's human-readable name.

For a satellite with a single element set the ephemeris is a plain SGP4 arc over [start, end]. For a satellite with multiple element sets, one arc is propagated per set across the span between consecutive epochs and the arcs are joined at their closest-approach crossovers (see Ephemeris.stitch), then resampled onto the uniform [start, end, step] grid so every satellite in the returned constellation shares the same epoch lattice. Each returned satellite is keyed by its dnd_id and carries the freshest element set as its TLE metadata.

Element sets are de-duplicated by epoch (records within 1 ms collapse to the latest), and only the sets relevant to [start, end] are propagated.

Parameters:

Name Type Description Default
dataframe Any

Polars dataframe with required dnd_id, elset_line_1, elset_line_2 columns and an optional name column.

required
start ModifiedJulianDate

Start of the ephemeris window.

required
end ModifiedJulianDate

End of the ephemeris window.

required
step TimeSpan

Propagation / resampling step.

required
purge_on_fail bool

When True, a satellite whose element sets fail to parse or whose arcs cannot be stitched is dropped (and logged) and the rest are still returned. When False (the default), the first such failure raises. Defaults to False.

...
reference_frame ReferenceFrame | None

Frame for the cached states; defaults to TEME.

...

Returns:

Type Description
Constellation

One Satellite per dnd_id in a

Constellation

Constellation, each carrying a stitched

Constellation

ephemeris over [start, end].

Raises:

Type Description
TypeError

If dataframe is not a Polars dataframe.

ValueError

If a required column is missing, a value is null, an element set cannot be parsed, or (when purge_on_fail is False) a satellite's ephemeris cannot be built.

get_citra_residuals(citra_elset, citra_observations)

Compute citra-format residuals for a batch of citra observations.

Returns one dict per input observation (in input order: optical, then radar, then doa). Each dict carries identifiers (satellite_id, observation_id, elset_id, elset_username, elset_user_group_name, review_status), the source observation epoch (echoed from input), the residual total_error (cross-line-of-sight, km), the type ("optical" / "radar" / "doa"), and the residual numbers (range km, range_rate km/s, right_ascension / declination / right_ascension_rate / declination_rate in degrees, tdoa s, fdoa Hz). visual_magnitude is populated only for optical entries.

Note: elset_username and elset_user_group_name are populated from the citra elset dict's user_id and user_group_id fields (UUID strings). If you need resolved human-readable names, do that join in your own DB after this call.

Parameters:

Name Type Description Default
citra_elset dict[str, object]

CITRA elset dict. Must carry at minimum satellite_id and id (UUID strings), plus user_id and user_group_id to populate elset_username / elset_user_group_name.

required
citra_observations dict[str, list[dict[str, object]]]

{"optical": [...], "radar": [...], "doa": [...]} mapping. Each list contains CITRA observation dicts for that type. Missing keys default to empty lists.

required

Returns:

Type Description
list[dict[str, object]]

One residual dict per input observation, in input order

list[dict[str, object]]

(optical, then radar, then DOA). Each dict carries the identifier

list[dict[str, object]]

and residual fields described above.

Raises:

Type Description
ValueError

If a CITRA record fails to parse, the satellite cannot be constructed from the elset, or residual computation fails.