Source code for opengnc.utils.covariance
"""
Covariance transformation and reachability analysis tools.
"""
from __future__ import annotations
import numpy as np
from typing import cast
[docs]
class CovarianceTransform:
"""
Tools for transforming state covariances between various frames.
"""
[docs]
@staticmethod
def eci_to_ric(r_eci: np.ndarray, v_eci: np.ndarray, P_eci: np.ndarray) -> np.ndarray:
"""
Transform covariance from ECI to RIC (Radial, In-Track, Cross-Track).
Parameters
----------
r_eci : np.ndarray
Position (m).
v_eci : np.ndarray
Velocity (m/s).
P_eci : np.ndarray
$6 \times 6$ Covariance matrix in ECI.
Returns
-------
np.ndarray
$6 \times 6$ Covariance matrix in RIC.
"""
# 1. Define RIC Unit Vectors
u_r = r_eci / np.linalg.norm(r_eci)
h = np.cross(r_eci, v_eci)
u_c = h / np.linalg.norm(h)
u_i = np.cross(u_c, u_r)
# 2. Rotation Matrix R (ECI to RIC)
rot = np.vstack([u_r, u_i, u_c])
# 3. Full 6x6 Transformation Matrix T
T = np.zeros((6, 6))
T[0:3, 0:3] = rot
T[3:6, 3:6] = rot
# 4. Transform: P_ric = T P_eci T.T
return cast(np.ndarray, T @ P_eci @ T.T)
[docs]
@staticmethod
def ric_to_eci(r_eci: np.ndarray, v_eci: np.ndarray, P_ric: np.ndarray) -> np.ndarray:
"""
Transform covariance from RIC back to ECI.
Parameters
----------
r_eci : np.ndarray
Position (m).
v_eci : np.ndarray
Velocity (m/s).
P_ric : np.ndarray
$6 \times 6$ Covariance matrix in RIC.
Returns
-------
np.ndarray
$6 \times 6$ Covariance matrix in ECI.
"""
u_r = r_eci / np.linalg.norm(r_eci)
h = np.cross(r_eci, v_eci)
u_c = h / np.linalg.norm(h)
u_i = np.cross(u_c, u_r)
rot = np.vstack([u_r, u_i, u_c]).T # Inverse is Transpose
T = np.zeros((6, 6))
T[0:3, 0:3] = rot
T[3:6, 3:6] = rot
return cast(np.ndarray, T @ P_ric @ T.T)
[docs]
class ReachabilityAnalysis:
"""
Orbital maneuver reachability analysis.
"""
def __init__(self, mu: float = 398600.4418e9) -> None:
"""Initialize with gravity constant."""
self.mu = mu
[docs]
def get_reachable_delta_elements(
self, a: float, e: float, i: float, dv_total: float
) -> dict[str, float]:
r"""
Calculate maximum possible changes in orbital elements for a total delta-V.
Based on impulsive GVE approximations.
Parameters
----------
a, e, i : float
Current semi-major axis, eccentricity, inclination.
dv_total : float
Total $\Delta V$ budget (m/s).
Returns
-------
dict
Max theoretical $\Delta a, \Delta e, \Delta i$.
"""
v_circ = np.sqrt(self.mu / a)
# Max delta-a: Apply all delta-V along the velocity vector (Hohmann-like)
# dv = sqrt(mu/a) * (sqrt(2*r_next / (a+r_next)) - 1)
# Simplified: da = 2 * a * dv / v_orbit
max_da = 2 * a * dv_total / v_circ
# Max delta-e: Apply at perigee/apogee
max_de = 2 * dv_total / v_circ
# Max delta-i: Apply at nodes
# dv = 2 * v * sin(di/2)
max_di = 2 * np.arcsin(dv_total / (2 * v_circ))
return {
"max_delta_a": max_da,
"max_delta_e": max_de,
"max_delta_i": np.degrees(max_di),
}