Week 9 · Orbital Analyst

Ground-to-satellite line-of-sight and coverage

Can a ground station see a given satellite right now? This is a question of geometry, line-of-sight, and (a little) atmosphere. This week you build the math and the code.

Learning objectives

Primer

Visibility from the ground is the bread and butter of practical satellite tracking. "When can I see the ISS from my backyard?" "Will Starlink overfly me tonight?" "Does my ground station have line-of-sight to GOES-19?" All three reduce to the same geometric question: given a satellite ephemeris and an observer location, when is the satellite above the observer's horizon, and at what direction (azimuth) and how high (elevation) in the sky?

Azimuth and elevation

From any point on Earth's surface, a satellite's position relative to you can be described by two angles:

A satellite is "visible" when its elevation is greater than zero. For practical purposes (atmospheric absorption, trees, buildings), most ground stations consider visibility to start at ~10° elevation. Astronomy observations often require >30° elevation to escape atmospheric turbulence.

Pass geometry

A satellite pass has three key moments:

  1. Rise (AOS — acquisition of signal) — the satellite crosses 0° elevation from below.
  2. Culmination (TCA — time of closest approach) — the satellite reaches its maximum elevation during the pass.
  3. Set (LOS — loss of signal) — the satellite returns to 0° elevation and disappears.

For an ISS pass, the entire cycle takes 4–10 minutes depending on geometry. A "great" pass has a culmination >70°; an unusable pass culminates <10°.

Computing this with skyfield

from skyfield.api import load, wgs84

ts = load.timescale()
iss = EarthSatellite(line1, line2, "ISS", ts)
observer = wgs84.latlon(40.7128, -74.0060)  # NYC

# Look for events in next 24 hours
t0 = ts.now()
t1 = ts.tt_jd(t0.tt + 1.0)
times, events = iss.find_events(observer, t0, t1, altitude_degrees=10.0)
# events: 0=rise, 1=culminate, 2=set
for t, ev in zip(times, events):
    alt, az, _ = (iss - observer).at(t).altaz()
    print(f"{t.utc_iso()} event={ev} alt={alt.degrees:.1f}° az={az.degrees:.1f}°")

Line-of-sight

Pass visibility assumes nothing blocks the line of sight. For a real ground station, you also need to account for:

Coverage cone

From the satellite's perspective, the inverse question is: which observers can see me right now? The answer is a circular "footprint" on Earth's surface. For a LEO satellite at 400 km altitude with a 5° minimum elevation, the footprint radius is ~2,100 km — a circle covering most of the eastern United States.

The lab takes a user-supplied lat/lon and finds the next visible ISS pass within the next 24 hours, outputting AOS time, TCA time + max elevation, and LOS time as JSON. This is the same logic that powers launchdetect.com/satellite-tracker/'s "next visible pass" feature.

Hands-on lab: Predict next ISS pass over a user-supplied location

Given a lat/lon, propagate the ISS and find the next overhead pass (max elevation > 30°). Output the pass start, max-elevation time + angle, and pass end as JSON.

Quiz

Test yourself. Answer key on the certificate-track page (Gold-tier feature: progress tracking and auto-grading).

Q1. Elevation angle of 0° means:
  1. Directly overhead
  2. On the horizon
  3. Below the horizon
  4. Doesn't exist
Q2. Azimuth is measured:
  1. From north, clockwise
  2. From east, counter-clockwise
  3. From south, clockwise
  4. Up from horizon
Q3. A satellite at 90° elevation is:
  1. On the horizon
  2. Directly overhead
  3. Below the ground
  4. Just risen
Q4. Atmospheric refraction matters most at:
  1. High elevation
  2. Low elevation
  3. Nadir
  4. Apogee
Q5. Pass prediction depends on:
  1. Ground station lat/lon/alt and the satellite ephemeris
  2. Only the satellite name
  3. Date alone
  4. Weather