satellite.js

repository·develop·Indexed 21 days ago

https://github.com/shashwatak/satellite-js

A SGP4/SDP4 calculation library for satellite propagation using TLEs or OMM data. It provides coordinate transformations (ECI, ECF, Geodetic), Doppler factor and shadow calculations, and look angles. Version 7.1.0 includes a high-performance WebAssembly (WASM) implementation featuring SIMD acceleration, multi-threading via pthreads, and a BulkPropagator for high-throughput calculations with minimal memory overhead.

Tokens
26.3K
Snippets
74
Records
113
Agent score
74%

What's inside satellite.js

  1. High-performance SGP4 with WebAssembly (WASM)

    develop

    Satellite.js provides a WebAssembly (WASM) implementation of SGP4/SDP4 designed for high-performance, real-time applications. Unlike the pure JavaScript implementation which relies on object allocation and garbage collection, the WASM version uses a C++ backend optimized for minimal overhead.

    Key performance characteristics include:

    • Zero-copy data access: Results are returned as TypedArray views (e.g., Float64Array) directly on the WASM linear memory, avoiding expensive serialization/marshalling.
    • Minimal allocation: Uses a BulkPropagator pattern that allocates memory once and reuses it across multiple runs.
    • SIMD acceleration: Compiled with 128-bit SIMD instructions to allow parallel processing of mathematical operations.
    • Single boundary crossing: Entire calculation pipelines (from propagation to look angles) can be executed in a single call to the WASM module, preventing costly JS-WASM context switching.
    const now = new Date();
    const results = entireSatelliteDatabase
      .map(satRec => propagate(satRec, now));
  2. What is the Bulk Propagation API and when to use it

    develop

    The Bulk Propagation API is a high-performance alternative to pure JS functions, implemented via C++ compiled to WASM. It is optimized for processing large batches of satellite-time pairs.

    Use Cases

    Best for:

    • Real-time sky simulations (calculating positions for tens of thousands of satellites).
    • Trajectory/ephemeris calculations (hundreds of time points for multiple satellites).

    Avoid for:

    • Single satellite/date calculations (use pure JS functions instead).
    • Calculating extended properties like singly averaged mean elements.
  3. How the WASM bulk propagation layer works

    develop

    The WASM layer provides high-performance bulk propagation by running SGP4 in compiled C++. It consists of two parts:

    1. src-cpp/: The C++ source files.
    2. src/wasm/: The TypeScript orchestration layer. This includes the BulkPropagator class for propagating many satellites at many dates simultaneously, pluggable calculators (ECI, ECF, geodetic, etc.), and runtimes (single-thread and multi-thread).

    WASM Compilation Variants:

    • base-debug: common.cpp + base.cpp + debug.cpp. Used for debug tests with AddressSanitizer and LeakSanitizer.
    • base-release: common.cpp + base.cpp. Production single-thread.
    • pthreads-debug: common.cpp + pthreads.cpp + debug.cpp. Multi-threaded debug.
    • pthreads-release: common.cpp + pthreads.cpp. Production multi-thread.
  4. Choose a Runtime: SingleThreadRuntime vs MultiThreadRuntime

    develop

    A Runtime manages the WASM instance, its creation, and disposal. You must choose one based on your environment and performance needs.

    • SingleThreadRuntime:

      • Synchronous execution (blocks the thread).
      • Best for: Workers, small batches, console apps, or environments without SharedArrayBuffer support.
      • Multiple BulkPropagator instances can exist, but only one calculation runs at a time because it is blocking.
    • MultiThreadRuntime:

      • Asynchronous execution (uses separate threads).
      • Best for: High-performance bulk tasks where you don't want to block the main thread.
      • Requirement: Requires SharedArrayBuffer support (browsers must be cross-origin isolated and in a secure context).
      • Constraint: You can only run one BulkPropagator instance at a time on a single MultiThreadRuntime. Running more will throw an error.
    import { createSingleThreadRuntime } from 'satellite.js';
    using runtime = await createSingleThreadRuntime();
    import { createMultiThreadRuntime } from 'satellite.js';
    using runtime = await createMultiThreadRuntime({ threadsCount: 4 });
  5. Understand the SatRec object and its properties

    develop

    A SatRec object contains the Keplerian elements and other values extracted from TLE or OMM, along with calculated values required for the SGP4 algorithm.

    For most use cases, you do not need to interact with the internal complexity of the object; you simply pass the SatRec instance to propagation functions. However, you should monitor the error property to detect propagation failures.

  6. How BulkPropagator calculators work

    develop

    Calculators are classes used with the Bulk Propagation API to compute specific satellite outputs in WASM.

    Key Concepts:

    • Efficiency: You should only include the calculators you need in the calculators array passed to BulkPropagator. Fewer calculators result in faster execution.
    • Dependencies: Calculators often depend on others. If a calculator requires a dependency, both must be included in the calculators array.
    • Data Layout: Outputs are packed in TypedArray buffers. Indices are sorted satellite-first, date-second: index = satelliteIndex * datesCount + dateIndex.
    • Data Frequency: While most calculators produce one value per satellite/date pair, some (like GmstCalculator and SunPositionCalculator) produce one value per date only, meaning the data is not duplicated per satellite.
  7. Use BulkPropagator for high-throughput satellite calculations

    develop

    For processing large datasets (e.g., thousands of satellites over multiple timestamps), use the BulkPropagator. This class is designed to minimize memory churn by allocating satellite structs, date arrays, and a single contiguous output buffer once, then reusing them for every run() call.

    BulkPropagator implements the Disposable interface, so it is recommended to use the using syntax to ensure memory is properly cleaned up when finished.

  8. Understand the project structure

    develop

    The repository is organized into TypeScript source, C++ WASM source, and tests:

    • src/: TypeScript source code. Main API is exported from src/index.ts.
    • src-cpp/: C++ source files compiled to WASM via Emscripten.
    • src/wasm/: TypeScript orchestration layer wrapping WASM modules (e.g., BulkPropagator).
    • test/: Vitest tests, including test/propagation/ for SGP4 catalog verification and test/wasm/ for WASM tests.
    • dist/: Generated compiled JS output.
    • wasm-build/: Generated WASM modules (variants: base-debug, base-release, pthreads-debug, pthreads-release).
    • docs/: Docusaurus documentation site.
  9. Perform coordinate transformations and calculate look angles

    develop

    Satellite.js provides tools to transform ECI coordinates into other frames like ECF (Earth-Centered Fixed) or Geodetic (latitude, longitude, height).

    To calculate Look Angles (azimuth, elevation, range) for an observer, you must:

    1. Define the observer's geodetic position in radians.
    2. Calculate the GMST (Greenwich Mean Sidereal Time) using gstime(date).
    3. Convert the satellite's ECI position to ECF.
    4. Convert the observer's geodetic position to ECF.
    5. Use ecfToLookAngles(observerGd, positionEcf).

    Commonly used functions:

    • gstime(date): Get GMST.
    • eciToEcf(positionEci, gmst): ECI to ECF.
    • geodeticToEcf(observerGd): Geodetic to ECF.
    • eciToGeodetic(positionEci, gmst): ECI to Geodetic.
    • ecfToLookAngles(observerGd, positionEcf): ECF to Look Angles.
    • dopplerFactor(observerEcf, positionEcf, velocityEcf): Calculate Doppler factor.
    import * as satellite from 'satellite.js';
    
    // 1. Setup Observer (in RADIANS)
    const observerGd = {
      longitude: satellite.degreesToRadians(-122.0308),
      latitude: satellite.degreesToRadians(36.9613422),
      height: 0.370
    };
    
    // 2. Get GMST
    const gmst = satellite.gstime(new Date());
    
    // 3. Transform and Calculate
    const positionEcf = satellite.eciToEcf(positionEci, gmst);
    const observerEcf = satellite.geodeticToEcf(observerGd);
    const positionGd = satellite.eciToGeodetic(positionEci, gmst);
    const lookAngles = satellite.ecfToLookAngles(observerGd, positionEcf);
    
    // 4. Access Results
    console.log(`Azimuth: ${lookAngles.azimuth}, Elevation: ${lookAngles.elevation}, Range: ${lookAngles.rangeSat}`);
    console.log(`Lat: ${satellite.degreesLat(positionGd.latitude)}, Lon: ${satellite.degreesLong(positionGd.longitude)}`);
  10. Understand the C++ compilation variants

    develop

    The C++ source code in this repository is compiled into different WASM modules depending on the required performance and threading capabilities. The core logic is split between common.cpp (shared), base.cpp (single-threaded), and pthreads.cpp (multi-threaded).

    Available compilation types:

    • base: Single-threaded, SIMD-enabled.
    • pthreads: Multi-threaded, SIMD-enabled.

    Each type has a debug and a release version. Debug builds include debug.cpp (used for testing), while release builds exclude it for performance.

    Compilation file mappings:

    • base-debug: common.cpp + base.cpp + debug.cpp
    • pthreads-debug: common.cpp + pthreads.cpp + debug.cpp
    • base-release: common.cpp + base.cpp
    • pthreads-release: common.cpp + pthreads.cpp

    Note: The code is specifically written for Emscripten 6.0.3. Using different versions may cause breaking changes.

  11. Understand coordinate transform units in satellite.js

    develop

    When performing coordinate transforms, the library uses the following standard units:

    • Positions, ranges, and heights: kilometers (km)
    • Velocities: kilometers per second (km/s)
    • Angles: radians

    Note that types like Kilometer, AU, KilometerPerSecond, Radians, EciVec3, and EcfVec3 are provided as synonyms for number or { x: number, y: number, z: number }. They are not structurally distinct types; they are used primarily for documentation and to remind you of the expected units.

  12. Dispose BulkPropagator and Runtime to prevent memory leaks

    develop

    The Bulk Propagation API uses unmanaged memory. You must manually dispose of BulkPropagator and Runtime instances to prevent memory leaks.

    If your environment supports the Explicit Resource Management proposal (e.g., modern Node.js), use the using syntax to ensure automatic cleanup when the variable goes out of scope.

    Manual Disposal

    If you cannot use using, you must explicitly call the .dispose() method on both the BulkPropagator and the Runtime instances.

    // Recommended approach
    using runtime = await createSingleThreadRuntime();
    using bulkPropagator = new BulkPropagator(options);
    // Automatically disposed at end of scope
    
    // Manual approach
    const runtime = await createSingleThreadRuntime();
    const bulkPropagator = new BulkPropagator(options);
    
    // ... use them ...
    
    bulkPropagator.dispose();
    runtime.dispose();