Skip to content

Commit 9b51d72

Browse files
committed
doc: add JPL Horizons example
1 parent bed8ed3 commit 9b51d72

File tree

7 files changed

+1519
-2
lines changed

7 files changed

+1519
-2
lines changed

examples/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ This series of tutorials explain how to create custom applications based on
1111
.. nbgallery::
1212

1313
stk_engine/hohmann_transfer_using_targeter
14+
stk_engine/loading-ephem-from-jpl-horizons

examples/stk_engine/horizons_results.csv

Lines changed: 367 additions & 0 deletions
Large diffs are not rendered by default.

examples/stk_engine/horizons_results.txt

Lines changed: 479 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
# # Downloading ephemeris data from JPL Horizons
2+
#
3+
# This example shows how to download ephemeris data from JPL Horizons and use it in STK. A custom function is used to connect to the [NASA JPL Horizons System](https://ssd.jpl.nasa.gov/horizons/app.html) by using its [API service](https://ssd-api.jpl.nasa.gov/doc/horizons.html). After downloading the ephemerides file from the server, the file is adapted to STK ephemerides format by adding custom headers.
4+
5+
# ## What are ephemerides?
6+
#
7+
# Ephemerides files, often simply called ephemerides, are datasets containing information about the positions and movements of celestial objects such as planets, moons, asteroids, and comets. These files provide detailed information about the positions of these celestial bodies at specific points in time, typically given in coordinates such as right ascension and declination for celestial objects or ecliptic coordinates for objects within the solar system.
8+
#
9+
# In this example, the ephemerides of ['Oumuamua](https://ssd.jpl.nasa.gov/tools/sbdb_lookup.html#/?sstr=1I) are the ones studied.
10+
11+
# ## Downloading ephemerides from JPL Horizons System
12+
#
13+
# The JPL Horizons System provides a public API deployed in https://ssd.jpl.nasa.gov/api/horizons.api. Users can perform a ``GET`` request to previous endpoint and include different parameters in the query. Parameters allow to request desired data or indicate the output format. A complete list of the supported parameters can be found in the [official JPL Horizons System API reference](https://ssd-api.jpl.nasa.gov/doc/horizons.html).
14+
15+
JPL_HORIZONS_API = "https://ssd.jpl.nasa.gov/api/horizons.api"
16+
17+
# ### Defining the parameters
18+
#
19+
# STK ephemerides support a variety of parameters, see the [official STK ephemerides specification](https://help.agi.com/stk/12.8.0/index.htm#stk/importfiles-02.htm).
20+
#
21+
# To match previous specification, the parameters required for the JPL Horizons query are:
22+
#
23+
# - A ``Vector Table`` containing the data
24+
# - ``State Vector`` data
25+
# - CSV format
26+
#
27+
# In addition to previous ones, users can request a custom time span and coordinates center.
28+
#
29+
# Since this tutorial focuses on 1I/ʻOumuamua interstellar object, the JPL query parameters are the following:
30+
31+
# +
32+
common_parameters = {
33+
"format": "text",
34+
"COMMAND": "'DES=A/2017 U1;'", # Official indicator for Oumuamua
35+
"OBJ_DATA": "NO", # Do not include header data
36+
"MAKE_EPHEM": "YES", # Generate ephemerides for the query
37+
"EPHEM_TYPE": "VECTOR", # Request only vector data
38+
}
39+
40+
ephemerides_parameters = {
41+
"CENTER": "500@10", # Generate ephemerides w.r.t Sun's center
42+
"START_TIME": "2017-01-01", # Start time for the ephemerides
43+
"STOP_TIME": "2018-01-01", # Stop time for the ephemerides
44+
"STEP_SIZE": "1d", # Step size is 1 day
45+
"REF_SYSTEM": "ICRF", # Reference system is ICRF
46+
"VEC_TABLE": "2", # Request only state vector
47+
"OUT_UNITS": "KM-S", # Output units in Kilometers and seconds
48+
"CAL_FORMAT": "JD", # Dates must be in Julian Date
49+
"CSV_FORMAT": "YES", # Apply CSV formatting
50+
}
51+
52+
parameters = common_parameters | ephemerides_parameters
53+
# -
54+
55+
# ### Querying JPL Horizons System
56+
#
57+
# Once the query parameters are defined, an asynchronous function can be implemented for performing the query:
58+
59+
# +
60+
import httpx
61+
62+
63+
async def query_jpl_horizons(params: dict) -> str:
64+
"""Query JPL Horizons System with desired parameters.
65+
66+
Parameters
67+
----------
68+
params : dict
69+
Dictionary relating parameters and their values.
70+
71+
Returns
72+
-------
73+
str
74+
Ephemerides data in string format.
75+
76+
Notes
77+
-----
78+
For a complete list of valid parameters refer to https://ssd-api.jpl.nasa.gov/doc/horizons.html.
79+
80+
"""
81+
async with httpx.AsyncClient() as client:
82+
response = await client.get(JPL_HORIZONS_API, params=params)
83+
if response.status_code == 200:
84+
return response.text
85+
else:
86+
raise RuntimeError("Failed to retrieve data.")
87+
# -
88+
89+
# Finally, it is possible to query the JPL Horizons System:
90+
91+
oumuamua_ephem = await query_jpl_horizons(parameters)
92+
93+
# Previous data can be stored in a file, similarly to what JPL Horizons web application allows:
94+
95+
with open("horizons_results.txt", "w") as file:
96+
file.write(oumuamua_ephem)
97+
98+
# ## Cleaning ephemerides data
99+
#
100+
# The ephemerides file downloaded contains the following data:
101+
102+
# # !heat -n 50 horizons_results.txt
103+
104+
# This format is not supported by STK. The ephemerides data lies between the ``$$SOE`` (Start Of Ephemeris) and ``$EOE`` (End Of Ephemeris) keywords. In addition, no commas or headers must be present. Finally, some STK metadata needs to be included in the file, following [STK Ephemerides specification](https://help.agi.com/stk/12.8.0/index.htm#stk/importfiles-02.htm).
105+
#
106+
# The function below converts JPL Horizons ephemerides results to STK format:
107+
108+
# +
109+
import re
110+
111+
112+
def jpl_to_stk_ephem(jplfile: str, stkfile: str, version: str, metadata: dict) -> str:
113+
"""Convert from JPL Horizons Service ephemerides format into STK format.
114+
115+
Parameters
116+
----------
117+
jplfile : str
118+
Path string to JPL ephemerides file.
119+
stkfile: str
120+
Path string to STK ephemerides file.
121+
version: str
122+
STK version formatted as MAJOR.MINOR
123+
metadata: dict
124+
Desired metadata to be included in the STK ephemerides file.
125+
126+
Raises
127+
------
128+
ValueError
129+
If ``$$SEO`` and ``$$EOE`` guards are not found.
130+
131+
"""
132+
# Look for ephemerides from JPL Horizons results
133+
with open(jplfile, "r") as file:
134+
EPHEM_PATTERN = r'\$\$SOE(.*?)\$\$EOE'
135+
match = re.search(EPHEM_PATTERN, file.read(), re.DOTALL)
136+
if not match:
137+
raise ValueError("Could not find ephemerides guards $$SEO and $$EOE.")
138+
139+
# Extract JPL ephemerides data, remove empty lines, and split by commas
140+
jpl_ephem_data = match.group(1).strip().split(",")
141+
142+
# Drop calendar dates as they are not required in STK ephemerides
143+
ignore_indices = set(range(1, len(jpl_ephem_data), 8))
144+
desired_values = lambda value: jpl_ephem_data.index(value) not in ignore_indices
145+
stk_ephem_data = "".join(list(filter(desired_values, jpl_ephem_data)))
146+
147+
# Save data in STK ephemerides format
148+
with open(stkfile, "w") as file:
149+
file.write(f"stk.v.{version}\nBEGIN Ephemeris\n")
150+
151+
max_key_length = max(len(key) for key in metadata.keys())
152+
for key, value in metadata.items():
153+
file.write(f"\t{key.ljust(max_key_length)}\t\t{value}\n")
154+
155+
file.write(f"{stk_ephem_data}\nEND Ephemeris")
156+
157+
158+
# -
159+
160+
# Metadata needs to be consistent with the query. In this example, the following metadata applies:
161+
162+
metadata = {
163+
"InterpolationMethod": "Lagrange",
164+
"InterpolationOrder": "5",
165+
"DistanceUnit": "Kilometers",
166+
"CentralBody": "Sun",
167+
"CoordinateSystem": "ICRF",
168+
"TimeFormat": "JDate",
169+
"EphemerisTimePosVel": "",
170+
}
171+
172+
# Finally, it is possible to convert the ``horizons_result.txt`` file to an ephemerides file named ``oumuamua.e``:
173+
174+
jpl_to_stk_ephem(
175+
jplfile="horizons_results.txt",
176+
stkfile="oumuamua.e",
177+
version="12.9",
178+
metadata=metadata,
179+
)
180+
181+
# ## Using the ephemerides in STK
182+
#
183+
# Once the ephemerides have been downloaded, cleaned, and formatted as needed, it is possible to load them in STK.
184+
#
185+
# Start by launching an instance of STK by running:
186+
187+
# +
188+
from ansys.stk.core.stkengine import STKEngine
189+
190+
191+
stk = STKEngine.start_application(noGraphics=False)
192+
print(f"Using {stk.version}")
193+
# -
194+
195+
# ### Create a new scenario
196+
#
197+
# Start by creating a new scenario in STK. The scenario for simulating Oumuamua is set to have a time period that matches the one requested during the ephemerides query. In addition, the central body of this new scenario must be the Sun:
198+
199+
# +
200+
from ansys.stk.core.stkobjects import STK_OBJECT_TYPE
201+
202+
203+
root = stk.new_object_root()
204+
scenario = root.children.new_on_central_body(STK_OBJECT_TYPE.SCENARIO, "JPLHorizonsEphem", "Sun")
205+
scenario.set_time_period("1 Jan 2017", "1 Jan 2018")
206+
# -
207+
208+
# Make sure to rewind the scenario once created:
209+
210+
root.rewind()
211+
212+
# Next, you can display the scenario. Since the orbit of Oumuamua is very distant from the Sun, it is required to updated the far plane of the camera. This ensures that any object up to this distance is rendered on the scene:
213+
214+
# +
215+
from ansys.stk.core.stkengine.experimental.jupyterwidgets import GlobeWidget
216+
217+
218+
plotter = GlobeWidget(root, 640, 480)
219+
plotter.camera.far_plane = 1E12
220+
plotter.show()
221+
# -
222+
223+
# ### Visualizing the inner planets
224+
#
225+
# To better understand the orbit of Oumuamua and its trajectory through the solar system, the orbits of inner planets can be simulated. The default ephemerides provided by STK are used in this case:
226+
227+
# +
228+
from ansys.stk.core.stkobjects import STK_OBJECT_TYPE, PLANET_POSITION_SOURCE_TYPE, EPHEM_SOURCE_TYPE
229+
from ansys.stk.core.utilities.colors import Colors
230+
231+
232+
planets = ["Mercury", "Venus", "Earth", "Mars"]
233+
colors = [Colors.Gray, Colors.Orange, Colors.RoyalBlue, Colors.Red]
234+
235+
for planet_name, color in zip(planets, colors):
236+
planet = scenario.children.new(STK_OBJECT_TYPE.PLANET, planet_name)
237+
planet.common_tasks.set_position_source_central_body(planet_name, EPHEM_SOURCE_TYPE.DEFAULT)
238+
planet.graphics.color = color
239+
# -
240+
241+
# Ensure that the following configuration is declared to visualize the orbits and labels for vehicles and planets:
242+
243+
# +
244+
# General graphics configuration
245+
scenario.graphics.labels_visible = True
246+
247+
# Vehicle specific graphics
248+
scenario.graphics.orbits_visible = True
249+
250+
# Planet specific graphics
251+
scenario.graphics.planet_orbits_visible = True
252+
scenario.graphics.inertial_position_labels_visible = True
253+
scenario.graphics.inertial_position_visible = True
254+
scenario.graphics.sub_planet_points_visible = False
255+
scenario.graphics.sub_planet_labels_visible = False
256+
# -
257+
258+
# Next, display the scene to visualize the inner planets. The camera position is updated to have a detailed view of the scene:
259+
260+
plotter.camera.position = [-39909210.2975278, -717257963.8657558, -85235906.80896328]
261+
plotter.show()
262+
263+
# ### Simulating Oumuamua's orbit
264+
#
265+
# A satellite object can be used to simulate Omumuamua's orbit. The central body for this new object must be the Sun:
266+
267+
oumuamua = scenario.children.new_on_central_body(STK_OBJECT_TYPE.SATELLITE, "Oumuamua", "Sun")
268+
269+
# The ephemerides file created in the early stages of this notebook is used together with an external STK propagator:
270+
271+
# +
272+
from ansys.stk.core.stkobjects import VEHICLE_PROPAGATOR_TYPE, STK_EXTERNAL_EPHEMERIS_FORMAT
273+
274+
275+
oumuamua.set_propagator_type(VEHICLE_PROPAGATOR_TYPE.PROPAGATOR_STK_EXTERNAL)
276+
oumuamua.propagator.file_format = STK_EXTERNAL_EPHEMERIS_FORMAT.STK
277+
oumuamua.propagator.filename = "oumuamua.e"
278+
# -
279+
280+
# A yellow color is used for visualizing the orbit of Oumuamua. Make sure to increase the detail threshold up to the maximum so the orbit renders in the scene:
281+
282+
oumuamua.graphics.attributes.color = Colors.Yellow
283+
oumuamua.graphics_3d.model.detail_threshold.all = 1E12
284+
285+
# Next, propagate the orbit by running:
286+
287+
oumuamua.propagator.propagate()
288+
289+
# Finally, visualize the complete scene:
290+
291+
plotter.show()

0 commit comments

Comments
 (0)