# `Sidereon.CCSDS.OEM`
[🔗](https://github.com/neilberkman/sidereon-ex/blob/main/lib/sidereon/ccsds/oem.ex#L1)

Parse and encode CCSDS Orbit Ephemeris Messages (OEM).

Supports both the **KVN** (Keyword=Value Notation) and **XML** formats per
CCSDS 502.0-B. An OEM carries one or more segments, each a metadata block plus
a time-ordered list of Cartesian state samples (optionally with acceleration)
and optional covariance blocks.

`parse/1` auto-detects the format from the first non-whitespace character: a
leading `<` is treated as XML, anything else as KVN. Date/time fields are
preserved as raw strings exactly as written; the message round-trips through
the canonical struct without calendar rewriting.

## Examples

    {:ok, oem} = Sidereon.CCSDS.OEM.parse(kvn_string)
    [segment | _] = oem.segments
    segment.metadata.object_name
    [state | _] = segment.states
    state.position_km            # {x, y, z} in km

    # KVN output (default)
    kvn = Sidereon.CCSDS.OEM.encode(oem)

    # XML output
    xml = Sidereon.CCSDS.OEM.encode(oem, format: :xml)

    # Round-trip through XML
    {:ok, oem2} = Sidereon.CCSDS.OEM.parse(xml)

# `error`

```elixir
@type error() :: :missing_field | :invalid_field | :malformed
```

Failure reason from the OEM readers.

# `t`

```elixir
@type t() :: %Sidereon.CCSDS.OEM{
  ccsds_oem_vers: String.t(),
  creation_date: String.t() | nil,
  originator: String.t() | nil,
  segments: [Sidereon.CCSDS.OEM.Segment.t()],
  skipped_states: non_neg_integer()
}
```

# `vec3`

```elixir
@type vec3() :: {float(), float(), float()}
```

A Cartesian triple `{x, y, z}`.

# `encode`

```elixir
@spec encode(
  t(),
  keyword()
) :: String.t()
```

Encode an OEM.

## Options
  * `:format` - `:kvn` (default) or `:xml`

# `encode_kvn`

```elixir
@spec encode_kvn(t()) :: String.t()
```

Encode an OEM to KVN text explicitly.

# `encode_xml`

```elixir
@spec encode_xml(t()) :: String.t()
```

Encode an OEM to XML text explicitly.

# `parse`

```elixir
@spec parse(String.t()) :: {:ok, t()} | {:error, error()}
```

Parse an OEM in either KVN or XML format.

Format is auto-detected from the first non-whitespace character: `<` routes to
the XML parser, anything else to the KVN parser.

Returns `{:ok, %Sidereon.CCSDS.OEM{}}` or `{:error, reason}`.

# `parse_kvn`

```elixir
@spec parse_kvn(String.t()) :: {:ok, t()} | {:error, error()}
```

Parse an OEM in KVN format explicitly. Skips format auto-detection.

# `parse_xml`

```elixir
@spec parse_xml(String.t()) :: {:ok, t()} | {:error, error()}
```

Parse an OEM in XML format explicitly. Skips format auto-detection.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
