"""
SPICE Kernel and Frame management for STK/SPICE interoperability.
"""
import os
from typing import List, Optional
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] = []
if not SPICEYPY_AVAILABLE:
pass
[docs]
def load_kernel(self, kernel_path: str) -> None:
"""Load a SPICE kernel (.bsp, .tpc, .tls, etc.)."""
if not SPICEYPY_AVAILABLE:
raise ImportError(
"spiceypy is required for SPICE manager. Install via 'pip install OpenGNC[spk]'"
)
if os.path.exists(kernel_path):
spice.furnsh(kernel_path)
self.loaded_kernels.append(kernel_path)
else:
raise FileNotFoundError(f"Kernel not found: {kernel_path}")
[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 UTC string to Ephemeris Time (ET)."""
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 state vector of a target body.
Returns
-------
np.ndarray
[x, y, z, vx, vy, vz] in meters and m/s.
"""
if not SPICEYPY_AVAILABLE:
raise ImportError(
"spiceypy is required for SPICE manager. Install via 'pip install OpenGNC[spk]'"
)
state, lt = spice.spkezr(target, et, frame, "NONE", observer)
# state is in km and km/s
return np.array(state) * 1000.0