Skip to main content

Why tl3?

tl3 provides two things: the ability to automatically and efficiently download every two-line element (TLE) ever published by Space-Track (while staying within the API-imposed rate limit), and piping the resulting .txt files into a parquet file for efficient analysis using packages like duckdb or polars.

Installation

pip install tl3

The package should work wherever Polars and DuckDB (its primary dependencies) work.

Quickstart

To pull all TLEs from 1958 to the end of the previous UTC day, run:

import tl3

date_pairs = tl3.load_query_dates()
# Loads nicely-distributed dates to make each api query roughly the same size (20 MB)
tl3.save_tles(date_pairs)
# Makes queries to the Space-Track API, this takes about 5 hours for all dates
tl3.update_tle_cache()
# Pulls any dates after the above query dates were generated
tl3.build_parquet(from_scratch=True)
# Concatenates all TLE txt files into one parquet for efficient querying

This will download (while remaining within the rate limits) ~28 GB of raw TLE .txt files, and build a single parquet file out of the results.

Be considerate to Space-Track when using this package. tl3 automatically stays below the rate limit imposed by Space-Track, but do not repeatedly query all TLEs multiple times. The developer of tl3 is not responsible for any consequences resulting from its use.

The first time you import tl3, you will be prompted for your Space-Track login credentials, which are cached locally for all requests.

Querying The Database

Once the parquet file is built, you can query TLEs between two dates for a single NORAD ID or the full catalog:

import datetime
tles = tl3.tles_between(datetime.datetime(2024, 1, 1), datetime.datetime(2024, 1, 2), norad_cat_id='all', return_as='tle')
[['1 44928U 20001Q   24000.00001156 -.00049610  00000-0 -48650-3 0  9997'
'2 44928 053.0540 322.4610 0001433 089.1120 141.4850 15.65344620   574']
['1 45705U 20035BA  24000.00001156  .00214062  00000-0  83880-3 0  9991'
'2 45705 053.0460 027.5330 0008402 050.5410 020.3000 15.85815525   605']
['1 46031U 20055E   24000.00001156 -.00024118  00000-0 -16770-2 0  9998'
'2 46031 053.0530 116.6100 0001254 079.9010 008.0290 15.04774666   583']
...
['1 20962U 75100F   24000.99892966  .00000079  00000-0  00000-0 0  9996'
'2 20962 004.5720 273.5430 0280693 054.9400 307.9080 01.01930308 12691']
['1 20962U 75100F   24000.99892968  .00000079  00000-0  00000-0 0  9996'
'2 20962 004.5720 273.5430 0280682 054.9380 307.9090 01.01930308 12692']
['1 20962U 75100F   24000.99892985  .00000079  00000-0  00000-0 0  9996'
'2 20962 004.5720 273.5430 0280690 054.9390 307.9080 01.01930308 12694']]

You can query TLEs by COSPAR ID or NORAD ID:

import datetime
import numpy as np

date_start, date_end = datetime.datetime(2024, 1, 1), datetime.datetime(2024, 1, 2)
df_cospar = tl3.tles_between(date_start, date_end, identifier='2020-035BA')
df_norad = tl3.tles_between(date_start, date_end, identifier=45705)

print(np.all(df_cospar.to_numpy() == df_norad.to_numpy()))
True

If your use-case is more complex, you can run arbitrary queries directly against the full dataset using duckdb. For example, you can query the NORAD catalog IDs for all polar satellites in LEO with at least one TLE produced in 2024 with:

import tl3
import duckdb

df = duckdb.sql(f"""
   SELECT DISTINCT NORAD_CAT_ID FROM {repr(tl3.DB_PATH)}
   WHERE EPOCH BETWEEN '2024-01-01' AND '2025-01-01'
   AND ABS(INC - 90) < 0.1
   AND N < 10
""").pl()

Which returns a Polars dataframe:

┌──────────────┐
│ NORAD_CAT_ID │
│ ---          │
│ u32          │
╞══════════════╡
│ 2876         │
│ 54153        │
│ 54154        │
│ 2877         │
│ 2861         │
└──────────────┘

Or we could get the inclination and eccentricity history of the ISS:

df = duckdb.sql(f"""
    SELECT EPOCH, INC, ECC FROM {repr(tl3.DB_PATH)}
    WHERE NORAD_CAT_ID == 25544
""").pl()
shape: (48_981, 3)
┌─────────────────────────┬───────────┬──────────┐
│ EPOCH                   ┆ INC       ┆ ECC      │
│ ---                     ┆ ---       ┆ ---      │
│ datetime[μs]            ┆ f32       ┆ f32      │
╞═════════════════════════╪═══════════╪══════════╡
│ 1998-11-21 06:49:59.999 ┆ 51.59     ┆ 0.012536 │
│ 1998-11-21 07:58:35.072 ┆ 51.617001 ┆ 0.012341 │
│ 1998-11-21 10:57:42.787 ┆ 51.591    ┆ 0.012586 │
│ 1998-11-21 12:27:32.846 ┆ 51.595001 ┆ 0.012386 │
│ 1998-11-21 13:57:13.741 ┆ 51.595001 ┆ 0.012396 │
│ …                       ┆ …         ┆ …        │
│ 2024-07-16 10:39:50.426 ┆ 51.637001 ┆ 0.00103  │
│ 2024-07-16 11:17:07.495 ┆ 51.638    ┆ 0.00102  │
│ 2024-07-16 17:37:27.269 ┆ 51.638    ┆ 0.001024 │
│ 2024-07-16 19:56:56.165 ┆ 51.636002 ┆ 0.001031 │
│ 2024-07-16 20:17:12.377 ┆ 51.638    ┆ 0.001063 │
└─────────────────────────┴───────────┴──────────┘

For reference, the .parquet file contains the following columns:

┌──────────────┬─────────────────────────────────┐
│ Column       ┆ Type                            │
│ ---          ┆ ---                             │
│ str          ┆ object                          │
╞══════════════╪═════════════════════════════════╡
│ NORAD_CAT_ID ┆ UInt32                          │
│ INTL_DES     ┆ String                          │
│ N_DOT        ┆ Float32                         │
│ N_DDOT       ┆ Float32                         │
│ B_STAR       ┆ Float32                         │
│ ELSET_NUM    ┆ UInt16                          │
│ CHECKSUM1    ┆ UInt8                           │
│ INC          ┆ Float32                         │
│ RAAN         ┆ Float32                         │
│ ECC          ┆ Float32                         │
│ AOP          ┆ Float32                         │
│ MA           ┆ Float32                         │
│ N            ┆ Float32                         │
│ REV_NUM      ┆ UInt16                          │
│ CHECKSUM2    ┆ UInt8                           │
│ COSPAR_ID    ┆ String                          │
│ EPOCH        ┆ Datetime(time_unit='us', time_… │
└──────────────┴─────────────────────────────────┘

Notice that many floats have been compressed to float32 to save storage space.

Metadata

Release files for tl3 0.0.13

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tl3 0.0.13
File Size Uploaded Provenance Actions
tl3-0.0.13.tar.gz 18.8 kB Without provenance Details for tl3-0.0.13.tar.gz

Release files / tl3-0.0.13.tar.gz

Download URL tl3-0.0.13.tar.gz
Size 18.8 kB
Tags Source
SHA-256 checksum
How to use checksums
779ca9fa7aed238600b4f6aab1d314b5a2698bb0b760fbb90f54b78a0a1ca98a
BLAKE2b-256 checksum
How to use checksums
2c4da9b486e1a5fc0d179ab0c45619f3a4ad8c728bfef794710479d83dd535a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.0.1 CPython/3.11.11

Security for tl3 0.0.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page