ANISE¶
ANISE is a modern rewrite of NAIF SPICE, written in Rust and providing interfaces to other languages including Python.
Evidently, this tutorial applies to the Python usage of ANISE.
Goal¶
By the end of this tutorial, you should know how to build a data frame containing the Sun probe Earth angle of a given spacecraft BSP and plot that information. Your exercise will be to confirm that this calculation is correct by computing the Sun elevation at nadir below the spacecraft as detailed in tutorial 04.## Loading the latest orientation and planetary data
Let's start by installing ANISE: pip install anise
Load a BSP containing a spacecraft trajectory¶
In this tutorial, we will use the gmat-hermite.bsp
file which contains some trajectory data build in GMAT and used in ANISE for validation purposes. Although the Sun probe Earth angle typically applies to spacecraft trajectories, the calculation works for any two objects.
from anise import MetaAlmanac
almanac = MetaAlmanac.latest().load("../../data/gmat-hermite.bsp")
almanac.describe(spk=True)
=== SPK #0 === ┌─────────────┬──────────────────────┬─────────────┬───────────────────────────────────┬───────────────────────────────────┬────────────┬──────────────────────┐ │ Name │ Target │ Center │ Start epoch │ End epoch │ Duration │ Interpolation kind │ ├─────────────┼──────────────────────┼─────────────┼───────────────────────────────────┼───────────────────────────────────┼────────────┼──────────────────────┤ │ SPK_SEGMENT │ body -10000001 J2000 │ Earth J2000 │ 2000-01-01T12:00:32.183927328 TDB │ 2000-01-01T15:20:32.183931556 TDB │ 3 h 20 min │ Hermite Unequal Step │ └─────────────┴──────────────────────┴─────────────┴───────────────────────────────────┴───────────────────────────────────┴────────────┴──────────────────────┘ === SPK #1 === ┌────────────────┬─────────────────────────────┬─────────��─────────────────────┬───────────────────────────────────┬───────────────────────────────────┬─────────────┬────────────────────┐ │ Name │ Target │ Center │ Start epoch │ End epoch │ Duration │ Interpolation kind │ ├────────────────┼─────────────────────────────┼───────────────────────────────┼───────────────────────────────────┼───────────────────��───────────────┼─────────────┼────────────────────┤ │ DE-0440LE-0440 │ Mercury J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Venus J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Earth-Moon Barycenter J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Mars Barycenter J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Jupiter Barycenter J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Saturn Barycenter J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Uranus Barycenter J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Neptune Barycenter J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Pluto Barycenter J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Sun J2000 │ Solar System Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Moon J2000 │ Earth-Moon Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ Earth J2000 │ Earth-Moon Barycenter J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ body 199 J2000 │ Mercury J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ │ DE-0440LE-0440 │ body 299 J2000 │ Venus J2000 │ 1849-12-26T00:00:00.000046818 TDB │ 2150-01-21T23:59:59.999955683 TDB │ 109600 days │ Chebyshev Triplet │ └────────────────┴─────────────────────────────┴───────────────────────────────┴───────────────────────────────────┴───────────────────────────────────┴─────────────┴────────────────────┘
We have successfully loaded two BSP files, one with the planetary data and the other with spacecraft data. We now know that this spacecraft has the ID -10000001
. We can actually query the Almanac for the precise start and stop epochs (returned as hifitime Epoch
objects) of this ID in our loaded files.
start_epoch, stop_epoch = almanac.spk_domain(-10000001)
print(start_epoch, stop_epoch)
2000-01-01T11:59:28.000000021 UTC 2000-01-01T15:19:28.000000226 UTC
The Sun Probe Earth angle is the angle between a probe and the Sun and the probe the Earth. It allows one to know whether the point exactly nadir (i.e. below) the spacecraft is illuminated by the Sun or not. This is helpful information if the spacecraft carries a visible light camera and needs to take pictures when it's daytime below the spacecraft.
Let's look at the signature of this function.
almanac.sun_angle_deg?
Signature: almanac.sun_angle_deg(target_id, observer_id, epoch) Docstring: Returns the angle (between 0 and 180 degrees) between the observer and the Sun, and the observer and the target body ID. This computes the Sun Probe Earth angle (SPE) if the probe is in a loaded, its ID is the "observer_id", and the target is set to its central body. # Geometry If the SPE is greater than 90 degrees, then the celestial object below the probe is in sunlight. ## Sunrise at nadir ```text Sun | \ | \ | \ Obs. -- Target ``` ## Sun high at nadir ```text Sun \ \ __ θ > 90 \ \ Obs. ---------- Target ``` ## Sunset at nadir ```text Sun / / __ θ < 90 / / Obs. -- Target ``` # Algorithm 1. Compute the position of the Sun as seen from the observer 2. Compute the position of the target as seen from the observer 3. Return the arccosine of the dot product of the norms of these vectors. Type: builtin_function_or_method
One will note that this function is generic to what the "probe" SPK ID is ("observer") and what its central object should be instead of Earth ("target").
The other crucial point here is that this is one of the few functions where the object ID is required instead of a frame. This is because the Almanac will compute everything in the J2000 frame. Don't worry, if you have frame objects instead, you may use the sun_angle_deg_from_frame
function instead.
Let's see what is the SPE of our spacecraft at the start of the trajectory.
from anise.astro.constants import CelestialObjects
almanac.sun_angle_deg(-10000001, CelestialObjects.EARTH, start_epoch)
83.87312777296376
A angle of less than 90 degrees means that the nadir point is in the darkness. Let's look at the evolution of the SPE over the duration of the trajectory.
Package installation for plotting¶
%pip install "polars[plot]" hvplot geoviews # geoviews for geographic data
Requirement already satisfied: polars[plot] in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (0.20.4) Requirement already satisfied: hvplot in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (0.9.1) Collecting geoviews Downloading geoviews-1.11.0-py2.py3-none-any.whl (511 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 511.2/511.2 kB 690.4 kB/s eta 0:00:00 kB/s eta 0:00:01:01 Requirement already satisfied: bokeh>=1.0.0 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from hvplot) (3.3.3) Requirement already satisfied: colorcet>=2 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from hvplot) (3.0.1) Requirement already satisfied: holoviews>=1.11.0 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from hvplot) (1.18.1) Requirement already satisfied: pandas in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from hvplot) (2.1.4) Requirement already satisfied: numpy>=1.15 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from hvplot) (1.26.3) Requirement already satisfied: packaging in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from hvplot) (23.2) Requirement already satisfied: panel>=0.11.0 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from hvplot) (1.3.6) Requirement already satisfied: param<3.0,>=1.12.0 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from hvplot) (2.0.1) Collecting cartopy>=0.18.0 (from geoviews) Downloading Cartopy-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (11.9 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.9/11.9 MB 14.1 MB/s eta 0:00:00m eta 0:00:01[36m0:00:01 Collecting shapely (from geoviews) Downloading shapely-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.5/2.5 MB 54.5 MB/s eta 0:00:0031m72.7 MB/s eta 0:00:01 Collecting pyproj (from geoviews) Downloading pyproj-3.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (8.6 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.6/8.6 MB 13.8 MB/s eta 0:00:00m eta 0:00:010:01:01 Requirement already satisfied: xyzservices in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from geoviews) (2023.10.1) Requirement already satisfied: Jinja2>=2.9 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from bokeh>=1.0.0->hvplot) (3.1.2) Requirement already satisfied: contourpy>=1 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from bokeh>=1.0.0->hvplot) (1.2.0) Requirement already satisfied: pillow>=7.1.0 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from bokeh>=1.0.0->hvplot) (10.2.0) Requirement already satisfied: PyYAML>=3.10 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from bokeh>=1.0.0->hvplot) (6.0.1) Requirement already satisfied: tornado>=5.1 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from bokeh>=1.0.0->hvplot) (6.4) Collecting matplotlib>=3.4 (from cartopy>=0.18.0->geoviews) Downloading matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (11.6 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.6/11.6 MB 47.7 MB/s eta 0:00:00m eta 0:00:01[36m0:00:01 Collecting pyshp>=2.1 (from cartopy>=0.18.0->geoviews) Downloading pyshp-2.3.1-py2.py3-none-any.whl (46 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 46.5/46.5 kB 14.8 MB/s eta 0:00:00 Requirement already satisfied: pyct>=0.4.4 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from colorcet>=2->hvplot) (0.5.0) Requirement already satisfied: pyviz-comms>=0.7.4 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from holoviews>=1.11.0->hvplot) (3.0.0) Requirement already satisfied: python-dateutil>=2.8.2 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from pandas->hvplot) (2.8.2) Requirement already satisfied: pytz>=2020.1 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from pandas->hvplot) (2023.3.post1) Requirement already satisfied: tzdata>=2022.1 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from pandas->hvplot) (2023.4) Requirement already satisfied: markdown in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from panel>=0.11.0->hvplot) (3.5.2) Requirement already satisfied: markdown-it-py in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from panel>=0.11.0->hvplot) (3.0.0) Requirement already satisfied: linkify-it-py in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from panel>=0.11.0->hvplot) (2.0.2) Requirement already satisfied: mdit-py-plugins in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from panel>=0.11.0->hvplot) (0.4.0) Requirement already satisfied: requests in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from panel>=0.11.0->hvplot) (2.31.0) Requirement already satisfied: tqdm>=4.48.0 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from panel>=0.11.0->hvplot) (4.66.1) Requirement already satisfied: bleach in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from panel>=0.11.0->hvplot) (6.1.0) Requirement already satisfied: typing-extensions in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from panel>=0.11.0->hvplot) (4.9.0) Requirement already satisfied: certifi in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from pyproj->geoviews) (2023.11.17) Requirement already satisfied: MarkupSafe>=2.0 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from Jinja2>=2.9->bokeh>=1.0.0->hvplot) (2.1.3) Collecting cycler>=0.10 (from matplotlib>=3.4->cartopy>=0.18.0->geoviews) Downloading cycler-0.12.1-py3-none-any.whl (8.3 kB) Collecting fonttools>=4.22.0 (from matplotlib>=3.4->cartopy>=0.18.0->geoviews) Downloading fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.9 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.9/4.9 MB 49.5 MB/s eta 0:00:0031m56.4 MB/s eta 0:00:01 Collecting kiwisolver>=1.3.1 (from matplotlib>=3.4->cartopy>=0.18.0->geoviews) Downloading kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.4 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.4/1.4 MB 47.4 MB/s eta 0:00:00 Collecting pyparsing>=2.3.1 (from matplotlib>=3.4->cartopy>=0.18.0->geoviews) Downloading pyparsing-3.1.1-py3-none-any.whl (103 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 103.1/103.1 kB 42.5 MB/s eta 0:00:00 Requirement already satisfied: six>=1.5 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from python-dateutil>=2.8.2->pandas->hvplot) (1.16.0) Requirement already satisfied: webencodings in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from bleach->panel>=0.11.0->hvplot) (0.5.1) Requirement already satisfied: uc-micro-py in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from linkify-it-py->panel>=0.11.0->hvplot) (1.0.2) Requirement already satisfied: mdurl~=0.1 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from markdown-it-py->panel>=0.11.0->hvplot) (0.1.2) Requirement already satisfied: charset-normalizer<4,>=2 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from requests->panel>=0.11.0->hvplot) (3.3.2) Requirement already satisfied: idna<4,>=2.5 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from requests->panel>=0.11.0->hvplot) (3.6) Requirement already satisfied: urllib3<3,>=1.21.1 in /home/chris/Workspace/nyx-space/anise/anise-py/.venv/lib64/python3.11/site-packages (from requests->panel>=0.11.0->hvplot) (2.1.0) Installing collected packages: shapely, pyshp, pyproj, pyparsing, kiwisolver, fonttools, cycler, matplotlib, cartopy, geoviews Successfully installed cartopy-0.22.0 cycler-0.12.1 fonttools-4.47.2 geoviews-1.11.0 kiwisolver-1.4.5 matplotlib-3.8.2 pyparsing-3.1.1 pyproj-3.6.1 pyshp-2.3.1 shapely-2.0.2 [notice] A new release of pip is available: 23.1.2 -> 23.3.2 [notice] To update, run: pip install --upgrade pip Note: you may need to restart the kernel to use updated packages.
Evolution of SPE over time¶
We'll plot the change in Sun probe Earth angle over time. We'll also grab the latitude and longitude data so we can plot the position of the spacecraft above the Earth at those times (as a separate plot).
from anise.time import TimeSeries, Unit, Epoch
from anise.astro import Frame
from anise.astro.constants import Frames, Orientations
import polars as pl
from datetime import datetime
def hifitime_to_datetime(e: Epoch) -> datetime:
return datetime.fromisoformat(str(e).replace(" UTC", "")[:23])
epochs = []
spe_deg = []
lat_deg = []
long_deg = []
# Let's be sure to load the PCK data, which includes the frame information such as the shape of
# the ellipsoid and how to compute the rotation of the body fixed frames
almanac = almanac.load("../../data/pck08.pca")
SC_ID = -10000001
SC_J2K = Frame(SC_ID, Orientations.J2000)
for epoch in TimeSeries(start_epoch, stop_epoch, Unit.Minute*1, inclusive=True):
epochs += [hifitime_to_datetime(epoch)]
spe_deg += [almanac.sun_angle_deg(CelestialObjects.EARTH, SC_ID, epoch)]
# Grab position of the spacecraft in the IAU Earth frame
sc_iau_earth = almanac.transform(SC_J2K, Frames.IAU_EARTH_FRAME, epoch)
lat_deg += [sc_iau_earth.latitude_deg()]
long_deg += [sc_iau_earth.longitude_deg()]
# Build the data frame
df = pl.DataFrame(
{
'Epoch': epochs,
'Sun Probe Earth angle (deg)': spe_deg,
'Latitude (deg)': lat_deg,
'Longitude (deg)': long_deg
}
)
import hvplot.polars
from bokeh.models.formatters import DatetimeTickFormatter
formatter = DatetimeTickFormatter(days='%d/%m') # Make the world a better place
df.hvplot(x="Epoch", y="Sun Probe Earth angle (deg)", xformatter=formatter,
title="Sun probe Earth angle over time", hover_cols=["Latitude (deg)", "Longitude (deg)"])
df.hvplot.points('Longitude (deg)', 'Latitude (deg)', geo=True, color='red', tiles='ESRI', xlim=(0, 360), ylim=(-60, 60), hover_cols=["Epoch", "Sun Probe Earth angle (deg)"])
Exercise¶
The goal of this exercise is to show that the SPE is essentially the Sun elevation angle from the position exactly nadir of the vehicle plus 90 degrees (or so, since the Earth isn't a perfect sphere). It also shows you how to combine the different functionality you've seen with the other tutorials to solve similar problem.
Note: this is the verify_geometry
Rust test, but rebuilt in Python.
- For the whole spacecraft trajectory, build the locality exactly nadir of it at each epoch (remember that a
TimeSeries
instance can only be used once, so you'll need to rebuild a new one). For this step, initialize a newOrbit
instance from the latitude and longitude constructor using the position of the spacecraft in the IAU Earth frame as we did above. - At each epoch, grab the state of the Sun as seen from the Earth (both can be in the J2000 frame since the AER computation will transform the states into the correct frames anyway).
- Call the azimuth, elevtion, and range function of your loaded Almanac, and store the elevation of the Sun as seen from the point exactly nadir of the spacecraft.
- Plot the elevation data in degrees compared to the SPE angle calculated above.