Orbit Computation

This tutorial demonstrates how to generate satellite orbits using various models.

Setup

import numpy as np
import pandas as pd

import plotly.graph_objs as go

from ostk.physics.unit import Length
from ostk.physics.unit import Angle
from ostk.physics.time import Scale
from ostk.physics.time import Instant
from ostk.physics.time import Duration
from ostk.physics.time import Interval
from ostk.physics.time import DateTime
from ostk.physics import Environment

from ostk.astrodynamics.trajectory import Orbit
from ostk.astrodynamics.trajectory.orbit.model import Kepler
from ostk.astrodynamics.trajectory.orbit.model.kepler import COE
from ostk.astrodynamics.trajectory.orbit.model import SGP4
from ostk.astrodynamics.trajectory.orbit.model.sgp4 import TLE

from ostk.astrodynamics.utilities import convert_state
from ostk.astrodynamics.display import create_2d_map
from ostk.astrodynamics.display import create_3d_globe

SGP4

Computation

environment = Environment.default()

Create a Classical Orbital Element (COE) set:

a = Length.kilometers(7000.0)
e = 0.0001
i = Angle.degrees(35.0)
raan = Angle.degrees(40.0)
aop = Angle.degrees(45.0)
nu = Angle.degrees(50.0)

coe = COE(a, e, i, raan, aop, nu)

Setup a Keplerian orbital model:

epoch = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
earth = environment.access_celestial_object_with_name("Earth")

keplerian_model = Kepler(coe, epoch, earth, Kepler.PerturbationType.No)

Create a Two-Line Element (TLE) set:

tle = TLE(
    "1 39419U 13066D   18260.77424112  .00000022  00000-0  72885-5 0  9996",
    "2 39419  97.6300 326.6556 0013847 175.2842 184.8495 14.93888428262811",
)

Setup a SGP4 orbital model:

sgp4_model = SGP4(tle)

Setup the orbit:

# orbit = Orbit(keplerian_model, environment.access_celestial_object_with_name("Earth"))
orbit = Orbit(sgp4_model, environment.access_celestial_object_with_name("Earth"))

Now that the orbit is set, we can compute the satellite position:

start_instant = Instant.date_time(DateTime(2018, 9, 5, 0, 0, 0), Scale.UTC)
end_instant = Instant.date_time(DateTime(2018, 9, 6, 0, 0, 0), Scale.UTC)
interval = Interval.closed(start_instant, end_instant)
step = Duration.minutes(1.0)

Generate a time grid:

instants = interval.generate_grid(step)
states = [[instant, orbit.get_state_at(instant)] for instant in instants]
orbit_data = [convert_state(state) for state in states]
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[13], line 1
----> 1 orbit_data = [convert_state(state) for state in states]

Cell In[13], line 1, in <listcomp>(.0)
----> 1 orbit_data = [convert_state(state) for state in states]

File /usr/local/lib/python3.11/dist-packages/ostk/astrodynamics/utilities.py:236, in convert_state(state)
    212 def convert_state(
    213     state: State,
    214 ) -> tuple[str, float, float, float, float, float, float, float, float, float]:
    215     """
    216     Convert a State into dataframe-ready values.
    217     - Instant (str)
   (...)
    233         tuple[str, float, float, float, float, float, float, float, float, float]: The dataframe-ready values.
    234     """
--> 236     lla: LLA = lla_from_state(state)
    238     instant: Instant = state.get_instant()
    240     return (
    241         str(instant.to_string()),
    242         float(instant.get_modified_julian_date(Scale.UTC)),
   (...)
    247         float(lla.get_altitude().in_meters()),
    248     )

File /usr/local/lib/python3.11/dist-packages/ostk/astrodynamics/utilities.py:36, in lla_from_state(state)
     25 def lla_from_state(state: State) -> LLA:
     26     """
     27     Return latitude (degrees), longitude (degrees), altitude (meters) float list from a state.
     28 
   (...)
     33         LLA: The LLA.
     34     """
---> 36     return lla_from_position(state.get_position(), state.get_instant())

AttributeError: 'list' object has no attribute 'get_position'
orbit_df = pd.DataFrame(
    orbit_data,
    columns=[
        "$Time^{UTC}$",
        "$MJD^{UTC}$",
        "$x_{x}^{ECI}$",
        "$x_{y}^{ECI}$",
        "$x_{z}^{ECI}$",
        "$v_{x}^{ECI}$",
        "$v_{y}^{ECI}$",
        "$v_{z}^{ECI}$",
        "$Latitude$",
        "$Longitude$",
        "$Altitude$",
    ],
)

Output

Table:

orbit_df.head()
$Time^{UTC}$ $MJD^{UTC}$ $x_{x}^{ECI}$ $x_{y}^{ECI}$ $x_{z}^{ECI}$ $v_{x}^{ECI}$ $v_{y}^{ECI}$ $v_{z}^{ECI}$ $Latitude$ $Longitude$ $Altitude$
0 2018-09-05 00:00:00 [UTC] 58366.000000 -1.713399e+06 4.882015e+05 6.729062e+06 -4963.295593 5457.439154 -1666.177507 75.158968 -179.618850 602749.955899
1 2018-09-05 00:01:00 [UTC] 58366.000694 -2.007361e+06 8.143839e+05 6.614883e+06 -4831.972213 5411.464113 -2138.444901 71.875980 173.964827 601723.868966
2 2018-09-05 00:02:00 [UTC] 58366.001389 -2.292825e+06 1.137119e+06 6.472626e+06 -4680.139327 5342.558791 -2601.814472 68.453880 169.415947 600586.133346
3 2018-09-05 00:03:00 [UTC] 58366.002083 -2.568580e+06 1.455038e+06 6.302882e+06 -4508.419228 5250.994715 -3054.324538 64.948448 166.007089 599350.279488
4 2018-09-05 00:04:00 [UTC] 58366.002778 -2.833452e+06 1.766793e+06 6.106362e+06 -4317.518408 5137.138312 -3494.054108 61.389166 163.333225 598031.727629

2D plot, over World Map:

figure = create_2d_map(
    data=go.Scattergeo(
        lon=orbit_df["$Longitude$"],
        lat=orbit_df["$Latitude$"],
        mode="lines",
        line=go.scattergeo.Line(width=1, color="red"),
    ),
)
figure.layout.update(
    {
        "width": 1200,
        "height": 600,
    }
)

figure.show("png")
../_images/91e23b9e8e177948bd76d431e20c37ee7214426e5557c7ae5845be1a4773043c.png

3D plot, in Earth Fixed frame:

figure = create_3d_globe(
    data=[
        go.Scattergeo(
            lon=orbit_df["$Longitude$"],
            lat=orbit_df["$Latitude$"],
            mode="lines",
            line=go.scattergeo.Line(width=2, color="rgb(255, 62, 79)"),
        )
    ],
)
figure.layout.update(
    {
        "width": 1200,
        "height": 600,
    }
)

figure.show("png")
../_images/6ea207790fd0ba9ae6b8244744fb744c2dc15844111346db0df41115bc8f08d7.png

3D plot, in Earth Inertial frame:

theta = np.linspace(0, 2 * np.pi, 30)
phi = np.linspace(0, np.pi, 30)

theta_grid, phi_grid = np.meshgrid(theta, phi)

r = float(earth.get_equatorial_radius().in_meters())

x = r * np.cos(theta_grid) * np.sin(phi_grid)
y = r * np.sin(theta_grid) * np.sin(phi_grid)
z = r * np.cos(phi_grid)

earth_surface = go.Surface(x=x, y=y, z=z, colorscale="Viridis", showscale=False)

trace = go.Scatter3d(
    x=orbit_df["$x_{x}^{ECI}$"],
    y=orbit_df["$x_{y}^{ECI}$"],
    z=orbit_df["$x_{z}^{ECI}$"],
    mode="lines",
    marker=dict(
        size=0, color=orbit_df["$x_{z}^{ECI}$"], colorscale="Viridis", showscale=False
    ),
    line=dict(color=orbit_df["$x_{z}^{ECI}$"], width=1),
)

figure = go.Figure(
    data=[earth_surface, trace],
    layout=go.Layout(
        title=None,
        width=800,
        height=1000,
        showlegend=False,
        scene=go.layout.Scene(
            xaxis=dict(
                gridcolor="rgb(255, 255, 255)",
                zerolinecolor="rgb(255, 255, 255)",
                showbackground=True,
                backgroundcolor="rgb(230, 230,230)",
            ),
            yaxis=dict(
                gridcolor="rgb(255, 255, 255)",
                zerolinecolor="rgb(255, 255, 255)",
                showbackground=True,
                backgroundcolor="rgb(230, 230,230)",
            ),
            zaxis=dict(
                gridcolor="rgb(255, 255, 255)",
                zerolinecolor="rgb(255, 255, 255)",
                showbackground=True,
                backgroundcolor="rgb(230, 230,230)",
            ),
            camera=dict(
                up=dict(x=0, y=0, z=1),
                eye=dict(
                    x=-1.7428,
                    y=1.0707,
                    z=0.7100,
                ),
            ),
            aspectratio=dict(x=1, y=1, z=1),
            aspectmode="manual",
        ),
    ),
)

figure.show("png")
../_images/83b45d2d1a9577465a22cb1392d6b6ba647f8742ec3ad3bb7c95140d918feefd.png