Using TLEEphemeris

This example shows how to propagate a satellite from a Two-Line Element (TLE) set and obtain positions in different frames.

import datetime as dt
import numpy as np
import rust_ephem as re

# Example TLE (ISS, may be outdated)
tle1 = "1 25544U 98067A   20344.91777778  .00002182  00000-0  46906-4 0  9991"
tle2 = "2 25544  51.6460  44.6055 0002398  79.4451  23.5248 15.49364984256518"

# Define time range
begin = dt.datetime(2024, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc)
end = dt.datetime(2024, 1, 1, 1, 0, 0, tzinfo=dt.timezone.utc)
step_size = 60  # seconds

# Create ephemeris from TLE - several methods available:

# Method 1: Direct TLE strings (legacy)
sat = re.TLEEphemeris(tle1, tle2, begin, end, step_size, polar_motion=False)

# Method 2: From file path
# sat = re.TLEEphemeris(tle="path/to/tle_file.txt", begin=begin, end=end, step_size=step_size)

# Method 3: From URL (with caching)
# sat = re.TLEEphemeris(tle="https://celestrak.org/NORAD/elements/gp.php?CATNR=25544", begin=begin, end=end, step_size=step_size)

# Method 4: From NORAD ID (Celestrak, or Space-Track.org if credentials are set)
# If SPACETRACK_USERNAME and SPACETRACK_PASSWORD are set, Space-Track.org is
# tried first with automatic failover to Celestrak on failure.
# sat = re.TLEEphemeris(norad_id=25544, begin=begin, end=end, step_size=step_size)

# Method 5: From satellite name (fetches from Celestrak)
# sat = re.TLEEphemeris(norad_name="ISS (ZARYA)", begin=begin, end=end, step_size=step_size)

# Method 6: Explicit Space-Track.org credentials with norad_id
# sat = re.TLEEphemeris(
#     norad_id=25544,
#     spacetrack_username="your_username",
#     spacetrack_password="your_password",
#     begin=begin, end=end, step_size=step_size,
#     epoch_tolerance_days=4.0  # Optional: cache tolerance in days
# )

# All frames are pre-computed during initialization
# Access pre-computed frames (PositionVelocityData objects)
pv_teme = sat.teme_pv
pv_itrs = sat.itrs_pv
pv_gcrs = sat.gcrs_pv

# Access Sun and Moon positions/velocities
sun = sat.sun_pv
moon = sat.moon_pv

# Access timestamps
times = sat.timestamp

# Position (km) and velocity (km/s)
print("TEME position (km):", pv_teme.position[0])  # First timestep
print("TEME velocity (km/s):", pv_teme.velocity[0])
print("GCRS position norm (km):", np.linalg.norm(pv_gcrs.position[0]))

# Access astropy SkyCoord objects (requires astropy)
gcrs_skycoord = sat.gcrs
itrs_skycoord = sat.itrs

# Access obsgeoloc/obsgeovel for astropy GCRS frames
obsgeoloc = sat.obsgeoloc
obsgeovel = sat.obsgeovel

# Geodetic coordinates for the observer (derived from positions)
# These are Quantity arrays — index [0] gives the scalar at first timestep
print("Latitude (deg):", sat.latitude_deg[0])
print("Longitude (deg):", sat.longitude_deg[0])
print("Height (m):", sat.height_m[0])

TLEEphemeris Notes

  • Position magnitudes should be in LEO range (6500–8000 km); velocity around 7–8 km/s.

  • All coordinate frames are pre-computed during initialization for efficiency.

  • The polar_motion parameter enables polar motion corrections (requires EOP data).

  • TLE data can be provided in multiple ways: direct strings, file paths, URLs, NORAD IDs, satellite names, or Space-Track.org.

  • File and URL TLE sources are cached locally for improved performance on subsequent uses.

  • Space-Track.org fetches use epoch-aware caching: cached TLEs are reused if their epoch is within the configured tolerance (default: ±4 days) of the requested begin time.

  • See tests under tests/ for more examples and validation.

Space-Track.org Integration

When Space-Track.org credentials are available, the norad_id parameter will:

  1. Try fetching from Space-Track.org first (with epoch-based queries)

  2. Fall back to Celestrak automatically if Space-Track.org fails

Credentials can be provided via:

  1. Explicit parameters: spacetrack_username and spacetrack_password

  2. Environment variables: SPACETRACK_USERNAME and SPACETRACK_PASSWORD

  3. .env file in the current directory or home directory

If no credentials are found, norad_id uses Celestrak directly.

Space-Track.org provides historical TLE data with epoch-based queries. When you specify a begin time, the library fetches the TLE with an epoch closest to that time. This ensures the most accurate propagation for your time range.

The epoch_tolerance_days parameter controls caching behavior. If a cached TLE exists with an epoch within ±N days of the requested begin time, it will be used instead of making a new API request. Default tolerance is 4 days.

Note: Please follow Space-Track.org’s usage guidelines (max 1 TLE query per hour for automated scripts). TLEs are cached in ~/.cache/rust_ephem/spacetrack_cache/.

Using fetch_tle for TLE Management

For more control over TLE fetching and inspection, use the fetch_tle() function which returns a TLERecord object. This is useful when you need to:

  • Inspect TLE metadata before creating an ephemeris

  • Cache or store TLEs for later use

  • Access TLE fields like NORAD ID, epoch, or classification

import rust_ephem
from datetime import datetime, timezone

# Fetch TLE and inspect metadata
tle = rust_ephem.fetch_tle(norad_id=25544)

print(f"Satellite: {tle.name}")
print(f"NORAD ID: {tle.norad_id}")
print(f"TLE Epoch: {tle.epoch}")
print(f"Source: {tle.source}")
print(f"Classification: {tle.classification}")

# View the raw TLE lines
print(tle.to_tle_string())

# Pass TLERecord directly to TLEEphemeris
begin = datetime(2024, 1, 1, tzinfo=timezone.utc)
end = datetime(2024, 1, 2, tzinfo=timezone.utc)

sat = rust_ephem.TLEEphemeris(
    tle=tle,  # Pass TLERecord directly
    begin=begin,
    end=end,
    step_size=60
)

# Access the exact TLERecord used for propagation
used_tle = sat.tle_record
print(used_tle.source, used_tle.epoch)

Benefits of using fetch_tle:

  • Metadata access: Inspect TLE epoch, source, and satellite name before propagation

  • Serialization: TLERecord is a Pydantic model supporting JSON serialization via model_dump_json()

  • Validation: Verify the TLE was fetched from the expected source

  • Reuse: Fetch once, create multiple ephemeris objects with different time ranges

TLERecord Derived Fields

TLERecord exposes parsed and derived element fields populated when the model validates line1 and line2. These provide direct access to commonly used TLE values without manual column slicing.

Available helpers include:

  • mean_motion_dot_rev_per_day2

  • mean_motion_ddot_rev_per_day3

  • bstar_drag

  • ephemeris_type

  • element_set_number

  • revolution_number_at_epoch

  • inclination_deg

  • right_ascension_deg

  • eccentricity

  • arg_periapsis_deg

  • mean_anomaly_deg

  • mean_motion_rev_per_day

  • mean_motion_rad_s

  • true_anomaly_deg

  • semimajor_axis_m (derived using WGS72 Earth gravitational parameter)

tle = rust_ephem.fetch_tle(norad_id=25544)

print(tle.inclination_deg)
print(tle.mean_motion_rev_per_day)
print(tle.mean_motion_rad_s)
print(tle.semimajor_axis_m)

For convenience, classical_elements(mu_m3_s2=...) returns the same element set as a dictionary. When mu_m3_s2 is overridden, SemimajorAxis_m is recomputed using the supplied gravitational parameter.

# Serialize TLE for storage
json_str = tle.model_dump_json()

# Fetch from Space-Track with specific epoch
historical_tle = rust_ephem.fetch_tle(
    norad_id=25544,
    epoch=datetime(2020, 6, 15, tzinfo=timezone.utc),
    enforce_source="spacetrack"  # Don't fall back to Celestrak
)

See Python API for complete fetch_tle() and TLERecord documentation.