Week 8 · Orbital Analyst~7 min · 623 words

SGP4 propagation in Python with skyfield

skyfield is Python's premier orbit-propagation library. SGP4 is the workhorse algorithm. This week you'll propagate the ISS, generate ground tracks, and produce a publishable GeoJSON.

If a satellite is overhead at exactly 7:42 PM tonight, where will it be at 7:43? At 8:42?

SGP4 is how every satellite-tracking app on your phone answers that question. This week you'll write it yourself in 10 lines of Python. By the end, you can compute the ISS's ground track 24 hours into the future.

Learning objectives

From Honolulu, the ISS ground track crosses overhead at roughly 51.6° from the equator. Run the Week 8 lab and you'll generate the next 24h of ISS positions as a polyline on this map.

Try it: propagate the ISS forward in time

Stylized SGP4 — pick a time offset in hours from now; the tool tells you the ISS's approximate sub-satellite point.

Primer

A TLE describes where the orbit is; an SGP4 propagator computes where the satellite is at any given time. This week we connect the two using Python — the same code path that powers every satellite tracking app on the internet.

What SGP4 does

Simplified General Perturbations 4 is the orbital propagation model standardized by NORAD for use with TLEs. It accounts for the first-order perturbations of low-Earth orbit: Earth's J2 oblateness, atmospheric drag, solar/lunar gravity, and solar radiation pressure. It's accurate to within ~1 km for a fresh TLE and degrades over time as the underlying model assumptions diverge from reality.

SGP4 is not the most accurate propagator available — for centimeter-precision (GPS satellites, geodetic missions) you'd use a numerical propagator with high-fidelity models — but it's accurate enough for almost every satellite-tracking and ground-station application, and it's standardized across the industry.

skyfield: Python's reference implementation

skyfield is a high-precision astronomy + satellite-propagation library that wraps the canonical SGP4 implementation and adds proper time-system handling (UT1 vs UTC vs TT vs TDB), Earth orientation, and a clean Python API. It is the right default choice; the older sgp4 standalone package is lower-level and lacks the time-system polish.

from skyfield.api import EarthSatellite, load, wgs84

tle_lines = [
    "1 25544U 98067A   24130.50145833  .00018539  00000-0  33188-3 0  9994",
    "2 25544  51.6406 348.5395 0006703 117.9568 358.1729 15.50289267449420",
]
ts = load.timescale()
iss = EarthSatellite(tle_lines[0], tle_lines[1], "ISS", ts)
t = ts.now()
geocentric = iss.at(t)
subpoint = wgs84.subpoint(geocentric)
print(f"ISS sub-satellite point: ({subpoint.latitude.degrees:.4f}, {subpoint.longitude.degrees:.4f}), alt {subpoint.elevation.km:.1f} km")

Time matters

Satellite propagation is exquisitely time-sensitive. A 1-second error means a 7 km position error for an ISS-like LEO orbit. skyfield handles four time systems: UTC, UT1, TT (Terrestrial Time), and TDB (Barycentric Dynamical Time). TLE epochs are in UT1 (Universal Time 1), which is Earth-rotation-based and differs from UTC by up to ±0.9 seconds — skyfield handles the conversion automatically via the IERS (International Earth Rotation Service) tables it downloads on first use.

Always use skyfield's ts.utc(...), ts.tt(...), etc., to construct times. Never compute time offsets manually.

Sub-satellite points and ground tracks

The sub-satellite point is the lat/lon directly below the satellite (the point on Earth's surface intersected by the line from Earth's center through the satellite). For a 24-hour ground track, you propagate to a series of times, extract the sub-satellite point at each, and stitch them into a polyline.

from skyfield.api import load
import numpy as np

t0 = ts.now()
times = [ts.tt_jd(t0.tt + i/1440) for i in range(0, 1440, 1)]  # 1-minute steps for 24h
positions = [(wgs84.subpoint(iss.at(t)).longitude.degrees,
              wgs84.subpoint(iss.at(t)).latitude.degrees) for t in times]

One subtlety: ground tracks cross the antimeridian (180° / -180°) and must be split there into separate line segments for proper visualization, or the line will draw across the entire map. The lab notebook handles this with a simple jump-detection split.

The lab

You'll generate the ISS's next 24-hour ground track at 1-minute resolution (1,440 points) as a GeoJSON LineString with embedded timestamps. Then you'll visualize it in QGIS over a Natural Earth basemap, color-coded by altitude (use a small vertical exaggeration since ISS altitude varies only ~10 km over an orbit).

This is the same pipeline LaunchDetect uses for the live satellite tracker in production — the only differences are streaming updates (Week 19) and a 3D globe instead of a 2D map (Week 18).

Connecting to Hawaiʻi: Ground tracks over the Pacific

The International Space Station is on a 51.6° inclination orbit, which means its ground track passes over Hawaiʻi roughly once every few days. From Honolulu the ISS is visible (at favorable geometry) maybe 2–4 times a week, just before dawn or just after sunset. The Pacific Voyaging Society has used satellite tracking as a teaching tool — the same SGP4 math gives the ISS's next pass over Hōkūleʻa wherever she's sailing. Different navigation traditions; same physics underneath.

ISS Detector and Heavens-Above are free apps that show you when to look. Both use SGP4. After this week you'll know how they work.

Hands-on lab: Generate a 24-hour ISS ground track

Load the current ISS TLE. Propagate it from now + 24 hours in 60-second steps. Compute the sub-satellite point at each step. Output as a GeoJSON LineString with timestamps.

Quiz — click an answer to check it

No grade, no shame. Tap any option; you'll see if it's right plus the answer if not. The point is to notice what you already know and what's still settling.

Q1. SGP4 stands for:
  1. Simplified General Perturbations 4
  2. Solar Geostationary Path 4
  3. Satellite Geoid Position 4
  4. Standard GPS Propagation 4
Q2. skyfield is:
  1. A web app
  2. A Python library for high-precision astronomy and satellite propagation
  3. A C library
  4. A QGIS plugin
Q3. The sub-satellite point is:
  1. The closest ground station
  2. The point on Earth's surface directly below the satellite
  3. The orbital periapsis
  4. The launch site
Q4. TLE epochs are in:
  1. UTC
  2. GPS time
  3. TT (Terrestrial Time)
  4. UT1
Q5. skyfield's `wgs84.subpoint()` returns:
  1. Lat/lon/elevation
  2. Only lat
  3. Only lon
  4. ECEF coordinates

Reflection

Take five minutes with this. Write your answer somewhere. Carry it into next week.

Knowing exactly when a satellite is overhead is useful — for spotting, for radio contact, for photography. It's also useful for adversarial purposes (timing operations to avoid surveillance, or timing them to USE surveillance). What's the right way to teach a powerful skill?
Mark this week complete Visiting alone doesn't count it as 'done'. Click when you've actually worked through the primer + lab + quiz.
Share + discuss on Twitter/X Discuss on GitHub