Access Computation Access computation finds time windows when satellites can observe or communicate with ground locations, subject to geometric and operational constraints. Brahe provides the location_accesses() function as the primary function for finding accesses, with optional search configuration parameters to tune performance and accuracy.
Basic Workflow The simplest access computation requires: a location, a propagator, time bounds, and a constraint.
Python Rust
import brahe as bh
# Initialize Earth orientation data
bh . initialize_eop ()
# Define ground location (San Francisco, CA)
location = bh . PointLocation ( - 122.4194 , 37.7749 , 0.0 ) . with_name ( "San Francisco" )
# Create propagator from TLE (example for ISS)
tle_line1 = "1 25544U 98067A 25306.42331346 .00010070 00000-0 18610-3 0 9999"
tle_line2 = "2 25544 51.6344 342.0717 0004969 8.9436 351.1640 15.49700017536601"
propagator = bh . SGPPropagator . from_tle ( tle_line1 , tle_line2 , 60.0 ) . with_name ( "ISS" )
# Define time window (7 days starting from epoch)
epoch_start = bh . Epoch ( 2025 , 11 , 2 , 0 , 0 , 0.0 , 0.0 )
epoch_end = epoch_start + 7 * 86400.0
# Define constraint (minimum 10° elevation)
constraint = bh . ElevationConstraint ( min_elevation_deg = 10.0 )
# Compute access windows
windows = bh . location_accesses ( location , propagator , epoch_start , epoch_end , constraint )
# Process results
print ( f "Found { len ( windows ) } access windows" )
for i , window in enumerate ( windows [: 3 ], 1 ):
duration_min = window . duration / 60.0
print ( f " \n Window { i } :" )
print ( f " Start: { window . window_open } " )
print ( f " End: { window . window_close } " )
print ( f " Duration: { duration_min : .2f } minutes" )
# Access computed properties
elev_max = window . properties . elevation_max
print ( f " Max elevation: { elev_max : .1f } °" )
# Output:
# Found 35 access windows
# Window 1:
# Start: 2025-11-02 05:39:28.345 UTC
# End: 2025-11-02 05:44:00.000 UTC
# Duration: 4.53 minutes
# Max elevation: 18.7°
# Window 2:
# Start: 2025-11-02 07:15:16.033 UTC
# End: 2025-11-02 07:21:00.000 UTC
# Duration: 5.73 minutes
# Max elevation: 38.9°
# Window 3:
# Start: 2025-11-02 08:54:59.619 UTC
# End: 2025-11-02 08:56:00.000 UTC
# Duration: 1.01 minutes
# Max elevation: 10.9°
use brahe as bh ;
use bh :: utils :: Identifiable ;
fn main () -> Result < (), Box < dyn std :: error :: Error >> {
// Initialize EOP
bh :: initialize_eop () ? ;
// Define ground location
let location = bh :: PointLocation :: new (
- 122.4194 ,
37.7749 ,
0.0 ,
)
. with_name ( "San Francisco" );
// Create propagator from TLE
let tle_line1 = "1 25544U 98067A 25306.42331346 .00010070 00000-0 18610-3 0 9999" ;
let tle_line2 = "2 25544 51.6344 342.0717 0004969 8.9436 351.1640 15.49700017536601" ;
let propagator = bh :: SGPPropagator :: from_tle ( tle_line1 , tle_line2 , 60.0 ) ?
. with_name ( "ISS" );
// Define time window
let epoch_start = bh :: Epoch :: from_datetime ( 2025 , 11 , 2 , 0 , 0 , 0.0 , 0.0 , bh :: TimeSystem :: UTC );
let epoch_end = epoch_start + 7.0 * 86400.0 ;
// Define constraint
let constraint = bh :: ElevationConstraint :: new ( Some ( 10.0 ), None ) ? ;
// Compute access windows
let windows = bh :: location_accesses (
& location ,
& propagator ,
epoch_start ,
epoch_end ,
& constraint ,
None , // Use default config
None , // No custom property computers
None , // No progress callback
) ? ;
// Process results
println! ( "Found {} access windows" , windows . len ());
for ( i , window ) in windows . iter (). take ( 3 ). enumerate () {
let duration_min = window . duration () / 60.0 ;
println! ( " \n Window {}:" , i + 1 );
println! ( " Start: {}" , window . window_open );
println! ( " End: {}" , window . window_close );
println! ( " Duration: {:.2} minutes" , duration_min );
// Access computed properties
let elev_max = window . properties . elevation_max ;
println! ( " Max elevation: {:.1}°" , elev_max );
}
Ok (())
}
// Output:
// Found 35 access windows
// Window 1:
// Start: 2025-11-02 05:39:28.345 UTC
// End: 2025-11-02 05:44:00.000 UTC
// Duration: 4.53 minutes
// Max elevation: 18.7°
// Window 2:
// Start: 2025-11-02 07:15:16.033 UTC
// End: 2025-11-02 07:21:00.000 UTC
// Duration: 5.73 minutes
// Max elevation: 38.9°
// Window 3:
// Start: 2025-11-02 08:54:59.619 UTC
// End: 2025-11-02 08:56:00.000 UTC
// Duration: 1.01 minutes
// Max elevation: 10.9°
Multiple Locations and Satellites Compute access for multiple locations and satellites simultaneously:
Python Rust
import brahe as bh
from collections import defaultdict
bh . initialize_eop ()
# Define multiple ground stations
locations = [
bh . PointLocation ( - 122.4194 , 37.7749 , 0.0 ) . with_name ( "San Francisco" ),
bh . PointLocation ( - 71.0589 , 42.3601 , 0.0 ) . with_name ( "Boston" ),
bh . PointLocation ( 15.4038 , 78.2232 , 458.0 ) . with_name ( "Svalbard" ),
]
# Define multiple satellites (from TLEs, epoch: 2024-01-01)
tle_data = [
# ISS - LEO, 51.6° inclination
(
"1 25544U 98067A 25306.42331346 .00010070 00000-0 18610-3 0 9999" ,
"2 25544 51.6344 342.0717 0004969 8.9436 351.1640 15.49700017536601" ,
"ISS" ,
),
# Tiangong - LEO, 41.5° inclination
(
"1 48274U 21035A 25306.17586037 .00031797 00000-0 38131-3 0 9995" ,
"2 48274 41.4666 263.0710 0006682 308.7013 51.3228 15.60215133257694" ,
"Tiangong" ,
),
]
propagators = [
bh . SGPPropagator . from_tle ( line1 , line2 , 60.0 ) . with_name ( name )
for line1 , line2 , name in tle_data
]
# Compute all location-satellite pairs (24 hours from TLE epoch)
epoch_start = bh . Epoch ( 2024 , 1 , 1 , 12 , 0 , 0.0 , 0.0 )
epoch_end = epoch_start + 86400.0 # 24 hours
constraint = bh . ElevationConstraint ( min_elevation_deg = 10.0 )
windows = bh . location_accesses (
locations , propagators , epoch_start , epoch_end , constraint
)
# Results include windows for all location-satellite combinations
print ( f "Total windows: { len ( windows ) } " )
# Group by location
by_location = defaultdict ( list )
for window in windows :
by_location [ window . location_name ] . append ( window )
for loc_name , loc_windows in by_location . items ():
print ( f " \n { loc_name } : { len ( loc_windows ) } windows" )
# Output:
# Total windows: 20
# Boston: 10 windows
# San Francisco: 10 windows
use brahe as bh ;
use bh :: utils :: Identifiable ;
use std :: collections :: HashMap ;
fn main () -> Result < (), Box < dyn std :: error :: Error >> {
bh :: initialize_eop () ? ;
// Define multiple locations
let locations = vec! [
bh :: PointLocation :: new (
- 122.4194 ,
37.7749 ,
0.0 ,
)
. with_name ( "San Francisco" ),
bh :: PointLocation :: new ( - 71.0589 , 42.3601 , 0.0 )
. with_name ( "Boston" ),
bh :: PointLocation :: new ( 15.4038 , 78.2232 , 458.0 )
. with_name ( "Svalbard" ),
];
// Define multiple satellites
let propagators = vec! [
bh :: SGPPropagator :: from_tle (
"1 25544U 98067A 25306.42331346 .00010070 00000-0 18610-3 0 9999" ,
"2 25544 51.6344 342.0717 0004969 8.9436 351.1640 15.49700017536601" ,
60.0 ,
) ?
. with_name ( "ISS" ),
bh :: SGPPropagator :: from_tle (
"1 48274U 21035A 25306.17586037 .00031797 00000-0 38131-3 0 9995" ,
"2 48274 41.4666 263.0710 0006682 308.7013 51.3228 15.60215133257694" ,
60.0 ,
) ?
. with_name ( "Tiangong" ),
];
// Compute access windows
let epoch_start = bh :: Epoch :: from_datetime ( 2025 , 11 , 2 , 2 , 0 , 0.0 , 0.0 , bh :: TimeSystem :: UTC );
let epoch_end = epoch_start + 86400.0 ;
let constraint = bh :: ElevationConstraint :: new ( Some ( 10.0 ), None ) ? ;
let windows = bh :: location_accesses (
& locations ,
& propagators ,
epoch_start ,
epoch_end ,
& constraint ,
None ,
None ,
None ,
) ? ;
println! ( "Total windows: {}" , windows . len ());
// Group by location
let mut by_location : HashMap < String , Vec <& bh :: AccessWindow >> = HashMap :: new ();
for window in & windows {
let loc_name = window . location_name . clone (). unwrap_or_else ( || "Unknown" . to_string ());
by_location
. entry ( loc_name )
. or_insert_with ( Vec :: new )
. push ( window );
}
for ( loc_name , loc_windows ) in by_location {
println! ( " \n {}: {} windows" , loc_name , loc_windows . len ());
}
Ok (())
}
// Expected output:
// Total windows: 20
// Boston: 10 windows
// San Francisco: 10 windows
Algorithm Explanation Brahe uses a two-step search algorithm to balance accuracy and performance:
Phase 1: Coarse Search The algorithm evaluates the constraint at regular time intervals (initial_time_step) across the entire search period. When the constraint transitions from false to true, a candidate access window has been found. This phase identifies periods of potential access quickly.
Optionally, adaptive stepping can be enabled to speed up the search by increasing by increasing the first step after an access window is found. The step size is based on a fraction of the satellite's orbital period (adaptive_fraction). For LEO satellites, this can significantly reduce the number of evaluations needed, as at most one access window occurs per orbit.
Example: With a 60-second time step over 24 hours, the algorithm performs ~1,440 constraint evaluations to identify candidate windows.
Phase 2: Refinement For each candidate window, the algorithm uses binary search to precisely locate the boundary times:
Start at the coarse boundary estimate Take steps backward/forward at half the previous step size until the constraint changes Evaluate constraint at each step When constraint changes, reduce step size, change direction, and repeat Continue until boundary is located to desired precision Configuration The AccessSearchConfig struct controls algorithm behavior:
Python Rust
import brahe as bh
bh . initialize_eop ()
# Create custom configuration
config = bh . AccessSearchConfig (
initial_time_step = 60.0 , # Coarse search: 60-second steps
adaptive_step = True , # Enable adaptive refinement
adaptive_fraction = 0.75 , # Each step is 75% of orbital period
parallel = True , # Enable parallel processing
num_threads = 0 , # Auto-detect thread count
)
# Use custom config with location and propagator
location = bh . PointLocation ( - 122.4194 , 37.7749 , 0.0 ) . with_name ( "San Francisco" )
tle_line1 = "1 25544U 98067A 25306.42331346 .00010070 00000-0 18610-3 0 9999"
tle_line2 = "2 25544 51.6344 342.0717 0004969 8.9436 351.1640 15.49700017536601"
propagator = bh . SGPPropagator . from_tle ( tle_line1 , tle_line2 , 60.0 ) . with_name ( "ISS" )
epoch_start = bh . Epoch ( 2025 , 11 , 2 , 0 , 0 , 0.0 , 0.0 )
epoch_end = epoch_start + 86400.0 # 24 hours
constraint = bh . ElevationConstraint ( min_elevation_deg = 10.0 )
windows = bh . location_accesses (
location , propagator , epoch_start , epoch_end , constraint , config = config
)
print ( f "Found { len ( windows ) } access windows with custom configuration" )
print (
f "Configuration: { config . initial_time_step } s time step, adaptive= { config . adaptive_step } "
)
# Expected output:
# Found 5 access windows with custom configuration
# Configuration: 60s time step, adaptive=True
use brahe as bh ;
use bh :: utils :: Identifiable ;
fn main () -> Result < (), Box < dyn std :: error :: Error >> {
bh :: initialize_eop () ? ;
// Create custom configuration
let config = bh :: AccessSearchConfig {
initial_time_step : 60.0 ,
adaptive_step : true ,
adaptive_fraction : 0.75 ,
parallel : true ,
num_threads : Some ( 0 ),
};
// Use custom config with location and propagator
let location = bh :: PointLocation :: new (
- 122.4194 ,
37.7749 ,
0.0 ,
)
. with_name ( "San Francisco" );
let tle_line1 = "1 25544U 98067A 25306.42331346 .00010070 00000-0 18610-3 0 9999" ;
let tle_line2 = "2 25544 51.6344 342.0717 0004969 8.9436 351.1640 15.49700017536601" ;
let propagator = bh :: SGPPropagator :: from_tle ( tle_line1 , tle_line2 , 60.0 ) ?
. with_name ( "ISS" );
let epoch_start = bh :: Epoch :: from_datetime ( 2025 , 11 , 2 , 0 , 0 , 0.0 , 0.0 , bh :: TimeSystem :: UTC );
let epoch_end = epoch_start + 86400.0 ;
let constraint = bh :: ElevationConstraint :: new ( Some ( 10.0 ), None ) ? ;
let windows = bh :: location_accesses (
& location ,
& propagator ,
epoch_start ,
epoch_end ,
& constraint ,
None ,
Some ( & config ),
None ,
) ? ;
println! (
"Found {} access windows with custom configuration" ,
windows . len ()
);
println! (
"Configuration: {}s time step, adaptive={}" ,
config . initial_time_step , config . adaptive_step
);
Ok (())
}
// Output:
// Found 5 access windows with custom configuration
// Configuration: 60s time step, adaptive=true
Parameter Guidance initial_time_step - Coarse search step size (seconds)
Smaller values (10-60s): More accurate, slower, for complex constraints or short windows Larger values (60-180s): Faster, risk missing brief access windows Rule of thumb : Use 1/10th of expected minimum window duration adaptive_step - Enable adaptive stepping to speed up coarse search
true : Enabled, faster for LEO satellites with regular orbits false : Disabled, standard fixed-step search adaptive_fraction - Fraction of orbital period for adaptive step size
Smaller values (0.3-0.6): Smaller adaptive step, less risk of missing windows Larger values (0.6-0.8): Larger adaptive step, faster but riskier Recommended : 0.5-0.75 for LEO satellites parallel - Enable parallel processing
true : Process location-satellite pairs in parallel (recommended) false : Sequential processing, lower memory usage num_threads - Thread pool size
0 : Auto-detect CPU cores (recommended) N > 0 : Use exactly N threads for parallel work See Also