Skip to content

SGP Propagation

The SGPPropagator implements the SGP4/SDP4 propagation models for orbital prediction. SGP4 is a standard method for satellite tracking and includes simplified perturbations from Earth oblateness and atmospheric drag, making it suitable for operational satellite tracking and near-Earth orbit propagation. It is widely used with Two-Line Element (TLE) data provided by NORAD and other space tracking organizations.

For complete API documentation, see the SGPPropagator API Reference.

TLE Format Support

SGP4 propagation is based on Two-Line Element (TLE) sets, a compact data format for orbital elements. Brahe supports both traditional and modern TLE formats:

  • Classic Format: Traditional numeric NORAD catalog numbers (5 digits, up to 99999)
  • Alpha-5 Format: Extended alphanumeric catalog numbers for satellites beyond 99999

The initialization automatically detects and handles both formats.

Initialization

The SGPPropagator is initialized from TLE data. The TLE lines contain all orbital parameters needed for propagation.

From Two Line Elements (TLE)

The most common initialization uses two lines of TLE data.

import brahe as bh
import numpy as np

bh.initialize_eop()  # Required for accurate frame transformations

# ISS TLE data (example)
line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"

# Create propagator with 60-second step size
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

print(f"NORAD ID: {prop.norad_id}")
print(f"TLE epoch: {prop.epoch}")
print(
    f"Initial position magnitude: {np.linalg.norm(prop.initial_state()[:3]) / 1e3:.1f} km"
)
# Expected output:
# NORAD ID: 25544
# TLE epoch: 2008-09-20 12:25:40.104 UTC
# Initial position magnitude: 6720.2 km
use brahe as bh;
use brahe::traits::SStatePropagator;

fn main() {
    bh::initialize_eop().unwrap();  // Required for accurate frame transformations

    // ISS TLE data (example)
    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";

    // Create propagator with 60-second step size
    let prop = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap();

    println!("NORAD ID: {}", prop.norad_id);
    println!("TLE epoch: {}", prop.epoch);
    println!("Initial position magnitude: {:.1} km",
             prop.initial_state().fixed_rows::<3>(0).norm() / 1e3);
    // Expected output:
    // NORAD ID: 25544
    // TLE epoch: 2008-09-20 12:25:40.104 UTC
    // Initial position magnitude: 6720.2 km
}

From 3-Line Elements (3LE)

Three-line TLE format includes an optional satellite name on the first line.

import brahe as bh

bh.initialize_eop()

# 3-line TLE with satellite name
name = "ISS (ZARYA)"
line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"

# Create propagator with satellite name
prop = bh.SGPPropagator.from_3le(name, line1, line2, 60.0)

print(f"Satellite name: {prop.satellite_name}")
print(f"NORAD ID: {prop.norad_id}")
# Expected output:
# Satellite name: ISS (ZARYA)
# NORAD ID: 25544
use brahe as bh;

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

    // 3-line TLE with satellite name
    let name = "ISS (ZARYA)";
    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";

    // Create propagator with satellite name
    let prop = bh::SGPPropagator::from_3le(Some(name), line1, line2, 60.0).unwrap();

    println!("Satellite name: {:?}", prop.satellite_name);
    println!("NORAD ID: {}", prop.norad_id);
    // Expected output:
    // Satellite name: Some("ISS (ZARYA)")
    // NORAD ID: 25544
}

Configuring Output Format

By default, SGP4 outputs states in ECI Cartesian coordinates. Use with_output_format() to configure the output frame and representation.

import brahe as bh

bh.initialize_eop()

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"

# Create with ECEF Cartesian output
prop_ecef = bh.SGPPropagator.from_tle(line1, line2, 60.0)
prop_ecef.set_output_format(bh.OrbitFrame.ECEF, bh.OrbitRepresentation.CARTESIAN, None)

# Or with Keplerian output (ECI only)
prop_kep = bh.SGPPropagator.from_tle(line1, line2, 60.0)
prop_kep.set_output_format(
    bh.OrbitFrame.ECI, bh.OrbitRepresentation.KEPLERIAN, bh.AngleFormat.DEGREES
)

# Propagate to 1 hour after epoch
dt = 3600.0
prop_ecef.propagate_to(prop_ecef.epoch + dt)
prop_kep.propagate_to(prop_kep.epoch + dt)
print(f"ECEF position (km): {prop_ecef.current_state()[:3] / 1e3}")
state_kep = prop_kep.current_state()
print(
    f"Keplerian elements: [{state_kep[0]:.1f} km, {state_kep[1]:.4f}, {state_kep[2]:.4f}, "
    f"{state_kep[3]:.4f} deg, {state_kep[4]:.4f} deg, {state_kep[5]:.4f} deg]"
)

# Expected output:
# ECEF position (km): [ 5548.63233725  2869.31027561 -2526.64252368]
# Keplerian elements: [8198150.8 km, 0.1789, 47.9402, 249.8056 deg, 323.0545 deg, 4.5675 deg]
use brahe as bh;
use brahe::traits::{OrbitFrame, SStatePropagator, OrbitRepresentation};

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

    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";

    // Create with ECEF Cartesian output
    let mut prop_ecef = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap()
        .with_output_format(OrbitFrame::ECEF, OrbitRepresentation::Cartesian, None);

    // Or with Keplerian output (ECI only)
    let mut prop_kep = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap()
        .with_output_format(OrbitFrame::ECI, OrbitRepresentation::Keplerian, Some(bh::AngleFormat::Degrees));

    // Propagate to 1 hour after epoch
    let dt = 3600.0;
    prop_ecef.propagate_to(prop_ecef.epoch + dt);
    prop_kep.propagate_to(prop_kep.epoch + dt);

    let state_ecef = prop_ecef.current_state();
    println!("ECEF position (km): [{:.3}, {:.3}, {:.3}]",
             state_ecef[0] / 1e3, state_ecef[1] / 1e3, state_ecef[2] / 1e3);

    let state_kep = prop_kep.current_state();
    println!("Keplerian elements: [{:.1} km, {:.4}, {:.4}, {:.4} deg, {:.4} deg, {:.4} deg]",
             state_kep[0] / 1e3, state_kep[1], state_kep[2],
             state_kep[3], state_kep[4], state_kep[5]);
}

// Output:
// ECEF position (km): [5548.632, 2869.310, -2526.643]
// Keplerian elements: [8198.2 km, 0.1789, 47.9402, 249.8056 deg, 323.0545 deg, 4.5675 deg]

Stepping Through Time

The SGP propagator uses the same stepping interface as other propagators through the OrbitPropagator trait.

Single and Multiple Steps

import brahe as bh

bh.initialize_eop()

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

# Single step (60 seconds)
prop.step()
print(f"After 1 step: {prop.current_epoch}")

# Multiple steps
prop.propagate_steps(10)
print(f"After 11 total steps: {len(prop.trajectory)} states")

# Step by custom duration
prop.step_by(120.0)
print(f"After custom step: {prop.current_epoch}")

# Expected output:
# After 1 step: 2008-09-20 12:26:40.104 UTC
# After 11 total steps: 12 states
# After custom step: 2008-09-20 12:38:40.104 UTC
use brahe as bh;
use brahe::traits::{SStatePropagator, Trajectory};

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

    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
    let mut prop = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap();

    // Single step (60 seconds)
    prop.step();
    println!("After 1 step: {}", prop.current_epoch());

    // Multiple steps
    prop.propagate_steps(10);
    println!("After 11 total steps: {} states", prop.trajectory.len());

    // Step by custom duration
    prop.step_by(120.0);
    println!("After custom step: {}", prop.current_epoch());
}

// Output
// After 1 step: 2008-09-20 12:26:40.104 UTC
// After 11 total steps: 12 states
// After custom step: 2008-09-20 12:38:40.104 UTC

Propagate to Target Epoch

import brahe as bh

bh.initialize_eop()

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

# Propagate to specific epoch
target = prop.epoch + 7200.0  # 2 hours later
prop.propagate_to(target)

print(f"Target epoch: {target}")
print(f"Current epoch: {prop.current_epoch}")
print(f"Trajectory contains {len(prop.trajectory)} states")

# Expected output:
# Target epoch: 2008-09-20 14:25:40.104 UTC
# Current epoch: 2008-09-20 14:25:40.104 UTC
# Trajectory contains 121 states
use brahe as bh;
use brahe::traits::{SStatePropagator, Trajectory};

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

    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
    let mut prop = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap();

    // Propagate to specific epoch
    let target = prop.epoch + 7200.0;  // 2 hours later
    prop.propagate_to(target);

    println!("Target epoch: {}", target);
    println!("Current epoch: {}", prop.current_epoch());
    println!("Trajectory contains {} states", prop.trajectory.len());

    // Expected output:
    // Target epoch: 2008-09-20 14:25:40.104 UTC
    // Current epoch: 2008-09-20 14:25:40.104 UTC
    // Trajectory contains 121 states
}

Direct State Queries

The SGP propagator implements the StateProvider trait, allowing direct state computation at arbitrary epochs without stepping. Because SGP4 uses closed-form solutions, state queries are efficient and do not require building a trajectory.

Single Epoch Queries

import brahe as bh

bh.initialize_eop()

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

# Query state 1 orbit later (doesn't add to trajectory)
query_epoch = prop.epoch + 5400.0  # ~90 minutes

state_eci = prop.state_eci(query_epoch)  # ECI Cartesian
state_ecef = prop.state_ecef(query_epoch)  # ECEF Cartesian
state_kep = prop.state_koe_osc(
    query_epoch, bh.AngleFormat.DEGREES
)  # Osculating Keplerian

print(
    f"ECI position: [{state_eci[0] / 1e3:.1f}, {state_eci[1] / 1e3:.1f}, "
    f"{state_eci[2] / 1e3:.1f}] km"
)
print(f"Osculating semi-major axis: {state_kep[0] / 1e3:.1f} km")

# Expected output:
# ECI position: [3822.2, -1684.2, 5264.9] km
# Osculating semi-major axis: 6725.4 km
use brahe as bh;
use brahe::traits::SOrbitStateProvider;

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

    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
    let prop = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap();

    // Query state 1 orbit later (doesn't add to trajectory)
    let query_epoch = prop.epoch + 5400.0;  // ~90 minutes

    let state_eci = prop.state_eci(query_epoch).unwrap();          // ECI Cartesian
    let _state_ecef = prop.state_ecef(query_epoch).unwrap();        // ECEF Cartesian
    let state_kep = prop.state_koe_osc(query_epoch, bh::AngleFormat::Degrees).unwrap();    // Osculating Keplerian

    println!("ECI position: [{:.1}, {:.1}, {:.1}] km",
             state_eci[0]/1e3, state_eci[1]/1e3, state_eci[2]/1e3);
    println!("Osculating semi-major axis: {:.1} km", state_kep[0]/1e3);

    // Expected output:
    // ECI position: [3822.2, -1684.2, 5264.9] km
    // Osculating semi-major axis: 6725.4 km
}

Batch Queries

import brahe as bh
import numpy as np

bh.initialize_eop()

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

# Generate states for multiple orbits
orbital_period = 5400.0  # Approximate ISS period (seconds)
query_epochs = [prop.epoch + i * orbital_period for i in range(5)]
states_eci = prop.states_eci(query_epochs)

print(f"Generated {len(states_eci)} states over {len(query_epochs)} orbits")
for i, state in enumerate(states_eci):
    altitude = (np.linalg.norm(state[:3]) - bh.R_EARTH) / 1e3
    print(f"  Orbit {i}: altitude = {altitude:.1f} km")
# Expected output:
# Generated 5 states over 5 orbits
#   Orbit 0: altitude = 342.1 km
#   Orbit 1: altitude = 342.3 km
#   Orbit 2: altitude = 342.7 km
#   Orbit 3: altitude = 343.3 km
#   Orbit 4: altitude = 344.0 km
use brahe as bh;
use bh::utils::DOrbitStateProvider;

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

    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
    let prop = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap();

    // Generate states for multiple orbits
    let orbital_period = 5400.0;  // Approximate ISS period (seconds)
    let query_epochs: Vec<bh::Epoch> = (0..5)
        .map(|i| prop.epoch + i as f64 * orbital_period)
        .collect();
    let states_eci = prop.states_eci(&query_epochs).unwrap();

    println!("Generated {} states over {} orbits", states_eci.len(), query_epochs.len());
    for (i, state) in states_eci.iter().enumerate() {
        let altitude = (state.fixed_rows::<3>(0).norm() - bh::R_EARTH) / 1e3;
        println!("  Orbit {}: altitude = {:.1} km", i, altitude);
    }
    // Expected output:
    // Generated 5 states over 5 orbits
    //   Orbit 0: altitude = 342.1 km
    //   Orbit 1: altitude = 342.3 km
    //   Orbit 2: altitude = 342.7 km
    //   Orbit 3: altitude = 343.3 km
    //   Orbit 4: altitude = 344.0 km
}

Special: PEF Frame

SGP4 natively outputs states in the TEME (True Equator Mean Equinox) frame. For specialized applications, you can access states in the intermediate PEF (Pseudo-Earth-Fixed) frame:

import brahe as bh

bh.initialize_eop()

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

# Get state in PEF frame (TEME rotated by GMST)
state_pef = prop.state_pef(prop.epoch)
print(f"PEF position: {state_pef[:3] / 1e3}")
# Expected output:
# PEF position: [-3953.20574821  1427.51460044  5243.61453697]
use brahe as bh;

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

    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
    let prop = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap();

    // Get state in PEF frame (TEME rotated by GMST)
    let state_pef = prop.state_pef(prop.epoch);
    println!("PEF position: {:?}", state_pef.fixed_rows::<3>(0) / 1e3);
    // Expected output:
    // PEF position: [[-3953.2057482107907, 1427.514600436758, 5243.614536966578]]
}

Extracting Orbital Elements from TLE

The propagator can extract Keplerian orbital elements directly from the TLE data:

import brahe as bh

bh.initialize_eop()

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

# Extract Keplerian elements from TLE
elements_deg = prop.get_elements(bh.AngleFormat.DEGREES)
elements_rad = prop.get_elements(bh.AngleFormat.RADIANS)

print(f"Semi-major axis: {elements_deg[0] / 1e3:.1f} km")
print(f"Eccentricity: {elements_deg[1]:.6f}")
print(f"Inclination: {elements_deg[2]:.4f} degrees")
print(f"RAAN: {elements_deg[3]:.4f} degrees")
print(f"Argument of perigee: {elements_deg[4]:.4f} degrees")
print(f"Mean anomaly: {elements_deg[5]:.4f} degrees")
# Expected output:
# Semi-major axis: 6758.7 km
# Eccentricity: 0.000670
# Inclination: 51.6416 degrees
# RAAN: 247.4627 degrees
# Argument of perigee: 130.5360 degrees
# Mean anomaly: 325.0288 degrees
use brahe as bh;

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

    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
    let prop = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap();

    // Extract Keplerian elements from TLE
    let elements_deg = prop.get_elements(bh::AngleFormat::Degrees).unwrap();
    let _elements_rad = prop.get_elements(bh::AngleFormat::Radians).unwrap();

    println!("Semi-major axis: {:.1} km", elements_deg[0]/1e3);
    println!("Eccentricity: {:.6}", elements_deg[1]);
    println!("Inclination: {:.4} degrees", elements_deg[2]);
    println!("RAAN: {:.4} degrees", elements_deg[3]);
    println!("Argument of perigee: {:.4} degrees", elements_deg[4]);
    println!("Mean anomaly: {:.4} degrees", elements_deg[5]);
    // Expected output:
    // Semi-major axis: 6758.7 km
    // Eccentricity: 0.000670
    // Inclination: 51.6416 degrees
    // RAAN: 247.4627 degrees
    // Argument of perigee: 130.5360 degrees
    // Mean anomaly: 325.0288 degrees
}

Trajectory Management

SGP propagators support the same trajectory management as Keplerian propagators, including frame conversions and memory management.

Memory Management

import brahe as bh

bh.initialize_eop()

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

# Keep only 50 most recent states for memory efficiency
prop.set_eviction_policy_max_size(50)

# Propagate many steps
prop.propagate_steps(200)
print(f"Trajectory length: {len(prop.trajectory)}")  # Will be 50

# Alternative: Keep states within 30 minutes of current
prop.reset()
prop.set_eviction_policy_max_age(1800.0)  # 1800 seconds = 30 minutes
prop.propagate_steps(200)
print(f"Trajectory length with age policy: {len(prop.trajectory)}")
# Expected output:
# Trajectory length: 50
# Trajectory length with age policy: 31
use brahe as bh;
use brahe::traits::{SStatePropagator, Trajectory};

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

    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
    let mut prop = bh::SGPPropagator::from_tle(line1, line2, 60.0).unwrap();

    // Keep only 50 most recent states for memory efficiency
    prop.set_eviction_policy_max_size(50).unwrap();

    // Propagate many steps
    prop.propagate_steps(200);
    println!("Trajectory length: {}", prop.trajectory.len());  // Will be 50

    // Alternative: Keep states within 30 minutes of current
    prop.reset();
    prop.set_eviction_policy_max_age(1800.0).unwrap();  // 1800 seconds = 30 minutes
    prop.propagate_steps(200);
    println!("Trajectory length with age policy: {}", prop.trajectory.len());
    // Expected output:
    // Trajectory length: 50
    // Trajectory length with age policy: 31
}

Limitations and Considerations

Immutable Initial Conditions

Unlike the Keplerian propagator, SGP4 initial conditions are derived from the TLE and cannot be changed. Attempting to call set_initial_conditions() will result in a panic:

import brahe as bh
import numpy as np

line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"
prop = bh.SGPPropagator.from_tle(line1, line2, 60.0)

# This will raise an error - SGP initial conditions come from TLE
# prop.set_initial_conditions(...)  # Don't do this!

# To use different orbital elements, create a KeplerianPropagator instead
1
2
3
4
// This will panic - SGP initial conditions come from TLE
// prop.set_initial_conditions(...);  // Don't do this!

// To use different orbital elements, create a KeplerianPropagator instead

Identity Tracking

Like Keplerian propagators, SGP propagators support identity tracking:

import brahe as bh

bh.initialize_eop()

line0 = "ISS (ZARYA)"
line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927"
line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537"

# Create propagator and set identity
prop = bh.SGPPropagator.from_3le(line0, line1, line2, 60.0)

print(f"Name: {prop.get_name()}")
print(f"ID: {prop.get_id()}")
print(f"NORAD ID from TLE: {prop.norad_id}")
# Expected output:
# Name: ISS (ZARYA)
# ID: 25544
# NORAD ID from TLE: 25544
use brahe as bh;
use brahe::utils::Identifiable;

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

    let line0 = "ISS (ZARYA)";
    let line1 = "1 25544U 98067A   08264.51782528 -.00002182  00000-0 -11606-4 0  2927";
    let line2 = "2 25544  51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";

    // Create propagator and set identity
    let prop = bh::SGPPropagator::from_3le(Some(line0), line1, line2, 60.0).unwrap();

    println!("Name: {:?}", prop.get_name());
    println!("ID: {:?}", prop.get_id());
    println!("NORAD ID from TLE: {}", prop.norad_id);
    // Expected output:
    // Name:  Some("ISS (ZARYA)")
    // ID: Some(25544)
    // NORAD ID from TLE: 25544
}

See Also