-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add spice infrastructure code for spice bodies and frames
Add geometry functions for computing IMAP spacecraft state (position, velocity) Add IMAP SPK kernel from Nick's 366 day simulation Add test coverage for new geometry functions
- Loading branch information
1 parent
8ef0cb6
commit f3ec1af
Showing
4 changed files
with
164 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,128 @@ | ||
"""Functions for computing geometry using SPICE.""" | ||
"""Functions for computing geometry, many of which use SPICE.""" | ||
|
||
from collections.abc import Iterable | ||
from enum import Enum | ||
from typing import NamedTuple, Optional, Union | ||
|
||
import numpy as np | ||
import spiceypy as spice | ||
|
||
from imap_processing.spice.kernels import ensure_spice | ||
|
||
|
||
class SpiceId(NamedTuple): | ||
"""Class that represents a unique identifier in the NAIF SPICE library.""" | ||
|
||
strid: str | ||
numid: int | ||
|
||
|
||
class SpiceBody(Enum): | ||
"""Enum containing SPICE IDs for bodies that we use.""" | ||
|
||
# A subset of IMAP Specific bodies as defined in imap_wkcp.tf | ||
IMAP = SpiceId("IMAP", -43) | ||
IMAP_SPACECRAFT = SpiceId("IMAP_SPACECRAFT", -43000) | ||
# IMAP Pointing Frame (Despun) as defined in iamp_science_0001.tf | ||
IMAP_DPS = SpiceId("IMAP_DPS", -43901) | ||
# Standard NAIF bodies | ||
SSB = SpiceId("SOLAR_SYSTEM_BARYCENTER", 0) | ||
SUN = SpiceId("SUN", 10) | ||
EARTH = SpiceId("EARTH", 399) | ||
|
||
|
||
class SpiceFrame(Enum): | ||
"""Enum containing SPICE IDs for reference frames, defined in imap_wkcp.tf.""" | ||
|
||
# Standard SPICE Frames | ||
J2000 = SpiceId("J2000", 1) | ||
ECLIPJ2000 = SpiceId("ECLIPJ2000", 17) | ||
# IMAP specific | ||
IMAP_SPACECRAFT = SpiceId("IMAP_SPACECRAFT", -43000) | ||
IMAP_LO_BASE = SpiceId("IMAP_LO_BASE", -43100) | ||
IMAP_LO_STAR_SENSOR = SpiceId("IMAP_LO_STAR_SENSOR", -43103) | ||
IMAP_LO = SpiceId("IMAP_LO", -43105) | ||
IMAP_HI_45 = SpiceId("IMAP_HI_45", -43150) | ||
IMAP_HI_90 = SpiceId("IMAP_HI_90", -43160) | ||
IMAP_ULTRA_45 = SpiceId("IMAP_ULTRA_45", -43200) | ||
IMAP_ULTRA_90 = SpiceId("IMAP_ULTRA_90", -43210) | ||
IMAP_MAG = SpiceId("IMAP_MAG", -43250) | ||
IMAP_SWE = SpiceId("IMAP_SWE", -43300) | ||
IMAP_SWAPI = SpiceId("IMAP_SWAPI", -43350) | ||
IMAP_CODICE = SpiceId("IMAP_CODICE", -43400) | ||
IMAP_HIT = SpiceId("IMAP_HIT", -43500) | ||
IMAP_IDEX = SpiceId("IMAP_IDEX", -43700) | ||
IMAP_GLOWS = SpiceId("IMAP_GLOWS", -43750) | ||
|
||
|
||
def imap_state( | ||
et: Union[np.ndarray, float], | ||
ref_frame: Optional[SpiceFrame] = None, | ||
observer: Optional[SpiceBody] = None, | ||
) -> Union[np.ndarray, Iterable[np.ndarray]]: | ||
""" | ||
Get the state (position and velocity) of the IMPA spacecraft. | ||
By default, the state is returned in the ECLIPJ2000 frame as observed by the Sun. | ||
Parameters | ||
---------- | ||
et : np.ndarray or float | ||
Epoch time(s) [J2000 seconds] to get the IMAP state for. | ||
ref_frame : SpiceFrame, optional | ||
Reference frame which the IMAP state is expressed in. | ||
observer : SpiceBody, optional | ||
Observing body. | ||
Returns | ||
------- | ||
state : np.ndarray or Iterable[np.ndarray] | ||
The Cartesian state vector representing the position and velocity of the | ||
IMAP spacecraft. | ||
""" | ||
if ref_frame is None: | ||
ref_frame = SpiceFrame.ECLIPJ2000 | ||
if observer is None: | ||
observer = SpiceBody.SUN | ||
state, light_time = ensured_spkezr( | ||
SpiceBody.IMAP.name, et, ref_frame.name, "NONE", observer.name | ||
) | ||
return state | ||
|
||
|
||
def ensured_spkezr( | ||
targ: str, et: Union[np.ndarray, float], ref: str, abcorr: str, obs: str | ||
) -> Union[tuple[np.ndarray, float], tuple[Iterable[np.ndarray], Iterable[float]]]: | ||
""" | ||
Wrap spice.spkezr() function with ensure_spice. | ||
Parameters | ||
---------- | ||
targ : str | ||
Target body name. | ||
et : ndarray or float | ||
J2000 observer times. | ||
ref : str | ||
Reference frame name. | ||
abcorr : str | ||
Aberration correction method. | ||
obs : str | ||
Observing body name. | ||
Returns | ||
------- | ||
state : np.ndarray or Iterable[np.ndarray] | ||
State of target. | ||
light_time : float or Iterable[float] | ||
One way light time between observer and target. | ||
Notes | ||
----- | ||
https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/FORTRAN/spicelib/spkezr.html | ||
""" | ||
# No vectorization is needed b/c spiceypy already adds vectorization to the | ||
# spkezr function. If specific time coverage functionality is added to | ||
# @ensure_spice, parameters can be added here. | ||
ensured = ensure_spice(spice.spkezr) | ||
state, light_time = ensured(targ, et, ref, abcorr, obs) | ||
return state, light_time |
Binary file not shown.
3 changes: 2 additions & 1 deletion
3
imap_processing/tests/spice/test_data/imap_test_metakernel.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
{SPICE_TEST_DATA_PATH}/imap_sclk_0000.tsc | ||
{SPICE_TEST_DATA_PATH}/naif0012.tls | ||
{SPICE_TEST_DATA_PATH}/naif0012.tls | ||
{SPICE_TEST_DATA_PATH}/imap_spk_demo.bsp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,35 @@ | ||
"""Tests coverage for imap_processing/spice/geometry.py""" | ||
|
||
import numpy as np | ||
|
||
from imap_processing.spice.geometry import ( | ||
SpiceBody, | ||
SpiceFrame, | ||
ensured_spkezr, | ||
imap_state, | ||
) | ||
|
||
|
||
def test_imap_state(use_test_metakernel): | ||
"""Test coverage for imap_state()""" | ||
et = np.linspace(798033670, 798034670) | ||
state = imap_state(et, observer=SpiceBody.EARTH) | ||
assert len(state) == len(et) | ||
|
||
|
||
def test_ensured_spkezr(use_test_metakernel): | ||
"""Test coverage for ensured_spkezr()""" | ||
# The imap_spk_demo.bsp kernel provides ephemeris coverage for 2025-04-15 to | ||
# 2026-04-16. The kernel provides the IMAP ephemeris relative to Earth, so | ||
# only the position relative to Earth can be queried without loading | ||
# additional kernels. | ||
# The queried ET, 798033670 is ~2025-04-16T00:00:00.0 | ||
state, lt = ensured_spkezr( | ||
SpiceBody.IMAP.name, | ||
798033670, | ||
SpiceFrame.ECLIPJ2000.name, | ||
"NONE", | ||
SpiceBody.EARTH.name, | ||
) | ||
assert state.shape == (6,) | ||
assert isinstance(lt, float) |