"""
SPICE kernel and frame management for STK/SPICE interoperability.
"""
from pathlib import Path
from typing import cast
import numpy as np
try:
import spiceypy as spice
SPICEYPY_AVAILABLE = True
except ImportError:
SPICEYPY_AVAILABLE = False
[docs]
class SpiceManager:
"""
Standardized interface for SPICE kernels and frame transformations.
Wraps SpiceyPy to provide STK-compatible environment modeling.
"""
def __init__(self) -> None:
self.loaded_kernels: list[str] = []
[docs]
def load_kernel(self, kernel_path: str) -> None:
"""Load a SPICE kernel such as ``.bsp``, ``.tpc``, or ``.tls``."""
if not SPICEYPY_AVAILABLE:
raise ImportError(
"spiceypy is required for SPICE manager. Install via 'pip install OpenGNC[spk]'"
)
kernel = Path(kernel_path)
if not kernel.exists():
raise FileNotFoundError(f"Kernel not found: {kernel_path}")
spice.furnsh(str(kernel))
self.loaded_kernels.append(str(kernel))
[docs]
def clear_kernels(self) -> None:
"""Unload all kernels."""
if SPICEYPY_AVAILABLE:
spice.kclear()
self.loaded_kernels = []
[docs]
def utc_to_et(self, utc_str: str) -> float:
"""Convert a UTC string to ephemeris time."""
if not SPICEYPY_AVAILABLE:
raise ImportError(
"spiceypy is required for SPICE manager. Install via 'pip install OpenGNC[spk]'"
)
return float(spice.str2et(utc_str))
[docs]
def get_state(
self, target: str, et: float, frame: str = "J2000", observer: str = "EARTH"
) -> np.ndarray:
"""
Get the state vector of a target body.
Returns
-------
np.ndarray
``[x, y, z, vx, vy, vz]`` in meters and meters per second.
"""
if not SPICEYPY_AVAILABLE:
raise ImportError(
"spiceypy is required for SPICE manager. Install via 'pip install OpenGNC[spk]'"
)
state, _ = spice.spkezr(target, et, frame, "NONE", observer)
return cast(np.ndarray, np.asarray(state, dtype=float) * 1000.0)