Skip to content

ECI ↔ ECEF Transformations

The ECI (Earth-Centered Inertial) and ECEF (Earth-Centered Earth-Fixed) naming convention is a traditional and widely-used terminology in the astrodynamics community.

Naming Convention

Brahe provides two sets of function names for frame transformations, both currently mapping to the same underlying implementations:

  • ECI/ECEF naming: Common coordinate system names (e.g., rotation_eci_to_ecef, state_eci_to_ecef)
  • GCRF/ITRF naming: Explicit reference frame names (e.g., rotation_gcrf_to_itrf, state_gcrf_to_itrf)

The ECI/ECEF naming will always use the "best" available transformations in Brahe, while the GCRF/ITRF naming ensures consistent use of specific reference frame implementations.

Reference Frames

ECI (Earth-Centered Inertial)

  • A non-rotating frame fixed with respect to distant stars
  • Inertial frame suitable for integration of equations of motion
  • Current Realization: GCRF (Geocentric Celestial Reference Frame)

ECEF (Earth-Centered Earth-Fixed)

  • A rotating frame fixed to the Earth's surface
  • Ideal for computing positions and motions relative to terrestrial locations and observers
  • Current Realization: ITRF (International Terrestrial Reference Frame)

ECI to ECEF

Converting from ECI to ECEF accounts for the Earth's rotation, polar motion, and precession-nutation effects. These transformations are time-dependent and require Earth Orientation Parameters (EOP) for high accuracy. The transformations will use the currently loaded Earth orientation data provider to obtain the necessary parameters automatically. See Earth Orientation Data for more details.

State Vector

Transform a complete state vector (position and velocity) from ECI to ECEF:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Define orbital elements in degrees
# LEO satellite: 500 km altitude, sun-synchronous orbit
oe = 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)
    ]
)

print("Orbital elements (degrees):")
print(f"  a    = {oe[0]:.3f} m = {(oe[0] - bh.R_EARTH) / 1e3:.1f} km altitude")
print(f"  e    = {oe[1]:.4f}")
print(f"  i    = {oe[2]:.4f}°")
print(f"  Ω    = {oe[3]:.4f}°")
print(f"  ω    = {oe[4]:.4f}°")
print(f"  M    = {oe[5]:.4f}°\n")

epc = bh.Epoch(2024, 1, 1, 12, 0, 0.0, time_system=bh.UTC)
print(f"Epoch: {epc}")

# Convert to ECI Cartesian state
state_eci = bh.state_koe_to_eci(oe, bh.AngleFormat.DEGREES)

print("ECI state vector:")
print(f"  Position: [{state_eci[0]:.3f}, {state_eci[1]:.3f}, {state_eci[2]:.3f}] m")
print(f"  Velocity: [{state_eci[3]:.6f}, {state_eci[4]:.6f}, {state_eci[5]:.6f}] m/s\n")

# Transform to ECEF at specific epoch
state_ecef = bh.state_eci_to_ecef(epc, state_eci)

print("\nECEF state vector:")
print(f"  Position: [{state_ecef[0]:.3f}, {state_ecef[1]:.3f}, {state_ecef[2]:.3f}] m")
print(
    f"  Velocity: [{state_ecef[3]:.6f}, {state_ecef[4]:.6f}, {state_ecef[5]:.6f}] m/s"
)
use brahe as bh;
use nalgebra as na;

fn main() {
    bh::initialize_eop().unwrap();

    // Define orbital elements in degrees
    // LEO satellite: 500 km altitude, sun-synchronous orbit
    let oe = 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)
    );

    println!("Orbital elements (degrees):");
    println!("  a    = {:.3} m = {:.1} km altitude", oe[0], (oe[0] - bh::R_EARTH) / 1e3);
    println!("  e    = {:.4}", oe[1]);
    println!("  i    = {:.4}°", oe[2]);
    println!("  Ω    = {:.4}°", oe[3]);
    println!("  ω    = {:.4}°", oe[4]);
    println!("  M    = {:.4}°\n", oe[5]);

    let epc = bh::Epoch::from_datetime(2024, 1, 1, 12, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    println!("Epoch: {}", epc);

    // Convert to ECI Cartesian state
    let state_eci = bh::state_koe_to_eci(oe, bh::AngleFormat::Degrees);

    println!("ECI state vector:");
    println!("  Position: [{:.3}, {:.3}, {:.3}] m", state_eci[0], state_eci[1], state_eci[2]);
    println!("  Velocity: [{:.6}, {:.6}, {:.6}] m/s\n", state_eci[3], state_eci[4], state_eci[5]);

    // Transform to ECEF at specific epoch
    let state_ecef = bh::state_eci_to_ecef(epc, state_eci);

    println!("\nECEF state vector:");
    println!("  Position: [{:.3}, {:.3}, {:.3}] m", state_ecef[0], state_ecef[1], state_ecef[2]);
    println!("  Velocity: [{:.6}, {:.6}, {:.6}] m/s", state_ecef[3], state_ecef[4], state_ecef[5]);
}
Output
Orbital elements (degrees):
  a    = 6878136.300 m = 500.0 km altitude
  e    = 0.0100
  i    = 97.8000°
  Ω    = 15.0000°
  ω    = 30.0000°
  M    = 45.0000°

Epoch: 2024-01-01 12:00:00.000 UTC
ECI state vector:
  Position: [1848964.106, -434937.468, 6560410.530] m
  Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s


ECEF state vector:
  Position: [757164.267, 1725863.563, 6564672.302] m
  Velocity: [989.350643, -7432.740021, 1896.768934] m/s
Orbital elements (degrees):
  a    = 6878136.300 m = 500.0 km altitude
  e    = 0.0100
  i    = 97.8000°
  Ω    = 15.0000°
  ω    = 30.0000°
  M    = 45.0000°

Epoch: 2024-01-01 12:00:00.000 UTC
ECI state vector:
  Position: [1848964.106, -434937.468, 6560410.530] m
  Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s


ECEF state vector:
  Position: [757164.267, 1725863.563, 6564672.302] m
  Velocity: [989.350643, -7432.740021, 1896.768934] m/s

Velocity Transformation

Simply rotating velocity vectors will not yield correct velocity components in the ECEF frame due to the Earth's rotation. State vector transformation functions properly account for observed velocity changes in the ECEF frame due to Earth's rotation.

Rotation Matrix

Get the rotation matrix from ECI to ECEF:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Define epoch
epc = bh.Epoch(2024, 1, 1, 12, 0, 0.0, time_system=bh.UTC)

# Get rotation matrix from ECI to ECEF
R_eci_to_ecef = bh.rotation_eci_to_ecef(epc)

print(f"Epoch: {epc}")
print("\nECI to ECEF rotation matrix:")
print(
    f"  [{R_eci_to_ecef[0, 0]:10.7f}, {R_eci_to_ecef[0, 1]:10.7f}, {R_eci_to_ecef[0, 2]:10.7f}]"
)
print(
    f"  [{R_eci_to_ecef[1, 0]:10.7f}, {R_eci_to_ecef[1, 1]:10.7f}, {R_eci_to_ecef[1, 2]:10.7f}]"
)
print(
    f"  [{R_eci_to_ecef[2, 0]:10.7f}, {R_eci_to_ecef[2, 1]:10.7f}, {R_eci_to_ecef[2, 2]:10.7f}]\n"
)

# Define orbital elements in degrees for satellite position
oe = np.array(
    [
        bh.R_EARTH + 500e3,  # Semi-major axis (m)
        0.01,  # Eccentricity
        97.8,  # Inclination (deg)
        15.0,  # RAAN (deg)
        30.0,  # Argument of periapsis (deg)
        45.0,  # Mean anomaly (deg)
    ]
)

# Convert to ECI Cartesian state and extract position
state_eci = bh.state_koe_to_eci(oe, bh.AngleFormat.DEGREES)
pos_eci = state_eci[0:3]

print("Position in ECI:")
print(f"  [{pos_eci[0]:.3f}, {pos_eci[1]:.3f}, {pos_eci[2]:.3f}] m\n")

# Transform position using rotation matrix
pos_ecef = R_eci_to_ecef @ pos_eci

print("Position in ECEF (using rotation matrix):")
print(f"  [{pos_ecef[0]:.3f}, {pos_ecef[1]:.3f}, {pos_ecef[2]:.3f}] m")

pos_ecef_direct = bh.position_eci_to_ecef(epc, pos_eci)
print("\nPosition in ECEF (using position_eci_to_ecef):")
print(
    f"  [{pos_ecef_direct[0]:.3f}, {pos_ecef_direct[1]:.3f}, {pos_ecef_direct[2]:.3f}] m"
)
use brahe as bh;
use nalgebra as na;

fn main() {
    bh::initialize_eop().unwrap();

    // Define epoch
    let epc = bh::Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    let epc = epc + 12.0 * 3600.0;  // Add 12 hours

    // Get rotation matrix from ECI to ECEF
    let r_eci_to_ecef = bh::rotation_eci_to_ecef(epc);

    println!("Epoch: {}", epc);
    println!("\nECI to ECEF rotation matrix:");
    println!("  [{:10.7}, {:10.7}, {:10.7}]", r_eci_to_ecef[(0, 0)], r_eci_to_ecef[(0, 1)], r_eci_to_ecef[(0, 2)]);
    println!("  [{:10.7}, {:10.7}, {:10.7}]", r_eci_to_ecef[(1, 0)], r_eci_to_ecef[(1, 1)], r_eci_to_ecef[(1, 2)]);
    println!("  [{:10.7}, {:10.7}, {:10.7}]\n", r_eci_to_ecef[(2, 0)], r_eci_to_ecef[(2, 1)], r_eci_to_ecef[(2, 2)]);

    // Define orbital elements in degrees for satellite position
    let oe = na::SVector::<f64, 6>::new(
        bh::R_EARTH + 500e3,  // Semi-major axis (m)
        0.01,                  // Eccentricity
        97.8,                  // Inclination (deg)
        15.0,                  // RAAN (deg)
        30.0,                  // Argument of periapsis (deg)
        45.0,                  // Mean anomaly (deg)
    );

    // Convert to ECI Cartesian state and extract position
    let state_eci = bh::state_koe_to_eci(oe, bh::AngleFormat::Degrees);
    let pos_eci = na::Vector3::new(state_eci[0], state_eci[1], state_eci[2]);

    println!("Position in ECI:");
    println!("  [{:.3}, {:.3}, {:.3}] m\n", pos_eci[0], pos_eci[1], pos_eci[2]);

    // Transform position using rotation matrix
    let pos_ecef = r_eci_to_ecef * pos_eci;

    println!("Position in ECEF (using rotation matrix):");
    println!("  [{:.3}, {:.3}, {:.3}] m", pos_ecef[0], pos_ecef[1], pos_ecef[2]);

    let pos_ecef_direct = bh::position_eci_to_ecef(epc, pos_eci);
    println!("\nPosition in ECEF (using position_eci_to_ecef):");
    println!("  [{:.3}, {:.3}, {:.3}] m", pos_ecef_direct[0], pos_ecef_direct[1], pos_ecef_direct[2]);
}
Output
Epoch: 2024-01-01 12:00:00.000 UTC

ECI to ECEF rotation matrix:
  [ 0.1794538, -0.9837663, -0.0003836]
  [ 0.9837637,  0.1794542, -0.0022908]
  [ 0.0023225,  0.0000338,  0.9999973]

Position in ECI:
  [1848964.106, -434937.468, 6560410.530] m

Position in ECEF (using rotation matrix):
  [757164.267, 1725863.563, 6564672.302] m

Position in ECEF (using position_eci_to_ecef):
  [757164.267, 1725863.563, 6564672.302] m
Epoch: 2024-01-01 12:00:00.000 UTC

ECI to ECEF rotation matrix:
  [ 0.1794538, -0.9837663, -0.0003836]
  [ 0.9837637,  0.1794542, -0.0022908]
  [ 0.0023225,  0.0000338,  0.9999973]

Position in ECI:
  [1848964.106, -434937.468, 6560410.530] m

Position in ECEF (using rotation matrix):
  [757164.267, 1725863.563, 6564672.302] m

Position in ECEF (using position_eci_to_ecef):
  [757164.267, 1725863.563, 6564672.302] m

ECEF to ECI

Converting from ECEF to ECI reverses the transformation, converting Earth-fixed coordinates back to the inertial frame.

State Vector

Transform a complete state vector (position and velocity) from ECEF to ECI:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Define orbital elements in degrees
# LEO satellite: 500 km altitude, sun-synchronous orbit
oe = 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)
    ]
)

print("Orbital elements (degrees):")
print(f"  a    = {oe[0]:.3f} m = {(oe[0] - bh.R_EARTH) / 1e3:.1f} km altitude")
print(f"  e    = {oe[1]:.4f}")
print(f"  i    = {oe[2]:.4f}°")
print(f"  Ω    = {oe[3]:.4f}°")
print(f"  ω    = {oe[4]:.4f}°")
print(f"  M    = {oe[5]:.4f}°\n")

# Convert to ECI Cartesian state
state_eci = bh.state_koe_to_eci(oe, bh.AngleFormat.DEGREES)

# Define epoch
epc = bh.Epoch(2024, 1, 1, 12, 0, 0.0, time_system=bh.UTC)
print(f"Epoch: {epc}")
print("ECI state vector:")
print(f"  Position: [{state_eci[0]:.3f}, {state_eci[1]:.3f}, {state_eci[2]:.3f}] m")
print(f"  Velocity: [{state_eci[3]:.6f}, {state_eci[4]:.6f}, {state_eci[5]:.6f}] m/s\n")

# Transform to ECEF
state_ecef = bh.state_eci_to_ecef(epc, state_eci)

print("ECEF state vector:")
print(f"  Position: [{state_ecef[0]:.3f}, {state_ecef[1]:.3f}, {state_ecef[2]:.3f}] m")
print(
    f"  Velocity: [{state_ecef[3]:.6f}, {state_ecef[4]:.6f}, {state_ecef[5]:.6f}] m/s\n"
)

# Transform back to ECI
state_eci_back = bh.state_ecef_to_eci(epc, state_ecef)

print("\nECI state vector (transformed from ECEF):")
print(
    f"  Position: [{state_eci_back[0]:.3f}, {state_eci_back[1]:.3f}, {state_eci_back[2]:.3f}] m"
)
print(
    f"  Velocity: [{state_eci_back[3]:.6f}, {state_eci_back[4]:.6f}, {state_eci_back[5]:.6f}] m/s"
)

diff_pos = np.linalg.norm(state_eci[0:3] - state_eci_back[0:3])
diff_vel = np.linalg.norm(state_eci[3:6] - state_eci_back[3:6])
print("\nRound-trip error:")
print(f"  Position: {diff_pos:.6e} m")
print(f"  Velocity: {diff_vel:.6e} m/s")
use brahe as bh;
use nalgebra as na;

fn main() {
    bh::initialize_eop().unwrap();

    // Define orbital elements in degrees
    // LEO satellite: 500 km altitude, sun-synchronous orbit
    let oe = 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)
    );

    println!("Orbital elements (degrees):");
    println!("  a    = {:.3} m = {:.1} km altitude", oe[0], (oe[0] - bh::R_EARTH) / 1e3);
    println!("  e    = {:.4}", oe[1]);
    println!("  i    = {:.4}°", oe[2]);
    println!("  Ω    = {:.4}°", oe[3]);
    println!("  ω    = {:.4}°", oe[4]);
    println!("  M    = {:.4}°\n", oe[5]);

    // Convert to ECI Cartesian state
    let state_eci = bh::state_koe_to_eci(oe, bh::AngleFormat::Degrees);

    // Define epoch
    let epc = bh::Epoch::from_datetime(2024, 1, 1, 12, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    println!("Epoch: {}", epc);
    println!("ECI state vector:");
    println!("  Position: [{:.3}, {:.3}, {:.3}] m", state_eci[0], state_eci[1], state_eci[2]);
    println!("  Velocity: [{:.6}, {:.6}, {:.6}] m/s\n", state_eci[3], state_eci[4], state_eci[5]);

    // Transform to ECEF
    let state_ecef = bh::state_eci_to_ecef(epc, state_eci);

    println!("ECEF state vector:");
    println!("  Position: [{:.3}, {:.3}, {:.3}] m", state_ecef[0], state_ecef[1], state_ecef[2]);
    println!("  Velocity: [{:.6}, {:.6}, {:.6}] m/s\n", state_ecef[3], state_ecef[4], state_ecef[5]);

    // Transform back to ECI
    let state_eci_back = bh::state_ecef_to_eci(epc, state_ecef);

    println!("\nECI state vector (transformed from ECEF):");
    println!("  Position: [{:.3}, {:.3}, {:.3}] m", state_eci_back[0], state_eci_back[1], state_eci_back[2]);
    println!("  Velocity: [{:.6}, {:.6}, {:.6}] m/s", state_eci_back[3], state_eci_back[4], state_eci_back[5]);

    let diff_pos = (na::Vector3::new(state_eci[0], state_eci[1], state_eci[2]) -
                    na::Vector3::new(state_eci_back[0], state_eci_back[1], state_eci_back[2])).norm();
    let diff_vel = (na::Vector3::new(state_eci[3], state_eci[4], state_eci[5]) -
                    na::Vector3::new(state_eci_back[3], state_eci_back[4], state_eci_back[5])).norm();
    println!("\nRound-trip error:");
    println!("  Position: {:.6e} m", diff_pos);
    println!("  Velocity: {:.6e} m/s", diff_vel);

}
Output
Orbital elements (degrees):
  a    = 6878136.300 m = 500.0 km altitude
  e    = 0.0100
  i    = 97.8000°
  Ω    = 15.0000°
  ω    = 30.0000°
  M    = 45.0000°

Epoch: 2024-01-01 12:00:00.000 UTC
ECI state vector:
  Position: [1848964.106, -434937.468, 6560410.530] m
  Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s

ECEF state vector:
  Position: [757164.267, 1725863.563, 6564672.302] m
  Velocity: [989.350643, -7432.740021, 1896.768934] m/s


ECI state vector (transformed from ECEF):
  Position: [1848964.106, -434937.468, 6560410.530] m
  Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s

Round-trip error:
  Position: 9.617484e-10 m
  Velocity: 9.094947e-13 m/s
Orbital elements (degrees):
  a    = 6878136.300 m = 500.0 km altitude
  e    = 0.0100
  i    = 97.8000°
  Ω    = 15.0000°
  ω    = 30.0000°
  M    = 45.0000°

Epoch: 2024-01-01 12:00:00.000 UTC
ECI state vector:
  Position: [1848964.106, -434937.468, 6560410.530] m
  Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s

ECEF state vector:
  Position: [757164.267, 1725863.563, 6564672.302] m
  Velocity: [989.350643, -7432.740021, 1896.768934] m/s


ECI state vector (transformed from ECEF):
  Position: [1848964.106, -434937.468, 6560410.530] m
  Velocity: [-7098.379734, -2173.344867, 1913.333385] m/s

Round-trip error:
  Position: 9.617484e-10 m
  Velocity: 9.094947e-13 m/s

Velocity Transformation

Simply rotating velocity vectors will not yield correct velocity components in the ECI frame due to the Earth's rotation. State vector transformation functions properly account for observed velocity changes when transforming from the rotating ECEF frame.

Rotation Matrix

Get the rotation matrix from ECEF to ECI:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Define epoch
epc = bh.Epoch(2024, 1, 1, 12, 0, 0.0, time_system=bh.UTC)

# Get rotation matrix from ECEF to ECI
R_ecef_to_eci = bh.rotation_ecef_to_eci(epc)

print(f"Epoch: {epc.to_datetime()}")
print("\nECEF to ECI rotation matrix:")
print(
    f"  [{R_ecef_to_eci[0, 0]:10.7f}, {R_ecef_to_eci[0, 1]:10.7f}, {R_ecef_to_eci[0, 2]:10.7f}]"
)
print(
    f"  [{R_ecef_to_eci[1, 0]:10.7f}, {R_ecef_to_eci[1, 1]:10.7f}, {R_ecef_to_eci[1, 2]:10.7f}]"
)
print(
    f"  [{R_ecef_to_eci[2, 0]:10.7f}, {R_ecef_to_eci[2, 1]:10.7f}, {R_ecef_to_eci[2, 2]:10.7f}]\n"
)

R_eci_to_ecef = bh.rotation_eci_to_ecef(epc)
print("Verification: R_ecef_to_eci = R_eci_to_ecef^T")
print(f"  Max difference: {np.max(np.abs(R_ecef_to_eci - R_eci_to_ecef.T)):.2e}\n")

# Define orbital elements in degrees for satellite position
oe = np.array(
    [
        bh.R_EARTH + 500e3,  # Semi-major axis (m)
        0.01,  # Eccentricity
        97.8,  # Inclination (deg)
        15.0,  # RAAN (deg)
        30.0,  # Argument of periapsis (deg)
        45.0,  # Mean anomaly (deg)
    ]
)

# Convert to ECI Cartesian state and extract position
state_eci = bh.state_koe_to_eci(oe, bh.AngleFormat.DEGREES)

# Transform to ECEF
pos_ecef = bh.position_eci_to_ecef(epc, state_eci[0:3])

print("Satellite position in ECEF:")
print(f"  [{pos_ecef[0]:.3f}, {pos_ecef[1]:.3f}, {pos_ecef[2]:.3f}] m\n")

# Transform back to ECI using rotation matrix
pos_eci = R_ecef_to_eci @ pos_ecef

print("Satellite position in ECI (using rotation matrix):")
print(f"  [{pos_eci[0]:.3f}, {pos_eci[1]:.3f}, {pos_eci[2]:.3f}] m")

pos_eci_direct = bh.position_ecef_to_eci(epc, pos_ecef)
print("\nSatellite position in ECI (using position_ecef_to_eci):")
print(
    f"  [{pos_eci_direct[0]:.3f}, {pos_eci_direct[1]:.3f}, {pos_eci_direct[2]:.3f}] m"
)
use brahe as bh;
use nalgebra as na;

fn main() {
    bh::initialize_eop().unwrap();

    // Define epoch
    let epc = bh::Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    let epc = epc + 12.0 * 3600.0;  // Add 12 hours

    // Get rotation matrix from ECEF to ECI
    let r_ecef_to_eci = bh::rotation_ecef_to_eci(epc);

    println!("Epoch: 2024-01-01 12:00:00 UTC");
    println!("\nECEF to ECI rotation matrix:");
    println!("  [{:10.7}, {:10.7}, {:10.7}]", r_ecef_to_eci[(0, 0)], r_ecef_to_eci[(0, 1)], r_ecef_to_eci[(0, 2)]);
    println!("  [{:10.7}, {:10.7}, {:10.7}]", r_ecef_to_eci[(1, 0)], r_ecef_to_eci[(1, 1)], r_ecef_to_eci[(1, 2)]);
    println!("  [{:10.7}, {:10.7}, {:10.7}]\n", r_ecef_to_eci[(2, 0)], r_ecef_to_eci[(2, 1)], r_ecef_to_eci[(2, 2)]);

    let r_eci_to_ecef = bh::rotation_eci_to_ecef(epc);
    let diff = (r_ecef_to_eci - r_eci_to_ecef.transpose()).abs();
    let max_diff = diff.max();
    println!("Verification: R_ecef_to_eci = R_eci_to_ecef^T");
    println!("  Max difference: {:.2e}\n", max_diff);

    // Define orbital elements in degrees for satellite position
    let oe = na::SVector::<f64, 6>::new(
        bh::R_EARTH + 500e3,  // Semi-major axis (m)
        0.01,                  // Eccentricity
        97.8,                  // Inclination (deg)
        15.0,                  // RAAN (deg)
        30.0,                  // Argument of periapsis (deg)
        45.0,                  // Mean anomaly (deg)
    );

    // Convert to ECI Cartesian state and extract position
    let state_eci = bh::state_koe_to_eci(oe, bh::AngleFormat::Degrees);
    let pos_eci_orig = na::Vector3::new(state_eci[0], state_eci[1], state_eci[2]);

    // Transform to ECEF
    let pos_ecef = bh::position_eci_to_ecef(epc, pos_eci_orig);

    println!("Satellite position in ECEF:");
    println!("  [{:.3}, {:.3}, {:.3}] m\n", pos_ecef[0], pos_ecef[1], pos_ecef[2]);

    // Transform back to ECI using rotation matrix
    let pos_eci = r_ecef_to_eci * pos_ecef;

    println!("Satellite position in ECI (using rotation matrix):");
    println!("  [{:.3}, {:.3}, {:.3}] m", pos_eci[0], pos_eci[1], pos_eci[2]);

    let pos_eci_direct = bh::position_ecef_to_eci(epc, pos_ecef);
    println!("\nSatellite position in ECI (using position_ecef_to_eci):");
    println!("  [{:.3}, {:.3}, {:.3}] m", pos_eci_direct[0], pos_eci_direct[1], pos_eci_direct[2]);
}
Output
Epoch: (2024, 1, 1, 12, 0, 0.0, 0.0)

ECEF to ECI rotation matrix:
  [ 0.1794538,  0.9837637,  0.0023225]
  [-0.9837663,  0.1794542,  0.0000338]
  [-0.0003836, -0.0022908,  0.9999973]

Verification: R_ecef_to_eci = R_eci_to_ecef^T
  Max difference: 0.00e+00

Satellite position in ECEF:
  [757164.267, 1725863.563, 6564672.302] m

Satellite position in ECI (using rotation matrix):
  [1848964.106, -434937.468, 6560410.530] m

Satellite position in ECI (using position_ecef_to_eci):
  [1848964.106, -434937.468, 6560410.530] m
Epoch: 2024-01-01 12:00:00 UTC

ECEF to ECI rotation matrix:
  [ 0.1794538,  0.9837637,  0.0023225]
  [-0.9837663,  0.1794542,  0.0000338]
  [-0.0003836, -0.0022908,  0.9999973]

Verification: R_ecef_to_eci = R_eci_to_ecef^T
  Max difference: 0.00e0

Satellite position in ECEF:
  [757164.267, 1725863.563, 6564672.302] m

Satellite position in ECI (using rotation matrix):
  [1848964.106, -434937.468, 6560410.530] m

Satellite position in ECI (using position_ecef_to_eci):
  [1848964.106, -434937.468, 6560410.530] m

See Also