Tutorial 03: State Vectors & Orbital Elements

This tutorial covers conversions between Cartesian State Vectors (Position, Velocity) and various orbital element representations.


Theory Prerequisites

1. Classical Orbital Elements (COE)

A set of 6 parameters uniquely describing an orbit:

  • \(a\): Semi-major axis (Size)

  • \(e\): Eccentricity (Shape)

  • \(i\): Inclination (Tilt)

  • \(\Omega\) (RAAN): Right Ascension of Ascending Node (Orientation)

  • \(\omega\): Argument of Perigee (Location of perigee)

  • \(\nu\): True Anomaly (Current position along orbit)

2. Equinoctial elements

Non-singular for circular (\(e=0\)) and equatorial (\(i=0\)) orbits.

  • \(a\): Semi-major axis

  • \(h = e \sin(\Omega + \omega)\)

  • \(k = e \cos(\Omega + \omega)\)

  • \(p = \tan(i/2) \sin(\Omega)\)

  • \(q = \tan(i/2) \cos(\Omega)\)

  • \(\lambda_M = \Omega + \omega + M\) (Mean longitude)


import numpy as np
from opengnc.utils.state_to_elements import eci2kepler, kepler2eci, anomalies
from opengnc.utils.equinoctial_utils import kepler2equinoctial, equinoctial2kepler, equinoctial2eci
from opengnc.ssa.tle_interface import TLECatalog, TLEEntity

print("Imports successful.")
Imports successful.

1. State Vector to Keplerian (COE)

Let’s define a position and velocity vector in ECI for a spacecraft in a standard orbit.

# Define State Vector (ECI)
r_eci = np.array([6771000.0, 0.0, 0.0]) # [m]
v_eci = np.array([0.0, 7670.0, 0.0])   # [m/s]

# Convert to Keplerian Elements
a, ecc, incl, raan, argp, nu, M, E, p, arglat, truelon, lonper = eci2kepler(r_eci, v_eci)

print(f"Semi-major axis (a): {a/1000.0:.2f} km")
print(f"Eccentricity (e):   {ecc:.6f}")
print(f"Inclination (i):    {np.degrees(incl):.2f} deg")
print(f"RAAN (\Omega):        {np.degrees(raan):.2f} deg")
print(f"Arg. of Perigee (\omega): {np.degrees(argp):.2f} deg")
print(f"True Anomaly (\nu):   {np.degrees(nu):.2f} deg")

# Verify round-trip
r_eci_rt, v_eci_rt = kepler2eci(a, ecc, incl, raan, argp, nu)
print(f"\nRound-trip Position Error: {np.linalg.norm(r_eci - r_eci_rt):.6e} m")
Semi-major axis (a): 6766.42 km
Eccentricity (e):   0.000677
Inclination (i):    0.00 deg
RAAN (\Omega):        0.00 deg
Arg. of Perigee (\omega): 180.00 deg
True Anomaly (
u):   180.00 deg

Round-trip Position Error: 4.629332e-26 m

2. Equinoctial elements Conversion

Equinoctial elements avoid singularities. Let’s convert our Keplerian elements to Equinoctial.

# Convert Keplerian to Equinoctial
a_eq, h, k, p_eq, q, l_mean = kepler2equinoctial(a, ecc, incl, raan, argp, M)

print(f"Equinoctial Elements:")
print(f"  a: {a_eq/1000.0:.2f} km")
print(f"  h: {h:.6e}")
print(f"  k: {k:.6e}")
print(f"  p: {p_eq:.6e}")
print(f"  q: {q:.6e}")
print(f"  lambda_mean: {np.degrees(l_mean):.2f} deg")

# Convert back to Keplerian
a_r, ecc_r, incl_r, raan_r, argp_r, nu_r, M_r = equinoctial2kepler(a_eq, h, k, p_eq, q, l_mean)
print(f"\nRecovered Eccentricity: {ecc_r:.6f} (Original: {ecc:.6f})")
print(f"Recovered Inclination:  {np.degrees(incl_r):.2f} deg (Original: {np.degrees(incl):.2f} deg)")
Equinoctial Elements:
  a: 6766.42 km
  h: 8.294149e-20
  k: -6.772687e-04
  p: 0.000000e+00
  q: 0.000000e+00
  lambda_mean: 0.00 deg

Recovered Eccentricity: 0.000677 (Original: 0.000677)
Recovered Inclination:  0.00 deg (Original: 0.00 deg)

3. TLE Interface & SGP4 Propagator

Load a TLE and initialize a propagator to propagate the orbit.

# Example TLE for ISS
tle_name = "ISS (ZARYA)"
line1 = "1 25544U 98067A   24083.42875141  .00015504  00000-0  27807-3 0  9993"
line2 = "2 25544  51.6416  35.8453 0004455  67.2341  49.3490 15.49842535445218"

# Create TLE Catalog
catalog = TLECatalog()
catalog.add_tle(tle_name, line1, line2)

print(f"Listing satellites in catalog: {catalog.list_satellites()}")

# Get propagator for the satellite
sat = catalog.get_by_name(tle_name)
propagator = sat.get_propagator()

# Propagate orbit forward in seconds
for t_min in [0, 10, 20]:
    dt_sec = t_min * 60.0
    pos, vel = propagator.propagate(None, None, dt_sec)
    print(f"\nTime: +{t_min} minutes")
    print(f"Position [TEME]: {pos/1000.0} km")
    print(f"Velocity [TEME]: {vel/1000.0} km/s")
Listing satellites in catalog: ['ISS (ZARYA)']

Time: +0 minutes
Position [TEME]: [-4674.10165916  1268.98804622  4754.93741611] km
Velocity [TEME]: [-4.30887388 -5.74033591 -2.69314811] km/s

Time: +10 minutes
Position [TEME]: [-6036.28965614 -2197.92391076  2209.02466488] km
Velocity [TEME]: [-0.05695672 -5.37151248 -5.465966  ] km/s

Time: +20 minutes
Position [TEME]: [-4738.02654378 -4696.3084195  -1313.38204718] km
Velocity [TEME]: [ 4.21667521 -2.63738784 -5.82276693] km/s