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

Parse and encode CCSDS Orbit Parameter Messages (OPM).

Supports both the **KVN** (Keyword=Value Notation) and **XML** formats per
CCSDS 502.0-B. An OPM carries a single epoch's Cartesian state plus optional
Keplerian elements, spacecraft parameters, a 6x6 covariance, and a list of
maneuvers.

`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.

## Examples

    {:ok, opm} = Sidereon.CCSDS.OPM.parse(kvn_string)
    opm.metadata.object_name
    opm.state.position_km        # {x, y, z} in km
    opm.keplerian.anomaly        # {:true_anomaly, deg} or {:mean_anomaly, deg}

    # KVN output (default)
    kvn = Sidereon.CCSDS.OPM.encode(opm)

    # XML output
    xml = Sidereon.CCSDS.OPM.encode(opm, format: :xml)

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

# `error`

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

Failure reason from the OPM readers.

# `t`

```elixir
@type t() :: %Sidereon.CCSDS.OPM{
  ccsds_opm_vers: String.t(),
  covariance: Sidereon.CCSDS.OPM.Covariance.t() | nil,
  creation_date: String.t() | nil,
  keplerian: Sidereon.CCSDS.OPM.Keplerian.t() | nil,
  maneuvers: [Sidereon.CCSDS.OPM.Maneuver.t()],
  metadata: Sidereon.CCSDS.OPM.Metadata.t(),
  originator: String.t() | nil,
  spacecraft: Sidereon.CCSDS.OPM.Spacecraft.t() | nil,
  state: Sidereon.CCSDS.OPM.State.t()
}
```

# `vec3`

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

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

# `encode`

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

Encode an OPM.

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

# `encode_kvn`

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

Encode an OPM to KVN text explicitly.

# `encode_xml`

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

Encode an OPM to XML text explicitly.

# `parse`

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

Parse an OPM 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.OPM{}}` or `{:error, reason}`.

# `parse_kvn`

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

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

# `parse_xml`

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

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

---

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