Cartesian ↔ Orbital Element Transformations¶
The functions described here convert between Keplerian orbital elements and Cartesian state vectors. While these transformations are part of the "coordinates" module, they specifically deal with orbital mechanics - converting between two different coordinate representations of a satellite's orbit.
Understanding both representations is essential: Keplerian elements provide intuitive orbital parameters like size, shape, and orientation, while Cartesian states are necessary for numerical orbit propagation and applying perturbations.
For complete API details, see the Cartesian Coordinates API Reference.
Orbital Representations¶
Keplerian Orbital Elements¶
Keplerian elements describe an orbit using six classical parameters:
- \(a\): Semi-major axis (meters) - defines the orbit's size
- \(e\): Eccentricity (dimensionless) - defines the orbit's shape (0 = circular, 0 < e < 1 = elliptical)
- \(i\): Inclination (radians or degrees) - tilt of orbital plane relative to equator
- \(\Omega\): Right ascension of ascending node (radians or degrees) - where orbit crosses equator going north
- \(\omega\): Argument of periapsis (radians or degrees) - where orbit is closest to Earth
- \(M\): Mean anomaly (radians or degrees) - position of satellite along orbit
In brahe, the combined vector has ordering [a, e, i, Ω, ω, M]
Info
Brahe uses mean anomaly as the default anomaly representation for Keplerian elements. Other anomaly types (eccentric, true) can be converted using the anomaly conversion functions in the Orbits module.
Cartesian State Vectors¶
Cartesian states represent position and velocity in three-dimensional space:
- Position: \([p_x, p_y, p_z]\) in meters
- Velocity: \([v_x, v_y, v_z]\) in meters per second
In brahe, the state vector is combined as [p_x, p_y, p_z, v_x, v_y, v_z]
Cartesian states are typically expressed in an inertial reference frame like Earth-Centered Inertial (ECI), where the axes are fixed with respect to the stars rather than rotating with Earth.
Info
All position and velocity components in Cartesian states are in SI base units (meters and meters per second).
They must be in SI base units for inputs and are always returned in SI base units.
Converting Orbital Elements to Cartesian¶
The most common workflow is to start with intuitive orbital parameters and convert them to Cartesian states for propagation.
Using Degrees¶
When working with human-readable orbital parameters, degrees are more intuitive:
import brahe as bh
import numpy as np
bh.initialize_eop()
# Define orbital elements [a, e, i, Ω, ω, M] in meters and degrees
# LEO satellite: 500 km altitude, 97.8° inclination (~sun-synchronous)
oe_deg = np.array(
[
bh.R_EARTH + 500e3, # Semi-major axis (m)
0.01, # Eccentricity
97.8, # Inclination (deg)
15.0, # Right ascension of ascending node (deg)
30.0, # Argument of periapsis (deg)
45.0, # Mean anomaly (deg)
]
)
# Convert orbital elements to Cartesian state using degrees
state = bh.state_osculating_to_cartesian(oe_deg, bh.AngleFormat.DEGREES)
print("Cartesian state [x, y, z, vx, vy, vz] (m, m/s):")
print(f"Position: [{state[0]:.3f}, {state[1]:.3f}, {state[2]:.3f}]")
print(f"Velocity: [{state[3]:.6f}, {state[4]:.6f}, {state[5]:.6f}]")
# Cartesian state (m, m/s):
# Position: [1848964.106, -434937.468, 6560410.530]
# Velocity: [-7098.379734, -2173.344867, 1913.333385]
use brahe as bh;
use nalgebra as na;
fn main() {
bh::initialize_eop().unwrap();
// Define orbital elements [a, e, i, Ω, ω, M] in meters and degrees
// LEO satellite: 500 km altitude, 97.8° inclination (~sun-synchronous)
let oe_deg = na::SVector::<f64, 6>::new(
bh::R_EARTH + 500e3, // Semi-major axis (m)
0.01, // Eccentricity
97.8, // Inclination (deg)
15.0, // Right ascension of ascending node (deg)
30.0, // Argument of periapsis (deg)
45.0 // Mean anomaly (deg)
);
// Convert orbital elements to Cartesian state using degrees
let state = bh::state_osculating_to_cartesian(oe_deg, bh::AngleFormat::Degrees);
println!("Cartesian state [x, y, z, vx, vy, vz] (m, m/s):");
println!("Position: [{:.3}, {:.3}, {:.3}]", state[0], state[1], state[2]);
println!("Velocity: [{:.6}, {:.6}, {:.6}]", state[3], state[4], state[5]);
// Cartesian state (m, m/s):
// Position: [1848964.106, -434937.468, 6560410.530]
// Velocity: [-7098.379734, -2173.344867, 1913.333385]
}
Using Radians¶
For mathematical consistency or when working with data already in radians:
import brahe as bh
import numpy as np
from math import pi
bh.initialize_eop()
# Define orbital elements [a, e, i, Ω, ω, M] in meters and degrees
# LEO satellite: 500 km altitude, 97.8° inclination (~sun-synchronous)
oe_deg = np.array(
[
bh.R_EARTH + 500e3, # Semi-major axis (m)
0.01, # Eccentricity
pi / 4, # Inclination (rad)
pi / 8, # Right ascension of ascending node (rad)
pi / 2, # Argument of periapsis (rad)
3 * pi / 4, # Mean anomaly (rad)
]
)
# Convert orbital elements to Cartesian state using degrees
state = bh.state_osculating_to_cartesian(oe_deg, bh.AngleFormat.RADIANS)
print("Cartesian state [x, y, z, vx, vy, vz] (m, m/s):")
print(f"Position: [{state[0]:.3f}, {state[1]:.3f}, {state[2]:.3f}]")
print(f"Velocity: [{state[3]:.6f}, {state[4]:.6f}, {state[5]:.6f}]")
# Cartesian state (m, m/s):
# Position: [-3117582.037, -5092452.343, -3511765.495]
# Velocity: [6408.435846, -1407.501408, -3752.763969]
use brahe as bh;
use nalgebra as na;
use std::f64::consts::PI;
fn main() {
bh::initialize_eop().unwrap();
// Define orbital elements [a, e, i, Ω, ω, M] in meters and radians
// LEO satellite: 500 km altitude, 45° inclination
let oe_rad = na::SVector::<f64, 6>::new(
bh::R_EARTH + 500e3, // Semi-major axis (m)
0.01, // Eccentricity
PI/4.0, // Inclination (rad)
PI/8.0, // Right ascension of ascending node (rad)
PI/2.0, // Argument of periapsis (rad)
3.0*PI/4.0 // Mean anomaly (rad)
);
// Convert orbital elements to Cartesian state using radians
let state = bh::state_osculating_to_cartesian(oe_rad, bh::AngleFormat::Radians);
println!("Cartesian state [x, y, z, vx, vy, vz] (m, m/s):");
println!("Position: [{:.3}, {:.3}, {:.3}]", state[0], state[1], state[2]);
println!("Velocity: [{:.6}, {:.6}, {:.6}]", state[3], state[4], state[5]);
// Cartesian state (m, m/s):
// Position: [-3117582.037, -5092452.343, -3511765.495]
// Velocity: [6408.435846, -1407.501408, -3752.763969]
}
Info
The AngleFormat parameter only affects the three angular elements (i, Ω, ω, M). Semi-major axis is always in meters, and eccentricity is always dimensionless.
Converting Cartesian to Orbital Elements¶
After propagating or receiving Cartesian state data, you often want to convert back to orbital elements for interpretation and analysis.
import brahe as bh
import numpy as np
bh.initialize_eop()
# Define Cartesian state vector [px, py, pz, vx, vy, vz] in meters and meters per second
state = np.array(
[1848964.106, -434937.468, 6560410.530, -7098.379734, -2173.344867, 1913.333385]
)
# Convert orbital elements to Cartesian state using degrees
oe_deg = bh.state_cartesian_to_osculating(state, bh.AngleFormat.DEGREES)
print("Osculating state [a, e, i, Ω, ω, M] (deg):")
print(f"Semi-major axis (m): {oe_deg[0]:.3f}")
print(f"Eccentricity: {oe_deg[1]:.6f}")
print(f"Inclination (deg): {oe_deg[2]:.6f}")
print(f"RA of ascending node (deg): {oe_deg[3]:.6f}")
print(f"Argument of periapsis (deg): {oe_deg[4]:.6f}")
print(f"Mean anomaly (deg): {oe_deg[5]:.6f}")
# Osculating state (deg):
# Semi-major axis (m): 6878136.299
# Eccentricity: 0.010000
# Inclination (deg): 97.800000
# RA of ascending node (deg): 15.000000
# Argument of periapsis (deg): 30.000000
# Mean anomaly (deg): 45.000000
# You can also convert using radians
oe_rad = bh.state_cartesian_to_osculating(state, bh.AngleFormat.RADIANS)
print("\nOsculating state [a, e, i, Ω, ω, M] (rad):")
print(f"Semi-major axis (m): {oe_rad[0]:.3f}")
print(f"Eccentricity: {oe_rad[1]:.6f}")
print(f"Inclination (rad): {oe_rad[2]:.6f}")
print(f"RA of ascending node (rad): {oe_rad[3]:.6f}")
print(f"Argument of periapsis (rad): {oe_rad[4]:.6f}")
print(f"Mean anomaly (rad): {oe_rad[5]:.6f}")
# Osculating state (rad):
# Semi-major axis (m): 6878136.299
# Eccentricity: 0.010000
# Inclination (rad): 1.706932
# RA of ascending node (rad): 0.261799
# Argument of periapsis (rad): 0.523599
# Mean anomaly (rad): 0.785398
use brahe as bh;
use nalgebra as na;
fn main() {
bh::initialize_eop().unwrap();
// Define Cartesian state vector [px, py, pz, vx, vy, vz] in meters and meters per second
let state = na::SVector::<f64, 6>::new(
1848964.106,
-434937.468,
6560410.530,
-7098.379734,
-2173.344867,
1913.333385
);
// Convert Cartesian state to orbital elements using degrees
let oe_deg = bh::state_cartesian_to_osculating(state, bh::AngleFormat::Degrees);
println!("Osculating state [a, e, i, Ω, ω, M] (deg):");
println!("Semi-major axis (m): {:.3}", oe_deg[0]);
println!("Eccentricity: {:.6}", oe_deg[1]);
println!("Inclination (deg): {:.6}", oe_deg[2]);
println!("RA of ascending node (deg): {:.6}", oe_deg[3]);
println!("Argument of periapsis (deg): {:.6}", oe_deg[4]);
println!("Mean anomaly (deg): {:.6}", oe_deg[5]);
// Osculating state (deg):
// Semi-major axis (m): 6878136.299
// Eccentricity: 0.010000
// Inclination (deg): 97.800000
// RA of ascending node (deg): 15.000000
// Argument of periapsis (deg): 30.000000
// Mean anomaly (deg): 45.000000
// You can also convert using radians
let oe_rad = bh::state_cartesian_to_osculating(state, bh::AngleFormat::Radians);
println!("\nOsculating state [a, e, i, Ω, ω, M] (rad):");
println!("Semi-major axis (m): {:.3}", oe_rad[0]);
println!("Eccentricity: {:.6}", oe_rad[1]);
println!("Inclination (rad): {:.6}", oe_rad[2]);
println!("RA of ascending node (rad): {:.6}", oe_rad[3]);
println!("Argument of periapsis (rad): {:.6}", oe_rad[4]);
println!("Mean anomaly (rad): {:.6}", oe_rad[5]);
// Osculating state (rad):
// Semi-major axis (m): 6878136.299
// Eccentricity: 0.010000
// Inclination (rad): 1.706932
// RA of ascending node (rad): 0.261799
// Argument of periapsis (rad): 0.523599
// Mean anomaly (rad): 0.785398
}
See Also¶
- Cartesian Coordinates API Reference - Complete function documentation
- Orbital Mechanics - Related orbital mechanics functions
- Anomaly Conversions - Converting between mean, eccentric, and true anomaly