"""
JPL Ephemeris integration using jplephem.
"""
import os
from typing import Optional, cast
import numpy as np
from jplephem.spk import SPK
[docs]
class JPLEphemeris:
"""
Handler for JPL SPK (Space Property Kernel) files.
Provides high-precision position and velocity for planets, Sun, and Moon.
Parameters
----------
spk_path : str, optional
Path to the .bsp SPK file (e.g., de421.bsp).
"""
def __init__(self, spk_path: Optional[str] = None) -> None:
"""Initialize SPK kernel."""
self.kernel = None
if spk_path and os.path.exists(spk_path):
self.kernel = SPK.open(spk_path)
else:
# Look for default in package directory if available
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
default_path = os.path.join(base_dir, "de421.bsp")
if os.path.exists(default_path):
self.kernel = SPK.open(default_path)
[docs]
def get_position(self, body_id: int, jd: float) -> np.ndarray:
"""
Get body position in km (usually relative to solar system barycenter).
Parameters
----------
body_id : int
JPL Body ID (e.g., 399 for Earth, 301 for Moon, 10 for Sun).
jd : float
Julian Date.
Returns
-------
np.ndarray
Position vector [x, y, z] in meters.
"""
if self.kernel is None:
raise RuntimeError("SPK Kernel not loaded. Provide a valid .bsp file.")
# jplephem returns km
pos_km = self.kernel[0, body_id].compute(jd)
return cast(np.ndarray, pos_km * 1000.0)
[docs]
def get_state(self, body_id: int, jd: float) -> np.ndarray:
"""
Get body position and velocity.
Parameters
----------
body_id : int
JPL Body ID.
jd : float
Julian Date.
Returns
-------
np.ndarray
State vector [x, y, z, vx, vy, vz] in m, m/s.
"""
if self.kernel is None:
raise RuntimeError("SPK Kernel not loaded.")
# compute_and_differentiate returns [x, y, z, vx, vy, vz] in km, km/day
state_km = self.kernel[0, body_id].compute_and_differentiate(jd)
pos = state_km[:3] * 1000.0
vel = state_km[3:] * 1000.0 / 86400.0
return cast(np.ndarray, np.concatenate([pos, vel]))