# `Sidereon.Covariance`
[🔗](https://github.com/neilberkman/sidereon-ex/blob/main/lib/sidereon/covariance.ex#L1)

Covariance matrix helpers for conjunction and orbit analysis.

Supports covariance extraction, frame transforms, and validation checks such
as positive semidefiniteness.

The authoritative RTN->ECI frame transform and the symmetric
positive-semidefinite validation live in the `sidereon-core` Rust core; this
module marshals inputs, performs structural validation, and decodes results.

# `mat3`

```elixir
@type mat3() :: [[float()]]
```

# `mat6`

```elixir
@type mat6() :: [[float()]]
```

# `state`

```elixir
@type state() :: {number(), vec3(), vec3()}
```

# `vec3`

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

# `covariance_from_jacobian`

```elixir
@spec covariance_from_jacobian([[number()]], number()) ::
  {:ok, [[float()]]} | {:error, atom()}
```

Fitted parameter covariance directly from a converged solve's design matrix
and cost.

Scales `(J^T J)^-1` by the post-fit reduced chi-square `2 * cost / (m - n)`,
the same scale `scipy.optimize.curve_fit` applies to its `pcov`. `jacobian` is
the `m`-by-`n` design matrix and `cost` the optimum `0.5 * dot(r, r)`. The
redundancy comes from the Jacobian's dimensions, so no residual or parameter
vectors are needed. Requires positive redundancy `m > n`.

Returns `{:ok, covariance_rows}` or `{:error, :singular_jacobian | :invalid_input}`.

# `eci_to_rtn6`

```elixir
@spec eci_to_rtn6(mat6(), state()) :: {:ok, mat6()} | {:error, term()}
```

Transform a 6x6 inertial ECI state covariance to RTN at `state`.

# `error_ellipse_2x2`

```elixir
@spec error_ellipse_2x2([[number()]], number()) ::
  {:ok,
   %{
     confidence: float(),
     chi_square_scale: float(),
     semi_major: float(),
     semi_minor: float(),
     orientation_rad: float()
   }}
  | {:error, atom()}
```

Confidence ellipse from an arbitrary 2x2 covariance block.

The semi-axes are scaled by the two-degree-of-freedom chi-square quantile
`-2 ln(1 - confidence)` applied to the eigenvalues of the symmetrized block.
Returns `{:ok, %{confidence:, chi_square_scale:, semi_major:, semi_minor:,
orientation_rad:}}` or `{:error, reason}`.

# `extract_pos_cov`

```elixir
@spec extract_pos_cov([float()]) :: {:ok, mat3()} | {:error, String.t()}
```

Extract a 3x3 position covariance matrix from a 6-element lower triangle (RTN).
Expected order: CR_R (0,0), CT_R (1,0), CT_T (1,1), CN_R (2,0), CN_T (2,1), CN_N (2,2).

# `from_diagonal6`

```elixir
@spec from_diagonal6([number()]) :: {:ok, mat6()} | {:error, atom()}
```

Build a 6x6 covariance matrix from six diagonal variances.

# `hessian_trace`

```elixir
@spec hessian_trace([[number()]]) :: float()
```

Trace of the Gauss-Newton Hessian approximation `J^T J`, i.e. the sum of the
squared column norms of `jacobian`. No inverse is formed.

# `interpolate_psd6`

```elixir
@spec interpolate_psd6(mat6(), mat6(), number()) :: {:ok, mat6()} | {:error, atom()}
```

PSD-preserving interpolation between two 6x6 covariance matrices.

# `km_to_m6`

```elixir
@spec km_to_m6(mat6()) :: {:ok, mat6()} | {:error, atom()}
```

Convert a 6x6 state covariance from kilometre units to metre units.

# `m_to_km6`

```elixir
@spec m_to_km6(mat6()) :: {:ok, mat6()} | {:error, atom()}
```

Convert a 6x6 state covariance from metre units to kilometre units.

# `normal_covariance`

```elixir
@spec normal_covariance([[number()]], number()) ::
  {:ok, [[float()]]} | {:error, atom()}
```

Parameter covariance `variance_scale * (J^T J)^-1` from a design (Jacobian)
matrix.

`jacobian` is an `m`-by-`n` matrix (a list of `m` rows of `n` numbers) with
`m >= n`. The covariance is formed from the thin SVD of `J` directly, the same
quantity (and construction) `scipy.optimize.curve_fit` reports as `pcov`: pass
the post-fit reduced chi-square as `variance_scale` for the fitted covariance,
or `1.0` for the bare `(J^T J)^-1` cofactor.

Returns `{:ok, covariance_rows}` or `{:error, :singular_jacobian | :invalid_input}`.

# `positive_semidefinite?`

```elixir
@spec positive_semidefinite?(mat3()) :: boolean()
```

Check if a 3x3 matrix is symmetric and positive semidefinite (PSD).

A symmetric 3x3 matrix is PSD if all its principal minors are non-negative.

# `rtn_to_eci6`

```elixir
@spec rtn_to_eci6(mat6(), state()) :: {:ok, mat6()} | {:error, term()}
```

Transform a 6x6 RTN state covariance to inertial ECI at `state`.

# `rtn_to_eci`

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

Transform a 3x3 RTN covariance matrix to ECI.

# `symmetric?`

```elixir
@spec symmetric?(any()) :: boolean()
```

Check if a matrix is symmetric.

# `transport_segments6`

```elixir
@spec transport_segments6(mat6(), [map()], keyword()) ::
  {:ok, [mat6()]} | {:error, term()}
```

Transport a 6x6 covariance through precomputed state-transition segments.

# `valid_matrix?`

```elixir
@spec valid_matrix?(any()) :: boolean()
```

Validate that the input is a 3x3 numeric matrix.

# `validate6`

```elixir
@spec validate6(mat6()) ::
  {:ok, %{symmetric: boolean(), positive_semidefinite: boolean()}}
  | {:error, atom()}
```

Validate a 6x6 covariance matrix for symmetry and positive semidefiniteness.

---

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