Source code for m4opt.observer._spice
from typing import override
import numpy as np
import numpy.typing as npt
import spiceypy as spice
from astropy import units as u
from astropy.coordinates import EarthLocation
from astropy.time import Time
from astropy.utils.data import download_file
from ._core import ObserverLocation
def _time_to_et(time: Time) -> float | npt.NDArray[np.floating]:
"""Convert an Astropy time to a SPICE elapsed time since epoch."""
return (time.tdb - Time("J2000")).sec
# SPICE routines vectorized over time argument
_spkgps = np.vectorize(spice.spkgps, excluded=[0, 2, 3], signature="()->(m),()")
[docs]
class SpiceObserverLocation(ObserverLocation):
"""A satellite whose orbit is specified by `Spice <https://naif.jpl.nasa.gov/naif/>`_ kernels.
Examples
--------
Load an example Spice kernel from a file:
>>> from astropy.time import Time
>>> from astropy import units as u
>>> from m4opt.observer import SpiceObserverLocation
>>> import numpy as np
>>> orbit = SpiceObserverLocation(
... 'MGS SIMULATION',
... 'https://archive.stsci.edu/missions/tess/models/TESS_EPH_PRE_LONG_2021252_21.bsp',
... 'https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/earth_latest_high_prec.bpc',
... 'https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/pck00010.tpc')
>>> t0 = Time('2021-10-31 00:00')
>>> orbit(t0)
<EarthLocation (259589.01504305, 267775.69181568, -6003.44398346) km>
>>> orbit(t0 + np.arange(4) * u.hour).shape
(4,)
""" # noqa: E501
def __init__(self, target: str, *kernels: str):
for kernel in kernels:
spice.furnsh(download_file(kernel, cache=True))
self._target = spice.bodn2c(target)
self._body = spice.bodn2c("EARTH")
[docs]
@override
def __call__(self, time):
et = _time_to_et(time)
pos, _ = _spkgps(self._target, et, "IAU_EARTH", self._body)
return EarthLocation.from_geocentric(*pos.T, unit=u.km)