Skip to content

Groundstation Datasets

Overview

Groundstation datasets provide geographic locations and metadata for commercial satellite ground facilities worldwide. This data is essential for:

  • Computing contact opportunities: Determine when satellites are visible from ground stations
  • Network planning: Analyze coverage and redundancy across multiple providers
  • Mission design: Evaluate downlink opportunities for different orbit configurations

Brahe includes embedded GeoJSON data for 6 major commercial groundstation providers, totaling 50+ facilities globally. All data is:

  • Offline-capable: No network requests required
  • Comprehensive: Global coverage across multiple providers
  • Standardized: Consistent format with geographic coordinates and metadata
  • Up-to-date: Maintained as provider networks evolve

When to Use

Use groundstation datasets when you need to:

  • Compute visibility windows for satellite-to-ground contacts
  • Plan downlink schedules for data collection
  • Analyze network coverage and redundancy
  • Compare provider capabilities across different locations

Available Providers

Brahe includes groundstation data from six major commercial providers:

Provider Description
Atlas Atlas Space Operations
AWS Amazon Web Services Ground Station
KSAT Kongsberg Satellite Services
Leaf Leaf Space
NASA DSN NASA Deep Space Network
NASA NEN NASA Near Earth Network
SSC Swedish Space Corporation
Viasat Viasat

Usage

Loading Groundstations

Load groundstation data from one or more providers:

import brahe as bh

# Initialize EOP data
bh.initialize_eop()

# Load groundstations from a single provider
ksat_stations = bh.datasets.groundstations.load("ksat")
print(f"KSAT stations: {len(ksat_stations)}")

# Load all available providers at once
all_stations = bh.datasets.groundstations.load_all()
print(f"Total stations (all providers): {len(all_stations)}")

# List available providers
providers = bh.datasets.groundstations.list_providers()
print(f"\nAvailable providers: {', '.join(providers)}")

# Load multiple specific providers
aws_stations = bh.datasets.groundstations.load("aws")
ssc_stations = bh.datasets.groundstations.load("ssc")
combined = aws_stations + ssc_stations
print(f"\nCombined AWS + SSC: {len(combined)} stations")

# Expected output:
# KSAT stations: 36
# Total stations (all providers): 96

# Available providers: atlas, aws, ksat, leaf, ssc, viasat

# Combined AWS + SSC: 22 stations
use brahe as bh;

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

    // Load groundstations from a single provider
    let ksat_stations = bh::datasets::groundstations::load_groundstations("ksat").unwrap();
    println!("KSAT stations: {}", ksat_stations.len());

    // Load all available providers at once
    let all_stations = bh::datasets::groundstations::load_all_groundstations().unwrap();
    println!("Total stations (all providers): {}", all_stations.len());

    // List available providers
    let providers = bh::datasets::groundstations::list_providers();
    println!("\nAvailable providers: {}", providers.join(", "));

    // Load multiple specific providers
    let aws_stations = bh::datasets::groundstations::load_groundstations("aws").unwrap();
    let ssc_stations = bh::datasets::groundstations::load_groundstations("ssc").unwrap();
    let combined: Vec<_> = aws_stations
        .iter()
        .chain(ssc_stations.iter())
        .cloned()
        .collect();
    println!("\nCombined AWS + SSC: {} stations", combined.len());

    // Expected output:
    // KSAT stations: 36
    // Total stations (all providers): 96

    // Available providers: atlas, aws, ksat, leaf, ssc, viasat

    // Combined AWS + SSC: 22 stations
}

Accessing Properties

Each groundstation includes geographic coordinates and metadata:

import brahe as bh

# Initialize EOP data
bh.initialize_eop()

# Load KSAT groundstations
stations = bh.datasets.groundstations.load("ksat")

# Access the first station
station = stations[0]

# Geographic coordinates (degrees and meters)
name = station.get_name() if station.get_name() else "Unknown"
print(f"Station: {name}")
print(f"Latitude: {station.lat:.4f}°")
print(f"Longitude: {station.lon:.4f}°")
print(f"Altitude: {station.alt:.1f} m")

# Access metadata properties
props = station.properties
print(f"\nProvider: {props['provider']}")
print(f"Frequency bands: {', '.join(props['frequency_bands'])}")

# Show all stations with their locations
print(f"\n{len(stations)} KSAT Stations:")
for i, gs in enumerate(stations, 1):
    gs_name = gs.get_name() if gs.get_name() else "Unknown"
    print(f"{i:2d}. {gs_name:30s} ({gs.lat:7.3f}°, {gs.lon:8.3f}°)")

# Expected output:
# Station: Prudhoe Bay
# Latitude: 70.2000°
# Longitude: -148.4700°
# Altitude: 0.0 m

# Provider: KSAT
# Frequency bands: S, X

# 36 KSAT Stations:
#  1. Prudhoe Bay                    ( 70.200°, -148.470°)
#  2. Athens                         ( 37.850°,   22.620°)
#  3. Awarua                         (-46.530°,  168.380°)
use brahe as bh;
use bh::utils::Identifiable;

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

    // Load KSAT groundstations
    let stations = bh::datasets::groundstations::load_groundstations("ksat").unwrap();

    // Access the first station
    let station = &stations[0];

    // Geographic coordinates (degrees and meters)
    let name = station.get_name().unwrap_or("Unknown");
    println!("Station: {}", name);
    println!("Latitude: {:.4}°", station.lat());
    println!("Longitude: {:.4}°", station.lon());
    println!("Altitude: {:.1} m", station.alt());

    // Show all stations with their locations
    println!("\n{} KSAT Stations:", stations.len());
    for (i, gs) in stations.iter().enumerate() {
        let gs_name = gs.get_name().unwrap_or("Unknown");
        println!(
            "{:2}. {:30} ({:7.3}°, {:8.3}°)",
            i + 1,
            gs_name,
            gs.lat(),
            gs.lon()
        );
    }

    // Expected output:
    // Station: Prudhoe Bay
    // Latitude: 70.2000°
    // Longitude: -148.4700°
    // Altitude: 0.0 m

    // 36 KSAT Stations:
    //  1. Prudhoe Bay                    ( 70.200°, -148.470°)
    //  2. Athens                         ( 37.850°,   22.620°)
    //  3. Awarua                         (-46.530°,  168.380°)
}

Computing Access Windows

Use groundstation data with brahe's access computation to find contact opportunities:

import brahe as bh
import numpy as np

# Initialize EOP data
bh.initialize_eop()

# Load groundstations from a provider
stations = bh.datasets.groundstations.load("ksat")
print(f"Computing access for {len(stations)} KSAT stations")

# Create a sun-synchronous orbit satellite
epoch = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)
oe = np.array([bh.R_EARTH + 600e3, 0.001, 97.8, 0.0, 0.0, 0.0])
state = bh.state_koe_to_eci(oe, bh.AngleFormat.DEGREES)
propagator = bh.KeplerianPropagator.from_eci(epoch, state, 60.0).with_name("EO-Sat")

# Define access constraint (minimum 5° elevation)
constraint = bh.ElevationConstraint(min_elevation_deg=5.0)

# Compute access windows for 24 hours
duration = 24.0 * 3600.0  # seconds
windows = bh.location_accesses(
    stations, [propagator], epoch, epoch + duration, constraint
)

# Display results
print(f"\nTotal access windows: {len(windows)}")
print("\nFirst 5 windows:")
for i, window in enumerate(windows[:5], 1):
    duration_min = (window.end - window.start) / 60.0
    print(f"{i}. {window.location_name:20s} -> {window.satellite_name:10s}")
    print(f"   Start: {window.start}")
    print(f"   Duration: {duration_min:.1f} minutes")

# Expected output:
# Computing access for 36 KSAT stations

# Total access windows: 213

# First 5 windows:
# 1. Long Beach           -> EO-Sat
#    Start: 2024-01-01 00:05:08.313 UTC
#    Duration: 8.9 minutes
# 2. Thomaston            -> EO-Sat
#    Start: 2024-01-01 00:07:15.029 UTC
#    Duration: 1.7 minutes
# 3. Inuvik               -> EO-Sat
#    Start: 2024-01-01 00:13:53.159 UTC
#    Duration: 10.1 minutes
# 4. Fairbanks            -> EO-Sat
#    Start: 2024-01-01 00:14:39.836 UTC
#    Duration: 8.3 minutes
# 5. Prudhoe Bay          -> EO-Sat
#    Start: 2024-01-01 00:15:18.853 UTC
#    Duration: 9.7 minutes
use brahe as bh;
use bh::access::location_accesses;
use bh::utils::Identifiable;
use nalgebra as na;

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

    // Load groundstations from a provider
    let stations = bh::datasets::groundstations::load_groundstations("ksat").unwrap();
    println!("Computing access for {} KSAT stations", stations.len());

    // Create a sun-synchronous orbit satellite
    let epoch = bh::Epoch::from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh::TimeSystem::UTC);
    let oe = na::SVector::<f64, 6>::new(
        bh::R_EARTH + 600e3,
        0.001,
        97.8_f64.to_radians(),
        0.0,
        0.0,
        0.0,
    );
    let state = bh::state_koe_to_eci(oe, bh::AngleFormat::Radians);
    let propagator =
        bh::KeplerianPropagator::from_eci(epoch, state, 60.0).with_name("EO-Sat");

    // Define access constraint (minimum 5° elevation)
    let constraint = bh::ElevationConstraint::new(Some(5.0), None).unwrap();

    // Compute access windows for 24 hours
    let duration = 24.0 * 3600.0; // seconds
    let windows = location_accesses(
        &stations,
        &vec![propagator],
        epoch,
        epoch + duration,
        &constraint,
        None,
        None,
        None,
    ).unwrap();

    // Display results
    println!("\nTotal access windows: {}", windows.len());
    println!("\nFirst 5 windows:");
    for (i, window) in windows.iter().take(5).enumerate() {
        let duration_min = (window.end() - window.start()) / 60.0;
        let loc_name = window.location_name.as_deref().unwrap_or("Unknown");
        let sat_name = window.satellite_name.as_deref().unwrap_or("Unknown");
        println!(
            "{}. {:20} -> {:10}",
            i + 1,
            loc_name,
            sat_name
        );
        println!("   Start: {}", window.start());
        println!("   Duration: {:.1} minutes", duration_min);
    }

    // Expected output:
    // Computing access for 36 KSAT stations

    // Total access windows: 213

    // First 5 windows:
    // 1. Long Beach           -> EO-Sat    
    //    Start: 2024-01-01 00:05:08.313 UTC
    //    Duration: 8.9 minutes
    // 2. Thomaston            -> EO-Sat    
    //    Start: 2024-01-01 00:07:15.029 UTC
    //    Duration: 1.7 minutes
    // 3. Inuvik               -> EO-Sat    
    //    Start: 2024-01-01 00:13:53.159 UTC
    //    Duration: 10.1 minutes
    // 4. Fairbanks            -> EO-Sat    
    //    Start: 2024-01-01 00:14:39.836 UTC
    //    Duration: 8.3 minutes
    // 5. Prudhoe Bay          -> EO-Sat    
    //    Start: 2024-01-01 00:15:18.853 UTC
    //    Duration: 9.7 minutes
}

Data Format

Each groundstation is represented as a PointLocation with standardized properties:

import brahe as bh

stations = bh.datasets.groundstations.load("ksat")
station = stations[0]

# Geographic coordinates (WGS84)
lon = station.lon()      # Longitude in degrees
lat = station.lat()      # Latitude in degrees
alt = station.alt()      # Altitude in meters

# Metadata properties
props = station.properties
name = station.get_name()              # Station name
provider = props["provider"]            # Provider name (e.g., "KSAT")
bands = props["frequency_bands"]        # Supported bands (e.g., ["S", "X"])

All groundstations include these standard properties:

  • provider: Provider name (string, e.g., "KSAT", "Atlas")
  • frequency_bands: List of supported frequency bands (e.g., ["S", "X", "Ka"])

Additional properties may be included in future releases as data becomes available.


See Also