Sidereon.Format.OMM (Sidereon v3.0.0)

Copy Markdown View Source

Parse and encode CCSDS Orbit Mean-Elements Messages (OMM).

OMM is the modern standard format for orbital data, carrying the same elements as TLE plus metadata such as originator, reference frame, time system, and mean-element theory. CelesTrak and Space-Track distribute OMM messages as KVN, XML, and JSON.

parse_kvn/1, parse_xml/1, parse_json/1, and string parse/1 return a typed %Sidereon.Format.OMM{} that keeps every item of CCSDS 502.0-B-3 tables 4-1 to 4-3 the message states: the header, metadata, mean elements, spacecraft parameters, TLE-related parameters, covariance, USER_DEFINED_* parameters and the comments of each block. A keyword the message does not state is nil; no reader fills in a default. parse_xml_all/1 and parse_json_array/1 read documents holding several OMMs. The legacy decoded-map parse/1 clause is kept for CelesTrak JSON maps and returns %Sidereon.Elements{}.

Each writer returns {:error, reason} for a message it cannot write so that its reader returns it unchanged, such as text with a line break in KVN, a character XML 1.0 cannot carry, a non-finite number, or, in GP JSON, a comment other than the single header comment GP JSON carries.

Summary

Types

A reader, writer or element-set refusal from the core (Sidereon.CCSDS.Error.omm/0), or {:invalid_field, field, value} for a struct field that does not hold a value of its type.

A refusal of the decoded-map parse/1: a missing or unreadable key, named as the key it reads.

A record parse_xml_all/1 or parse_json_array/1 could not read: its zero-based position in the document and the reason.

t()

A CCSDS OMM.

Functions

Encode an OMM value.

Encode a typed OMM struct as CCSDS/CelesTrak OMM JSON text.

Encode a typed OMM struct as GP JSON, leaving out every comment GP JSON cannot carry: all but the single header comment. A spacecraft-parameters block that held only comments is still written, as "MASS": null.

Encode a typed OMM struct as CCSDS OMM KVN text.

Encode a typed OMM struct as CCSDS OMM XML text.

Parse an OMM from text or from a decoded JSON map.

Parse CCSDS/CelesTrak OMM JSON text holding one record into a typed OMM struct.

Parse a CelesTrak/Space-Track GP JSON array of OMM records.

Parse CCSDS OMM KVN text into a typed OMM struct.

Parse CCSDS OMM XML text into a typed OMM struct.

Parse every OMM of a CCSDS OMM XML document: a single message or an NDM combined instantiation (CCSDS 505.0-B-3 4.11).

Convert a typed OMM struct to %Sidereon.Elements{} for SGP4 propagation.

Alias for encode_json/1, matching the core and Python binding terminology.

Alias for encode_kvn/1, matching the core and Python binding terminology.

Alias for encode_xml/1, matching the core and Python binding terminology.

Types

encode_error()

@type encode_error() :: error()

error()

@type error() :: Sidereon.CCSDS.Error.omm() | {:invalid_field, atom(), term()}

A reader, writer or element-set refusal from the core (Sidereon.CCSDS.Error.omm/0), or {:invalid_field, field, value} for a struct field that does not hold a value of its type.

parse_error()

@type parse_error() ::
  {:missing_field, String.t()} | {:invalid_field, String.t(), term()}

A refusal of the decoded-map parse/1: a missing or unreadable key, named as the key it reads.

skipped_record()

@type skipped_record() :: {non_neg_integer(), Sidereon.CCSDS.Error.omm()}

A record parse_xml_all/1 or parse_json_array/1 could not read: its zero-based position in the document and the reason.

t()

@type t() :: %Sidereon.Format.OMM{
  agom_m2_kg: float() | nil,
  arg_of_pericenter_deg: float(),
  bstar: float() | nil,
  bterm_m2_kg: float() | nil,
  ccsds_omm_vers: String.t() | nil,
  center_name: String.t() | nil,
  classification: String.t() | nil,
  classification_type: String.t() | nil,
  comments: Sidereon.Format.OMM.Comments.t(),
  covariance: Sidereon.Format.OMM.Covariance.t() | nil,
  creation_date: String.t() | nil,
  eccentricity: float(),
  element_set_no: integer() | nil,
  ephemeris_type: integer() | nil,
  epoch: Sidereon.Format.OMM.Epoch.t(),
  exact_sgp4_epoch: {float(), float()} | nil,
  gm_km3_s2: float() | nil,
  inclination_deg: float(),
  mean_anomaly_deg: float(),
  mean_element_theory: String.t() | nil,
  mean_motion: float() | nil,
  mean_motion_ddot: float() | nil,
  mean_motion_dot: float() | nil,
  message_id: String.t() | nil,
  norad_cat_id: non_neg_integer() | nil,
  object_id: String.t() | nil,
  object_name: String.t() | nil,
  originator: String.t() | nil,
  quantize_tle_derived_fields: boolean(),
  ra_of_asc_node_deg: float(),
  ref_frame: String.t() | nil,
  ref_frame_epoch: String.t() | nil,
  rev_at_epoch: integer() | nil,
  semi_major_axis_km: float() | nil,
  spacecraft: Sidereon.Format.OMM.Spacecraft.t() | nil,
  time_system: String.t() | nil,
  user_defined: [Sidereon.Format.OMM.UserDefined.t()]
}

A CCSDS OMM.

ccsds_omm_vers is the version the message states, or nil, as CelesTrak GP JSON and CSV state none; each writer states it only when present. At least one of mean_motion (rev/day) and semi_major_axis_km is present in a message read from text. The TLE-related parameters are nil when the message does not state them, since table 4-3 requires them only for SGP/SGP4 element sets. bterm_m2_kg and agom_m2_kg are the SGP4-XP BTERM and AGOM (m²/kg); gm_km3_s2 is GM (km³/s²).

exact_sgp4_epoch and quantize_tle_derived_fields retain the core OMM's in-memory SGP4 conversion policy across the NIF boundary. They are not CCSDS wire fields.

Functions

encode(value, opts \\ [])

@spec encode(
  t(),
  keyword()
) :: {:ok, String.t()} | {:error, encode_error()}

Encode an OMM value.

A typed %Sidereon.Format.OMM{} is serialized as text. The :format option may be :kvn, :xml, or :json and defaults to :kvn.

The legacy %Sidereon.Elements{} clause returns a JSON-compatible map with standard OMM field names.

encode_json(omm)

@spec encode_json(t()) :: {:ok, String.t()} | {:error, encode_error()}

Encode a typed OMM struct as CCSDS/CelesTrak OMM JSON text.

Returns {:ok, text} or {:error, reason}.

encode_json_discarding_comments(omm)

@spec encode_json_discarding_comments(t()) ::
  {:ok, String.t()} | {:error, encode_error()}

Encode a typed OMM struct as GP JSON, leaving out every comment GP JSON cannot carry: all but the single header comment. A spacecraft-parameters block that held only comments is still written, as "MASS": null.

encode_json/1 refuses such a record instead; use this function only when that loss is acceptable. Returns {:ok, text} or {:error, reason}.

encode_kvn(omm)

@spec encode_kvn(t()) :: {:ok, String.t()} | {:error, encode_error()}

Encode a typed OMM struct as CCSDS OMM KVN text.

Returns {:ok, text} or {:error, reason}.

encode_xml(omm)

@spec encode_xml(t()) :: {:ok, String.t()} | {:error, encode_error()}

Encode a typed OMM struct as CCSDS OMM XML text.

Returns {:ok, text} or {:error, reason}.

parse(text)

@spec parse(String.t()) :: {:ok, t()} | {:error, Sidereon.CCSDS.Error.omm()}
@spec parse(map()) :: {:ok, Sidereon.Elements.t()} | {:error, parse_error()}

Parse an OMM from text or from a decoded JSON map.

For binary input, the text format is auto-detected: a leading < selects XML, a leading { or [ selects JSON, and all other input is parsed as KVN. Text parsing returns {:ok, %Sidereon.Format.OMM{}} or {:error, reason}.

For map input, accepts decoded CelesTrak/Space-Track OMM JSON maps with field names such as "NORAD_CAT_ID", "INCLINATION", and "MEAN_MOTION". This legacy path returns {:ok, %Sidereon.Elements{}} and handles both numeric and string values for numeric fields. "MEAN_MOTION" and "BSTAR", which SGP4 propagates with, are required; an unstated mean-motion derivative, "CLASSIFICATION_TYPE", "EPHEMERIS_TYPE", "ELEMENT_SET_NO" or "REV_AT_EPOCH" is nil.

Examples

iex> {:ok, el} = Sidereon.Format.OMM.parse(%{
...>   "NORAD_CAT_ID" => 25544,
...>   "OBJECT_NAME" => "ISS (ZARYA)",
...>   "EPOCH" => "2024-01-01T00:00:00",
...>   "INCLINATION" => 51.6,
...>   "RA_OF_ASC_NODE" => 300.0,
...>   "ECCENTRICITY" => 0.0007,
...>   "ARG_OF_PERICENTER" => 90.0,
...>   "MEAN_ANOMALY" => 270.0,
...>   "MEAN_MOTION" => 15.5,
...>   "BSTAR" => 0.0001
...> })
iex> el.catalog_number
"25544"
iex> el.object_name
"ISS (ZARYA)"

parse_json(text)

@spec parse_json(String.t()) :: {:ok, t()} | {:error, Sidereon.CCSDS.Error.omm()}

Parse CCSDS/CelesTrak OMM JSON text holding one record into a typed OMM struct.

JSON input may be a single OMM object or an array holding one object. A document holding several records is refused; parse_json_array/1 reads them.

Returns {:ok, %Sidereon.Format.OMM{}} or {:error, reason}.

parse_json_array(text)

@spec parse_json_array(String.t()) ::
  {:ok, [t()], [skipped_record()]} | {:error, Sidereon.CCSDS.Error.omm()}

Parse a CelesTrak/Space-Track GP JSON array of OMM records.

Returns {:ok, omms, skipped}, where skipped lists each array element that could not be read as {index, reason}, or {:error, reason}.

parse_kvn(text)

@spec parse_kvn(String.t()) :: {:ok, t()} | {:error, Sidereon.CCSDS.Error.omm()}

Parse CCSDS OMM KVN text into a typed OMM struct.

Returns {:ok, %Sidereon.Format.OMM{}} or {:error, reason}.

parse_xml(text)

@spec parse_xml(String.t()) :: {:ok, t()} | {:error, Sidereon.CCSDS.Error.omm()}

Parse CCSDS OMM XML text into a typed OMM struct.

Returns {:ok, %Sidereon.Format.OMM{}} or {:error, reason}.

parse_xml_all(text)

@spec parse_xml_all(String.t()) ::
  {:ok, [t()], [skipped_record()]} | {:error, Sidereon.CCSDS.Error.omm()}

Parse every OMM of a CCSDS OMM XML document: a single message or an NDM combined instantiation (CCSDS 505.0-B-3 4.11).

Returns {:ok, omms, skipped}, where skipped lists each message that could not be read as {index, reason}, or {:error, reason} for a document that cannot be read at all.

to_elements(omm)

@spec to_elements(t()) :: {:ok, Sidereon.Elements.t()} | {:error, encode_error()}

Convert a typed OMM struct to %Sidereon.Elements{} for SGP4 propagation.

The elements are the SGP4 element set the core forms from the OMM (Omm::to_element_set); OMM-specific metadata remains available on the original OMM struct. The core refuses:

  • a stated MEAN_ELEMENT_THEORY other than SGP4, SGP/SGP4 or SDP4, CENTER_NAME other than EARTH, REF_FRAME other than TEME or TIME_SYSTEM other than UTC (compared ignoring surrounding whitespace and letter case), in that order, with {:incompatible_metadata, field, value}, since the elements would then not be the Earth-centred TEME UTC SGP4 elements (CCSDS 502.0-B-3 4.2.4.6); an absent or blank value is not refused;
  • an OMM without MEAN_MOTION or BSTAR, which SGP4 propagates with, with {:missing_field, :mean_motion} or {:missing_field, :bstar};
  • an epoch that names no UTC instant, or an element that is not finite or out of range, with {:invalid_field, field, kind}.

An OMM without NORAD_CAT_ID gives elements whose catalog_number is nil. epoch_jd is the epoch as the core's split Julian date, with the epoch's femtoseconds and a UTC leap second (23:59:60) kept, and propagation uses it; epoch is that instant as a DateTime to the microsecond, so a leap-second epoch reads as the start of the next day, the same Julian date. An epoch of whole microseconds, which python-sgp4 reads, gives elements with omm_epoch_days set, which SGP4 initialises as python-sgp4 initialises the OMM, and bstar and mean_motion_double_dot as stated; any other epoch bridges the OMM as a TLE, and bstar and mean_motion_double_dot are quantized to the values the TLE fields hold. A value no TLE field holds passes through unquantized. Unstated mean-motion derivatives and bookkeeping fields stay nil.

Returns {:ok, elements} or {:error, reason}.

to_json_string(omm)

@spec to_json_string(t()) :: {:ok, String.t()} | {:error, encode_error()}

Alias for encode_json/1, matching the core and Python binding terminology.

to_kvn_string(omm)

@spec to_kvn_string(t()) :: {:ok, String.t()} | {:error, encode_error()}

Alias for encode_kvn/1, matching the core and Python binding terminology.

to_xml_string(omm)

@spec to_xml_string(t()) :: {:ok, String.t()} | {:error, encode_error()}

Alias for encode_xml/1, matching the core and Python binding terminology.