Skip to content

Trajectory

Trajectory is a dynamically sized trajectory container that stores time-series state data with runtime-determined dimensions. Unlike static trajectory types, Trajectory allows you to specify the state vector dimension at creation time, making it ideal for applications where the dimension varies or is not known at compile time.

Use Trajectory when:

  • State dimension is determined at runtime
  • You need flexibility to work with different dimensions in the same codebase
  • State vectors are non-standard (not 3D or 6D)
  • Flexibility is prioritized over maximum performance

Initialization

Empty Trajectory

Create an empty trajectory by specifying the state dimension. The default dimension is 6 (suitable for position + velocity states):

import brahe as bh

bh.initialize_eop()

# Create 6D trajectory (default)
traj = bh.Trajectory()
print(f"Dimension: {traj.dimension()}")
# Dimension: 6

# Create 3D trajectory (position only)
traj_3d = bh.Trajectory(3)
print(f"Dimension: {traj_3d.dimension()}")
# Dimension: 3

# Create 12D trajectory (custom)
traj_12d = bh.Trajectory(12)
print(f"Dimension: {traj_12d.dimension()}")
# Dimension: 12
use brahe as bh;
use bh::trajectories::DTrajectory;

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

    // Create 6D trajectory (default)
    let traj = DTrajectory::default();
    println!("Dimension: {}", traj.dimension());
    // Output: 6

    // Create 3D trajectory
    let traj_3d = DTrajectory::new(3);
    println!("Dimension: {}", traj_3d.dimension());
    // Output: 3

    // Create 12D trajectory
    let traj_12d = DTrajectory::new(12);
    println!("Dimension: {}", traj_12d.dimension());
    // Output: 12
}

From Existing Data

Create a trajectory from existing epochs and states:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create epochs
epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)
epoch1 = epoch0 + 60.0  # 1 minute later
epoch2 = epoch0 + 120.0  # 2 minutes later

# Create states (6D: position + velocity)
state0 = np.array([bh.R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0])
state1 = np.array([bh.R_EARTH + 500e3, 456000.0, 0.0, -7600.0, 0.0, 0.0])
state2 = np.array([bh.R_EARTH + 500e3, 0.0, 0.0, 0.0, -7600.0, 0.0])

# Create trajectory from data
epochs = [epoch0, epoch1, epoch2]
states = np.array([state0, state1, state2])
traj = bh.Trajectory.from_data(epochs, states)

print(f"Trajectory length: {len(traj)}")
# Trajectory length: 3
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create epochs
    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);
    let epoch1 = epoch0 + 60.0;  // 1 minute later
    let epoch2 = epoch0 + 120.0;  // 2 minutes later

    // Create states
    let state0 = na::DVector::from_vec(vec![
        R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0
    ]);
    let state1 = na::DVector::from_vec(vec![
        R_EARTH + 500e3, 456000.0, 0.0, -7600.0, 0.0, 0.0
    ]);
    let state2 = na::DVector::from_vec(vec![
        R_EARTH + 500e3, 0.0, 0.0, 0.0, -7600.0, 0.0
    ]);

    // Create trajectory from data
    let epochs = vec![epoch0, epoch1, epoch2];
    let states = vec![state0, state1, state2];
    let traj = DTrajectory::from_data(epochs, states).unwrap();

    println!("Trajectory length: {}", traj.len());
    // Output: 3
}

Adding and Accessing States

Adding States

Add states to a trajectory one at a time:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create empty trajectory
traj = bh.Trajectory(6)

# Add states
epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)
state0 = np.array([bh.R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0])
traj.add(epoch0, state0)

print(f"Trajectory length: {len(traj)}")
# Trajectory length: 1

epoch1 = epoch0 + 60.0
state1 = np.array([bh.R_EARTH + 500e3, 456000.0, 0.0, -7600.0, 0.0, 0.0])
traj.add(epoch1, state1)

print(f"Trajectory length: {len(traj)}")
# Trajectory length: 2
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create empty trajectory
    let mut traj = DTrajectory::new(6);

    // Add states
    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);
    let state0 = na::DVector::from_vec(vec![
        R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0
    ]);
    traj.add(epoch0, state0);

    println!("Trajectory length: {}", traj.len());
    // Trajectory length: 1

    let epoch1 = epoch0 + 60.0;
    let state1 = na::DVector::from_vec(vec![
        R_EARTH + 500e3, 456000.0, 0.0, -7600.0, 0.0, 0.0
    ]);
    traj.add(epoch1, state1);

    println!("Trajectory length: {}", traj.len());
    // Trajectory length: 2
}

Accessing by Index

Retrieve states and epochs by their index:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create and populate trajectory
traj = bh.Trajectory(6)
epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)
state0 = np.array([bh.R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0])
traj.add(epoch0, state0)

epoch1 = epoch0 + 60.0
state1 = np.array([bh.R_EARTH + 600e3, 456000.0, 0.0, -7600.0, 0.0, 0.0])
traj.add(epoch1, state1)

epoch2 = epoch0 + 120.0
state2 = np.array([bh.R_EARTH + 700e3, 0.0, 0.0, 0.0, -7600.0, 0.0])
traj.add(epoch2, state2)

# Access by index
retrieved_epoch = traj.epoch_at_idx(1)
retrieved_state = traj.state_at_idx(1)

print(f"Epoch: {retrieved_epoch}")
print(f"Altitude: {retrieved_state[0] - bh.R_EARTH:.2f} m")

# Output:
# Epoch: 2024-01-01 00:01:00.000 UTC
# Altitude: 600000.00 m
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create and populate trajectory
    let mut traj = DTrajectory::new(6);
    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);
    let state0 = na::DVector::from_vec(vec![
        R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0
    ]);
    traj.add(epoch0, state0);

    let epoch1 = epoch0 + 60.0;
    let state1 = na::DVector::from_vec(vec![
        R_EARTH + 600e3, 456000.0, 0.0, -7600.0, 0.0, 0.0
    ]);
    traj.add(epoch1, state1);

    let epoch2 = epoch0 + 120.0;
    let state2 = na::DVector::from_vec(vec![
        R_EARTH + 700e3, 0.0, 0.0, 0.0, -7600.0, 0.0
    ]);
    traj.add(epoch2, state2);

    // Access by index
    let retrieved_epoch = traj.epoch_at_idx(1).unwrap();
    let retrieved_state = traj.state_at_idx(1).unwrap();

    println!("Epoch: {}", retrieved_epoch);
    println!("Altitude: {:.2} m", retrieved_state[0] - R_EARTH);
}

// Output:
// Epoch: 2024-01-01 00:01:00.000 UTC
// Altitude: 600000.00 m

Accessing by Epoch

Get states at or near specific epochs:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create trajectory with multiple states
traj = bh.Trajectory(6)
epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)

for i in range(5):
    epoch = epoch0 + i * 60.0
    state = np.array([bh.R_EARTH + 500e3 + i * 1000, 0.0, 0.0, 0.0, 7600.0, 0.0])
    traj.add(epoch, state)

# Get nearest state to a specific epoch
query_epoch = epoch0 + 120.0  # 2 minutes after start
nearest_epoch, nearest_state = traj.nearest_state(query_epoch)
print(
    f"Nearest state at t+120s altitude: {(nearest_state[0] - bh.R_EARTH) / 1e3:.2f} km"
)

# Get nearest state between stored epochs
query_epoch = epoch0 + 125.0  # Between stored epochs
nearest_epoch, nearest_state = traj.nearest_state(query_epoch)
print(
    f"Nearest state at t+125s altitude: {(nearest_state[0] - bh.R_EARTH) / 1e3:.2f} km"
)

# Output:
# Nearest state at t+120s altitude: 502.00 km
# Nearest state at t+125s altitude: 502.00 km
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create trajectory with multiple states
    let mut traj = DTrajectory::new(6);
    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);

    for i in 0..5 {
        let epoch = epoch0 + (i as f64) * 60.0;
        let state = na::DVector::from_vec(vec![
            R_EARTH + 500e3 + (i as f64) * 1000.0, 0.0, 0.0, 0.0, 7600.0, 0.0
        ]);
        traj.add(epoch, state);
    }

    // Get nearest state to a specific epoch
    let query_epoch1 = epoch0 + 120.0;  // 2 minutes after start
    let (_, nearest_state) = traj.nearest_state(&query_epoch1).unwrap();
    println!("Nearest state at t+120s altitude: {:.2} km",
        (nearest_state[0] - R_EARTH) / 1e3);

    // Get nearest state between stored epochs
    let query_epoch2 = epoch0 + 125.0;  // Between stored epochs
    let (_, nearest_state) = traj.nearest_state(&query_epoch2).unwrap();
    println!("Nearest state at t+125s altitude: {:.2} km",
        (nearest_state[0] - R_EARTH) / 1e3);
}

// Output:
// Nearest state at t+120s altitude: 502.00 km
// Nearest state at t+125s altitude: 502.00 km

Querying Trajectory Properties

Time Span and Bounds

Query the temporal extent of a trajectory:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create trajectory spanning 5 minutes
traj = bh.Trajectory(6)
epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)

for i in range(6):
    epoch = epoch0 + i * 60.0
    state = np.array([bh.R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0])
    traj.add(epoch, state)

# Query properties
print(f"Number of states: {len(traj)}")
print(f"Start epoch: {traj.start_epoch()}")
print(f"End epoch: {traj.end_epoch()}")
print(f"Timespan: {traj.timespan():.1f} seconds")
print(f"Is empty: {traj.is_empty()}")

# Access first and last states
first_epoch, first_state = traj.first()
last_epoch, last_state = traj.last()
print(f"First epoch: {first_epoch}")
print(f"Last epoch: {last_epoch}")

# Output:
# Number of states: 6
# Start epoch: 2024-01-01 00:00:00.000 UTC
# End epoch: 2024-01-01 00:05:00.000 UTC
# Timespan: 300.0 seconds
# Is empty: False
# First epoch: 2024-01-01 00:00:00.000 UTC
# Last epoch: 2024-01-01 00:05:00.000 UTC
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create trajectory spanning 5 minutes
    let mut traj = DTrajectory::new(6);
    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);

    for i in 0..6 {
        let epoch = epoch0 + (i as f64) * 60.0;
        let state = na::DVector::from_vec(vec![
            R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0
        ]);
        traj.add(epoch, state);
    }

    // Query properties
    println!("Number of states: {}", traj.len());
    println!("Start epoch: {}", traj.start_epoch().unwrap());
    println!("End epoch: {}", traj.end_epoch().unwrap());
    println!("Timespan: {:.1} seconds", traj.timespan().unwrap());
    println!("Is empty: {}", traj.is_empty());

    // Access first and last states
    let (first_epoch, _first_state) = traj.first().unwrap();
    let (last_epoch, _last_state) = traj.last().unwrap();
    println!("First epoch: {}", first_epoch);
    println!("Last epoch: {}", last_epoch);
}

// Output:
// Number of states: 6
// Start epoch: 2024-01-01 00:00:00.000 UTC
// End epoch: 2024-01-01 00:05:00.000 UTC
// Timespan: 300.0 seconds
// Is empty: false
// First epoch: 2024-01-01 00:00:00.000 UTC
// Last epoch: 2024-01-01 00:05:00.000 UTC

Interpolation

Trajectory supports linear interpolation to estimate states at arbitrary epochs between stored data points:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create trajectory with sparse data
traj = bh.Trajectory(6)
epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)

# Add states every 60 seconds
for i in range(3):
    epoch = epoch0 + i * 60.0
    # Simplified motion: position changes linearly with time
    state = np.array([bh.R_EARTH + 500e3 + i * 10000, 0.0, 0.0, 0.0, 7600.0, 0.0])
    traj.add(epoch, state)

# Interpolate state at intermediate time
query_epoch = epoch0 + 30.0  # Halfway between first two states
interpolated_state = traj.interpolate(query_epoch)

print(f"Interpolated altitude: {(interpolated_state[0] - bh.R_EARTH) / 1e3:.2f} km")
# Expected: approximately 505 km (halfway between 500 and 510 km)

# Output:
# Interpolated altitude: 505.00 km
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::{Trajectory, InterpolatableTrajectory};
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create trajectory with sparse data
    let mut traj = DTrajectory::new(6);
    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);

    // Add states every 60 seconds
    for i in 0..3 {
        let epoch = epoch0 + (i as f64) * 60.0;
        let state = na::DVector::from_vec(vec![
            R_EARTH + 500e3 + (i as f64) * 10000.0, 0.0, 0.0, 0.0, 7600.0, 0.0
        ]);
        traj.add(epoch, state);
    }

    // Interpolate state at intermediate time
    let query_epoch = epoch0 + 30.0;  // Halfway between first two states
    let interpolated_state = traj.interpolate(&query_epoch).unwrap();

    println!("Interpolated altitude: {:.2} km",
        (interpolated_state[0] - R_EARTH) / 1e3);
    // Expected: approximately 505 km (halfway between 500 and 510 km)
}

// Output:
// Interpolated altitude: 505.00 km

Memory Management

Trajectory supports eviction policies to automatically manage memory in long-running applications:

Maximum Size Policy

Keep only the N most recent states:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create trajectory with max size limit
traj = bh.Trajectory(6).with_eviction_policy_max_size(3)

epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)

# Add 5 states
for i in range(5):
    epoch = epoch0 + i * 60.0
    state = np.array([bh.R_EARTH + 500e3 + i * 1000, 0.0, 0.0, 0.0, 7600.0, 0.0])
    traj.add(epoch, state)

# Only the 3 most recent states are kept
print(f"Trajectory length: {len(traj)}")
print(f"Start epoch: {traj.start_epoch()}")
print(f"Start altitude: {(traj.state_at_idx(0)[0] - bh.R_EARTH) / 1e3:.2f} km")
# Output: ~502 km (states 0 and 1 were evicted)

# Output
# Trajectory length: 3
# Start epoch: 2024-01-01 00:02:00.000 UTC
# Start altitude: 502.00 km
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create trajectory with max size limit
    let mut traj = DTrajectory::new(6)
        .with_eviction_policy_max_size(3);

    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);

    // Add 5 states
    for i in 0..5 {
        let epoch = epoch0 + (i as f64) * 60.0;
        let state = na::DVector::from_vec(vec![
            R_EARTH + 500e3 + (i as f64) * 1000.0, 0.0, 0.0, 0.0, 7600.0, 0.0
        ]);
        traj.add(epoch, state);
    }

    // Only the 3 most recent states are kept
    println!("Trajectory length: {}", traj.len());
    println!("Start epoch: {}", traj.start_epoch().unwrap());
    println!("Start altitude: {:.2} km",
        (traj.state_at_idx(0).unwrap()[0] - R_EARTH) / 1e3);
    // Output: ~502 km (states 0 and 1 were evicted)
}

// Output
// Trajectory length: 3
// Start epoch: 2024-01-01 00:02:00.000 UTC
// Start altitude: 502.00 km

Maximum Age Policy

Keep only states within a time window:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Keep only states within last 2 minutes (120 seconds)
traj = bh.Trajectory(6).with_eviction_policy_max_age(120.0)

epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)

# Add states spanning 4 minutes
for i in range(5):
    epoch = epoch0 + i * 60.0
    state = np.array([bh.R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0])
    traj.add(epoch, state)

# Only states within 120 seconds of the most recent are kept
print(f"Trajectory length: {len(traj)}")
print(f"Timespan: {traj.timespan():.1f} seconds")

# Output:
# Trajectory length: 3
# Timespan: 120.0 seconds
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Keep only states within last 2 minutes (120 seconds)
    let mut traj = DTrajectory::new(6)
        .with_eviction_policy_max_age(120.0);

    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);

    // Add states spanning 4 minutes
    for i in 0..5 {
        let epoch = epoch0 + (i as f64) * 60.0;
        let state = na::DVector::from_vec(vec![
            R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0, 0.0
        ]);
        traj.add(epoch, state);
    }

    // Only states within 120 seconds of the most recent are kept
    println!("Trajectory length: {}", traj.len());
    println!("Timespan: {:.1} seconds", traj.timespan().unwrap());
}

// Output:
// Trajectory length: 3
// Timespan: 120.0 seconds

Iteration

Trajectories can be iterated to process all epoch-state pairs:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create and populate trajectory
traj = bh.Trajectory(6)
epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)

for i in range(3):
    epoch = epoch0 + i * 60.0
    state = np.array([bh.R_EARTH + 500e3 + i * 1000, 0.0, 0.0, 0.0, 7600.0, 0.0])
    traj.add(epoch, state)

# Iterate over all states
for epoch, state in traj:
    altitude = (state[0] - bh.R_EARTH) / 1e3
    print(f"Epoch: {epoch}, Altitude: {altitude:.2f} km")

# Output:
# Epoch: 2024-01-01 00:00:00.000 UTC, Altitude: 500.00 km
# Epoch: 2024-01-01 00:01:00.000 UTC, Altitude: 501.00 km
# Epoch: 2024-01-01 00:02:00.000 UTC, Altitude: 502.00 km
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create and populate trajectory
    let mut traj = DTrajectory::new(6);
    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);

    for i in 0..3 {
        let epoch = epoch0 + (i as f64) * 60.0;
        let state = na::DVector::from_vec(vec![
            R_EARTH + 500e3 + (i as f64) * 1000.0, 0.0, 0.0, 0.0, 7600.0, 0.0
        ]);
        traj.add(epoch, state);
    }

    // Iterate over all states
    for (epoch, state) in &traj {
        let altitude = (state[0] - R_EARTH) / 1e3;
        println!("Epoch: {}, Altitude: {:.2} km", epoch, altitude);
    }

    // Output:
    // Epoch: 2024-01-01 00:00:00.000 UTC, Altitude: 500.00 km
    // Epoch: 2024-01-01 00:01:00.000 UTC, Altitude: 501.00 km
    // Epoch: 2024-01-01 00:02:00.000 UTC, Altitude: 502.00 km
}

Matrix Export

Convert trajectory data to matrix format for analysis or export:

import brahe as bh
import numpy as np

bh.initialize_eop()

# Create trajectory
traj = bh.Trajectory(6)
epoch0 = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)

for i in range(3):
    epoch = epoch0 + i * 60.0
    state = np.array([bh.R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0 + i * 10, 0.0])
    traj.add(epoch, state)

# Convert to matrix (rows are states, columns are dimensions)
matrix = traj.to_matrix()
print(f"Matrix type: {type(matrix)}")
print(f"Matrix shape: {matrix.shape}")
print(f"First state velocity: {matrix[0, 4]:.1f} m/s")

# Output:
# Matrix type: <class 'numpy.ndarray'>
# Matrix shape: (3, 6)
# First state velocity: 7600.0 m/s
use brahe as bh;
use bh::time::Epoch;
use bh::trajectories::DTrajectory;
use bh::traits::Trajectory;
use bh::constants::R_EARTH;
use nalgebra as na;

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

    // Create trajectory
    let mut traj = DTrajectory::new(6);
    let epoch0 = Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0,
        bh::time::TimeSystem::UTC);

    for i in 0..3 {
        let epoch = epoch0 + (i as f64) * 60.0;
        let state = na::DVector::from_vec(vec![
            R_EARTH + 500e3, 0.0, 0.0, 0.0, 7600.0 + (i as f64) * 10.0, 0.0
        ]);
        traj.add(epoch, state);
    }

    // Convert to matrix (rows are states, columns are dimensions)
    let matrix = traj.to_matrix().unwrap();
    println!("Matrix shape: ({}, {})", matrix.nrows(), matrix.ncols());
    println!("First state velocity: {:.1} m/s", matrix[(0, 4)]);
}

// Output:
// Matrix shape: (3, 6)
// First state velocity: 7600.0 m/s

See Also