OSTk Cross-Platform Validation Against GMAT/Orekit (Mission Sequence Scenario 2)¶
This tutorial demonstrates how to compare OSTk numerical orbit propagation with other tools like Orekit or GMAT. This example will be for a mission sequence that consists of a coasting phases.
import csv
import os
import numpy as np
import pandas as pd
import plotly.graph_objs as go
from ostk.core.filesystem import Directory
from ostk.mathematics.geometry.d3.object import Cuboid
from ostk.mathematics.geometry.d3.object import Composite
from ostk.mathematics.geometry.d3.object import Point
from ostk.physics import Environment
from ostk.physics.environment.atmospheric import Earth as EarthAtmosphericModel
from ostk.physics.environment.gravitational import Earth as EarthGravitationalModel
from ostk.physics.environment.magnetic import Earth as EarthMagneticModel
from ostk.physics.environment.object.celestial import Earth
from ostk.physics.coordinate import Frame
from ostk.physics.coordinate import Position
from ostk.physics.coordinate import Velocity
from ostk.physics.time import DateTime
from ostk.physics.time import Duration
from ostk.physics.time import Instant
from ostk.physics.time import Interval
from ostk.physics.time import Scale
from ostk.physics.unit import Mass
from ostk.astrodynamics import Dynamics
from ostk.astrodynamics.trajectory.state import NumericalSolver
from ostk.astrodynamics.flight.system import SatelliteSystem
from ostk.astrodynamics.trajectory.state import CoordinateSubset
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianPosition
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianVelocity
from ostk.astrodynamics.trajectory import StateBuilder
from ostk.astrodynamics.trajectory import Propagator
from ostk.astrodynamics.trajectory import State
Set up Comparison Files¶
Define array inputs that can be changed to include/exclude gmat/orekit result comparisons
title_prefix = "EGM96 60x60 Gravity"
filenames = [
"gmat_astrodynamics/scenario002-mission-sequence.csv",
"orekit_astrodynamics/scenario002-mission-sequence.csv",
]
legend_name_list = ["GMAT", "OREKIT"]
multiplication_factors = [
np.array([1.0e3, 1.0e3, 1.0e3]),
np.array([1.0, 1.0, 1.0]),
] # GMAT and Orekit don't output in the same units
Setup Comparison Scenario in OSTk¶
Define the physical Environment
earth = Earth.from_models(
EarthGravitationalModel(
EarthGravitationalModel.Type.EGM96, Directory.undefined(), 60, 60
),
EarthMagneticModel(EarthMagneticModel.Type.Undefined),
EarthAtmosphericModel(EarthAtmosphericModel.Type.Undefined),
)
environment = Environment(Instant.J2000(), [earth])
Define the SatelliteSystem
mass = Mass(100.0, Mass.Unit.Kilogram)
satellite_geometry = Composite(
Cuboid(
Point(0.0, 0.0, 0.0),
np.eye(3).tolist(),
[1.0, 0.0, 0.0],
)
)
inertia_tensor = np.identity(3)
surface_area = 1.0
drag_coefficient = 2.2
satellite_system = SatelliteSystem(
mass,
satellite_geometry,
inertia_tensor,
surface_area,
drag_coefficient,
)
Define the initial State
of the SatelliteSystem
start_instant = Instant.date_time(DateTime.parse("2023-01-01T00:00:00.000"), Scale.UTC)
initial_state = State(
start_instant,
Position.meters(
np.array([-4283387.412456233, -4451426.776125101, -2967617.850750065]),
Frame.GCRF(),
),
Velocity.meters_per_second(
np.array([4948.074939732174, -957.3429532772124, -5721.173027553034]),
Frame.GCRF(),
),
)
state_builder = StateBuilder(
frame=Frame.GCRF(),
coordinate_subsets=[
CartesianPosition.default(),
CartesianVelocity.default(),
CoordinateSubset.mass(),
CoordinateSubset.surface_area(),
CoordinateSubset.drag_coefficient(),
],
)
coordinates = [
*initial_state.get_coordinates().tolist(),
mass.in_kilograms(),
satellite_system.get_cross_sectional_surface_area(),
satellite_system.get_drag_coefficient(),
]
start_state = state_builder.build(initial_state.get_instant(), coordinates)
Define the Dynamics to consider for Propagation
dynamics = Dynamics.from_environment(environment)
Define a NumericalSolver
numerical_solver = NumericalSolver(
NumericalSolver.LogType.NoLog,
NumericalSolver.StepperType.RungeKuttaDopri5,
30.0,
1.0e-12,
1.0e-12,
)
Define a Propagator
propagator = Propagator(numerical_solver, dynamics)
Propagate with OSTk¶
Define the propagation instants
of interest
instants = Interval.closed(
start_state.get_instant(), start_state.get_instant() + Duration.seconds(86400.0)
).generate_grid(Duration.seconds(120.0))
Now that everything is set up, we can calculate the state arrays from the desired time instant grid
ostk_states = propagator.calculate_states_at(start_state, instants)
Process Cross Platform Results¶
Read in reference data from CSV file for GMAT and Orekit
all_comparison_states = []
for ind, filename in enumerate(filenames):
with open(f"{os.getcwd()}/data/{filename}") as csvfile:
reader = csv.DictReader(csvfile)
comparison_states = []
comparison_instants = []
for row in reader:
instant_iter = start_instant + Duration.seconds(
float(row[reader.fieldnames[0]])
)
position_iter = Position.meters(
[
row[reader.fieldnames[1]],
row[reader.fieldnames[2]],
row[reader.fieldnames[3]],
],
Frame.GCRF(),
)
velocity_iter = Velocity.meters_per_second(
[
row[reader.fieldnames[4]],
row[reader.fieldnames[5]],
row[reader.fieldnames[6]],
],
Frame.GCRF(),
)
position_iter_m = np.multiply(
position_iter.get_coordinates(), multiplication_factors[ind]
)
velocity_iter_ms = np.multiply(
velocity_iter.get_coordinates(), multiplication_factors[ind]
)
comparison_instants.append(instant_iter)
comparison_states.append(
State(
instant_iter,
Position.meters(position_iter_m, Frame.GCRF()),
Velocity.meters_per_second(velocity_iter_ms, Frame.GCRF()),
)
)
all_comparison_states.append(comparison_states)
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[12], line 4
1 all_comparison_states = []
3 for ind, filename in enumerate(filenames):
----> 4 with open(f"{os.getcwd()}/data/{filename}") as csvfile:
5 reader = csv.DictReader(csvfile)
6 comparison_states = []
File /usr/local/lib/python3.11/dist-packages/IPython/core/interactiveshell.py:324, in _modified_open(file, *args, **kwargs)
317 if file in {0, 1, 2}:
318 raise ValueError(
319 f"IPython won't let you open fd={file} by default "
320 "as it is likely to crash IPython. If you know what you are doing, "
321 "you can use builtins' open."
322 )
--> 324 return io_open(file, *args, **kwargs)
FileNotFoundError: [Errno 2] No such file or directory: '/app/docs/_notebooks/data/gmat_astrodynamics/scenario002-mission-sequence.csv'
Trajectory RMS error vs GMAT run in the GCRF frame¶
def to_dataframe_RMS(state_ind, list_ind):
return [
repr(all_comparison_states[list_ind][state_ind].get_instant()),
float(
(
all_comparison_states[list_ind][state_ind].get_instant()
- all_comparison_states[list_ind][0].get_instant()
).in_seconds()
),
(
np.linalg.norm(
(
all_comparison_states[list_ind][state_ind]
.get_position()
.get_coordinates()
- ostk_states[state_ind].get_position().get_coordinates()
)
)
),
(
np.linalg.norm(
(
all_comparison_states[list_ind][state_ind]
.get_velocity()
.get_coordinates()
- ostk_states[state_ind].get_velocity().get_coordinates()
)
)
),
]
ostk_states_compared = [
[
to_dataframe_RMS(state_ind, list_ind)
for state_ind in range(0, len(all_comparison_states[list_ind]))
]
for list_ind in range(0, len(all_comparison_states))
]
ostk_states_compared_df = [
pd.DataFrame(
ostk_states_compared[list_ind],
columns=["$Time^{UTC}$", "Elapsed secs", "${\delta}x$", "${\delta}v$"],
)
for list_ind in range(0, len(all_comparison_states))
]
Validation Plots¶
Plot position error
orbit_df_RMS_position_list = [
ostk_states_compared_df[list_ind][["Elapsed secs", "${\delta}x$"]]
for list_ind in range(0, len(all_comparison_states))
]
figure = go.Figure()
figure.update_layout(
title=f"Orbit Propagation with {title_prefix}, Position Difference RMS",
showlegend=True,
height=1000,
)
figure.update_xaxes(title_text="Time Elapsed (s)")
figure.update_yaxes(title_text="Position Difference (m)")
for list_ind, orbit_df_RMS_position in enumerate(orbit_df_RMS_position_list):
figure.add_trace(
go.Scatter(
x=orbit_df_RMS_position["Elapsed secs"],
y=orbit_df_RMS_position["${\delta}x$"],
name=legend_name_list[list_ind],
mode="lines",
)
)
figure.show("svg")
Plot velocity error
orbit_df_RMS_velocity_list = [
ostk_states_compared_df[list_ind][["Elapsed secs", "${\delta}v$"]]
for list_ind in range(0, len(all_comparison_states))
]
figure = go.Figure()
figure.update_layout(
title=f"Orbit Propagation with {title_prefix}, Velocity Difference RMS",
showlegend=True,
height=1000,
)
figure.update_xaxes(title_text="Time Elapsed (s)")
figure.update_yaxes(title_text="Velocity Difference (m/s)")
for list_ind, orbit_df_RMS_velocity in enumerate(orbit_df_RMS_velocity_list):
figure.add_trace(
go.Scatter(
x=orbit_df_RMS_velocity["Elapsed secs"],
y=orbit_df_RMS_velocity["${\delta}v$"],
name=legend_name_list[list_ind],
mode="lines",
)
)
figure.show("svg")