How a pass prediction works

GP Passes turns a few lines of numbers into "look north-north-east at 3:45 AM". It does it in five steps, all on your phone.A-01 Each step has a short version, then a "Go deeper" section with the maths and the evidence for it.

We'll follow one real pass all the way through. The satellite is SARAMAGO, a small Portuguese satellite. It was the first object ever to be given catalog number 100000, on 11 July 2026.G-10E-01 The place is 40° N, 75° W, in the eastern United States.E-03


1. Element sets: the orbit, as numbers

A satellite's orbit can be written down as a handful of numbers, measured at one moment. That's called an element set. From it you can work out where the satellite will be at any time, much as you'd work out where a ship will be from its last known position, speed and heading.A-51

GP Passes gets element sets in two ways:

Element sets age. The app shows each one's age, suggests newer ones after three days, and says passes from one over fourteen days old are not reliable.A-16

Go deeper: what's in an element set

Here is SARAMAGO's first published element set, as a two-line element set (TLE).E-01E-02

SARAMAGO
1 A0000U 26067CY  26195.90649229  .00004770  00000+0  22159-3 0  9994
2 A0000  97.4593 154.0970 0005590 270.5113  89.5482 15.20467281 15911

Data: CelesTrak.E-01

The 69 columns of each line, field by field.E-11E-01
  1. Line 1
    1. 1 line number (column 1)
    2. A0000 catalog number (columns 3–7)
    3. U classification (column 8)
    4. 26067CY international designator (columns 10–17)
    5. 26195.90649229 epoch (columns 19–32)
    6. .00004770 first derivative of mean motion (columns 34–43)
    7. 00000+0 second derivative of mean motion (columns 45–52)
    8. 22159-3 B*, the drag term (columns 54–61)
    9. 0 ephemeris type (column 63)
    10. 999 element set number (columns 65–68)
    11. 4 checksum (column 69)
  2. Line 2
    1. 2 line number (column 1)
    2. A0000 catalog number (columns 3–7)
    3. 97.4593 inclination (columns 9–16)
    4. 154.0970 right ascension of the ascending node (columns 18–25)
    5. 0005590 eccentricity (columns 27–33)
    6. 270.5113 argument of perigee (columns 35–42)
    7. 89.5482 mean anomaly (columns 44–51)
    8. 15.20467281 mean motion (columns 53–63)
    9. 1591 revolution number (columns 64–68)
    10. 1 checksum (column 69)
field in this set means
catalog number A0000 100000, in Alpha-5 (below)
international designator 26067CY the launch label: launch year 2026, launch number 067 of that year, and object CY from that launch
epoch 26195.90649229 day 195.906… of 2026: 14 July 2026, 21:45 UTC. The moment the numbers describe
B* 22159-3 0.00022159 per Earth radius: the drag term SGP4 uses for the thin air at that height
inclination 97.4593 the tilt of the orbit against the equator, in degrees. 0° is right above the equator; 90° runs from pole to pole
eccentricity 0005590 0.0005590: how stretched the orbit is. 0 is a perfect circle, so this one is very nearly round
mean motion 15.20467281 orbits per day, so one orbit takes about 94.7 minutes

E-02K-17A-58 The rest (right ascension of the ascending node, argument of perigee, mean anomaly) fix where the orbit lies and where the satellite is on it.

TLE and OMM. A TLE has fixed columns, and its catalog number has room for only five characters. An OMM (Orbit Mean-Elements Message, CCSDS 502.0-B-3) carries the same elements as named fields, in CSV, JSON, XML or KVN, with room for catalog numbers of up to nine digits.G-12K-22K-24 The app reads all five formats.A-46

Catalog numbers above 99,999. The catalog passed 99,999 on 11 July 2026, when number 100000 was assigned. The usable five-digit range had already run out at 69,999 (CelesTrak).G-10G-11 For TLEs, a stopgap called Alpha-5 writes the first two digits of a six-digit number as one letter: A = 10 through Z = 33, skipping I and O. So 100000 is A0000, and the most it can hold is 339999.G-12K-23 Space-Track serves Alpha-5 and CelesTrak doesn't: CelesTrak's TLE answers leave out every object above 99999, while its OMM formats carry them all.G-12 The app downloads CSV, an OMM format.A-10 SARAMAGO's TLE above was written from CelesTrak's OMM by gpconf, since CelesTrak serves no TLE for it.E-01

Why sets age. Even at a satellite's height there's a trace of air, and it changes low orbits a little every day, so passes drift from what old numbers say.A-51 Ages: up to 3 days is current, over 3 is ageing, over 14 is not reliable.A-16


2. SGP4: from numbers to a position

SGP4 is the standard method for running an element set forward in time. It's what TLEs are made for.A-58 Give it an element set and a time, and it gives the satellite's position and velocity.K-11 The app uses GPKit's SGP4, a Swift port of David Vallado's SGP4.cpp.A-04K-30

SARAMAGO's set describes 14 July at 21:45 UTC (17:45 local time at the example place). To find its passes over the next two days, SGP4 is run forward from there, again and again.E-03

Go deeper: frames, constants and checks


3. Rise, highest point, set

A pass has three moments: Rise, when the satellite clears your horizon; Highest, the top of its climb; and Set, when it goes back below.A-51 The app looks ahead 3, 5 or 10 days (3 unless you choose more).A-17

To find them, the app checks how high the satellite is every 30 seconds. When it crosses the horizon between two checks, it narrows the moment down to a millisecond. Then it finds the top of the climb.K-50K-51

SARAMAGO, 15 July 2026, from 40° N, 75° WE-03E-06:

UTC local time at the example place (EDT, UTC−4) height direction
Rise 07:45:08 03:45:08 0° NNE (17.5°)
Highest 07:50:55 03:50:55 48.2° E (99.4°)
Set 07:56:37 03:56:37 0° S (181.1°)

About eleven and a half minutes above the horizon. At its highest it is 668 km away.E-03 These are Skyfield's figures. In GPKit's recorded comparison, its own times for this satellite's passes are within 0.12 s of them.E-04

GPKit's own figures for this pass: rise at 07:45:08.36 UTC, highest at 07:50:54.69 (48.21°, az 99.39°, 667.9 km), set at 07:56:36.61.E-09


4. Sunlight and darkness: can you see it?

Satellites have no lights of their own. You see sunlight bouncing off them. So you can see one only when two things are true at once: the satellite is in sunlight, and your sky is dark.A-51A-14 That's why the best time to look is usually the few hours after dusk and before dawn.A-51

The app marks each pass with what it foundA-15:

"Can be seen" means the light is right. Whether you see it also depends on how bright the satellite is and on your sky.A-15 For a radio, every pass counts: radio doesn't need sunlight.A-51

SARAMAGO's pass: can be seen from its rise at 3:45 AM until 3:52 AM, when it goes into the Earth's shadow. The sky is dark, with the sun 17° below the horizon.E-05E-06

Go deeper: the sun and the shadow


5. Where to look

The app gives every moment as a direction and a height.A-19

For SARAMAGO: face north-north-east at 07:45 UTC (03:45 local time at the example place) and look low. It climbs to almost halfway up the sky in the east, then sinks towards the south.E-03

The sky view draws the whole pass on a disc: the middle is straight overhead, the edge is your horizon, north is up and east is to the right, as on a map.A-19A-51

SARAMAGO's pass across the sky, 15 July 2026 A disc for the sky above 40° N, 75° W: straight up in the middle, the horizon at the edge, north at the top and east to the right. The satellite rises at 3:45 AM low in the north-north-east, climbs to 48° in the east at 3:50 AM, and sets in the south at 3:56 AM. 30°60° NE SW Rise 3:45 AMlow, north-north-east Highest 3:50 AM48° up, in the east Set 3:56 AMin the south
SARAMAGO, the first satellite numbered 100000, over 40° N, 75° W on 15 July 2026, in local time (EDT). Its real track, every 10 seconds, about eleven and a half minutes above the horizon.G-10E-03E-06E-08

Go deeper: azimuth and elevation


Sources

  1. A-01 Passes are worked out on the phone, from element sets, using GPKit's SGP4. Source: Passes: Modules/Sources/PassesCore/Passes.swift:116-129; Modules/Package.swift:24-25.
  2. G-10 On 11 July 2026 the catalog assigned number 100000, to SARAMAGO. Source: gpconf: README.md:98-99; docs/BRAND.md:59.
  3. E-01 SARAMAGO's first published element set, as a TLE rendered by gpconf from CelesTrak's OMM (gp-first.php?CATNR=100000&FORMAT=CSV, retrieved 2026-09-21T00:43:55Z), because CelesTrak serves no TLE above 99999. Source: gpconf: derived/alpha5-tle/alpha5-A-100000-saramago-first.tle and .provenance.json; the same lines in GPKit Tests/GPKitTests/Resources/pass-check-sets.tle (NOTICE:56-61, from gpconf at bec466b).
  4. E-03 Over 40.0° N, 75.0° W (100 m), from 2026-07-14T21:45 UTC for two days, Skyfield 1.55 gives a SARAMAGO pass: rise 2026-07-15 07:45:08 UTC at az 17.5°; culmination 07:50:55 UTC, el 48.21°, az 99.4°, range 667.9 km; set 07:56:37 UTC at az 181.1°. Source: GPKit: Tests/GPKitTests/Resources/skyfield-passes.json (set "SARAMAGO"), written by tools/skyfield_passes_reference.py.
  5. A-51 The six lessons and their titles, in order. Source: Passes: PassesCore/Lessons.swift:13,84-135.
  6. A-10 The app downloads https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=CSV, and only when you tap Download. Source: Passes: PassesCore/Download.swift:34; PassesUI/AppModel.swift:438-440 ("Nothing else in the app calls this"); callers SatelliteListView.swift:185-187.
  7. A-11 Only one source can be chosen in a release build: CelesTrak's amateur group. SatNOGS DB and an own server are not available. Source: Passes: PassesCore/DataSource.swift:117-140.
  8. E-10 CelesTrak's amateur group includes the ISS. Source: Passes: DataSource.swift:153 (app text).
  9. A-25 At most one request every two hours. The attempt is counted before it is sent, and there are no retries. Source: Passes: Download.swift:146-161; DataSource.swift:98.
  10. A-12 You can import element-set files from the Files app. Source: Passes: SatelliteListView.swift:161-169.
  11. A-47 Import is through the Files picker, and only in Detailed mode. There is no paste and no share-sheet import. Source: Passes: SatelliteListView.swift:113-169.
  12. A-16 An element set's age is shown, and it is marked "not reliable" past two weeks. Source: Passes: PassesCore/Lessons.swift:107-116; README.md:65-70.
  13. E-02 The field reading of that TLE: designator 26067CY; epoch day 195.90649229 of 2026 = 14 July 2026 21:45 UTC; B* 0.22159e-3; inclination 97.4593°; eccentricity 0.0005590; mean motion 15.20467281 rev/day, so a period of about 94.7 min (1440 / 15.20467281 = 94.707). Source: the line itself; the field layout as GPKit reads it (TLE.swift); the arithmetic shown.
  14. K-17 B* is stored per Earth radius; mean motion is revolutions per day. Source: GPKit: ElementSet.swift:24-25,39-40.
  15. A-58 The app's glossary text, as quoted: SGP4 "The standard method for running an element set forward in time… It's what TLEs are made for"; azimuth "true directions, as on a map"; designator, inclination, eccentricity. Source: Passes: PassesCore/Glossary.swift:43,67,69,71,73.
  16. G-12 Space-Track serves Alpha-5 and CelesTrak does not; a number above 339999 can't be written as a TLE. Source: gpconf: README.md:105-113,136-138.
  17. K-22 OMM in CSV, JSON, XML (505.0-B-3) and KVN (502.0-B-3 §7). Quirks handled as listed. DOCTYPE refused. Source: GPKit: ElementSets.swift:1-13; XML.swift:1-7; README.md:105-119.
  18. K-24 Catalog numbers are an Int; OMM NORAD_CAT_ID takes up to nine digits; Alpha-5 is refused in OMM. Source: GPKit: OMM.swift:4-17; ElementSet.swift:9-10; OMMTests.swift:58-70.
  19. A-46 Import reads OMM in CSV, JSON, XML or KVN, and TLE (including .2le and .3le), detected by content. Source: Passes: FormatDetector.swift:4-44.
  20. G-11 The five-digit range ended at 69,999 (CelesTrak). Source: gpconf: docs/BRAND.md:59; docs/RESEARCH.md:32-36.
  21. K-23 Alpha-5 as Space-Track defines it: A–Z without I and O = 10–33, ceiling 339999. Test vectors. Source: GPKit: Alpha5.swift:1-46; ValueTests.swift:85-101.
  22. K-11 Data flow: ElementSets.read → ElementSet → Propagator → TEME state → Earth-fixed → look → passes. Source: GPKit: ElementSets.swift:54-69; OMM.swift:43-104; Propagator.swift:94-128; Observer.swift:102-154; Passes.swift:58-177.
  23. A-04 Settings → About shows "Orbits: GPKit 1.0.0 (SGP4)". Source: Passes: Modules/Sources/PassesUI/SettingsScreen.swift:127.
  24. K-30 SGP4 is a port of Vallado's SGP4.cpp (CelesTrak/fundamentals-of-astrodynamics 49df0479c950), with names and order of operations kept. Source: GPKit: NOTICE:9-17; SGP4Core.swift:1-15.
  25. K-31 Gravity constants WGS-72 (default), WGS-84 and WGS-72-old. Source: GPKit: SGP4Core.swift:78-108; Propagator.swift:76-94.
  26. K-32 Deep-space terms apply at a period of 225 min or more. Source: GPKit: SGP4Core.swift:44-45.
  27. K-34 Positions are TEME; UT1 is taken as UTC (within 0.9 s); polar motion is left out. Source: GPKit: Observer.swift:102-104; README.md:218-219.
  28. K-35 WGS-84 ellipsoid for the observer. Source: GPKit: Observer.swift:95-100.
  29. K-40 SGP4 vs the unmodified C++: 33 cases (24 deep space), 2,349 steps, equal error codes, agreeing within 1 micrometre (asserted). Source: GPKit: SGP4Tests.swift:74-75,102-126.
  30. K-42 Skyfield 1.55: five public element sets over 40° N, 75° W, two days each; 49 passes; 95 rises and sets within 0.166 s; 47 culminations within 0.349 s; look-angle bounds. Source: GPKit: conformance/skyfield-2026-10-07.txt:1-19; test PassTests.swift:168-248.
  31. A-17 Look ahead 3, 5 or 10 days, default 3. Source: Passes: Passes.swift:101-102; PassFilter.swift:134.
  32. K-50 Elevation is sampled every 30 s. Source: GPKit: Passes.swift:110.
  33. K-51 Crossings are found by bisection to 0.5 ms; the highest point by golden-section search. Source: GPKit: Passes.swift:77-108.
  34. E-06 Local time at the example place: US Eastern Daylight Time (UTC−4) on 14–15 July 2026, so rise 03:45:08, highest 03:50:55, set 03:56:37, epoch 17:45. Source: IANA time zone America/New_York through Python's zoneinfo, run 2026-10-31.
  35. E-04 GPKit's recorded comparison for SARAMAGO: 9 passes, largest time difference 0.11986 s. Source: GPKit: conformance/skyfield-2026-10-07.txt:9.
  36. E-09 GPKit's own figures for the example pass: rise 07:45:08.36 UTC at az 17.49°; highest 07:50:54.69 UTC, el 48.21°, az 99.39°, range 667.9 km; set 07:56:36.61 UTC at az 181.08°; 9 passes in the two days, as in E-04. Source: Propagator.passes(over:from:for:): scratch package docs/checks/site-figures/ (GPKit v1.0.0, PassesCore at Passes cd6b3fd, E-01's lines), run 2026-11-07; output docs/checks/site-figures/output-2026-11-07.txt.
  37. K-52 Passes shorter than a step are found. Source: GPKit: Passes.swift:147-156; test PassTests.swift:290-303.
  38. K-53 No refraction; angles are geometric. Source: GPKit: README.md:216-217.
  39. A-18 Minimum elevation 0, 5, 10, 15, 20 or 30°, default 0 ("The horizon"), settable per satellite. Source: Passes: ObserverLocation.swift:246-271; SettingsScreen.swift:185-187.
  40. A-44 Horizon mask: eight directions, heights 0–60° in steps (0,5,10,15,20,30,45,60), joined in straight lines between directions. Passes are clipped every 5 s. Source: Passes: HorizonMask.swift:8-16,113-136; Passes.swift:134-139.
  41. A-45 Passes that never clear the mask are counted. Source: Passes: Passes.swift:67-68.
  42. K-54 A search stops at the first failure and keeps what it found. Source: GPKit: Passes.swift:29-31,164-175; tests PassTests.swift:313-367.
  43. A-14 "Can be seen" means the satellite is in sunlight while your sky is dark (sun more than 6° below the horizon). Source: Passes: PassesCore/Sun.swift:14-15,145-147,268.
  44. A-15 Sky categories: daylight (sun above 0°), twilight (0 to −6°), dark. Earth's shadow is a cylinder of radius 6378.137 km. The sun's position follows NOAA/Meeus. Passes are sampled every 10 s (at most 240 samples) and changes refined to the second. Source: Passes: Sun.swift:7-18,101-136,215-249.
  45. E-05 The light of the example pass: sky dark, the sun 17° below the horizon at the highest point (−17.49°); in sunlight from rise until 07:52:57 UTC (3:52:57 AM EDT), then in the Earth's shadow; "Can be seen 3:45 AM to 3:52 AM". Source: PassLight.of and its summary, skyLine, sunlightLine: scratch package docs/checks/site-figures/ (GPKit v1.0.0, PassesCore at Passes cd6b3fd, E-01's lines), run 2026-11-07; output docs/checks/site-figures/output-2026-11-07.txt.
  46. K-55 GPKit has no horizon that varies by direction: a pass search takes one minimum elevation. (1.0.0 also had no sun or visibility model; 1.1.0 adds one, K-96). Source: GPKit v1.1.0 (tag at 0c4d396, released 2026-11-09): docs/public-api.swift:201 (passes(over:from:for:above minimumElevation: Double = 0)), the only horizon parameter in the interface; v1.0.0: a grep of Sources for sun/eclipse/sunlit/twilight found nothing.
  47. A-19 Directions use a 16-point compass. The sky view has the zenith at the centre and north up. Source: Passes: Display.swift:21-23; Passes.swift:186-196.
  48. A-52 A fist at arm's length is about 10°. ISS FM repeater 145.990 up / 437.800 down, 67 Hz tone. A licence is needed to transmit. Source: Passes: Lessons.swift:84-90,127-135. The app cites its sources in docs/audit-2026-10/LEARNING.md §8.
  49. K-16 Azimuth is degrees clockwise from north (90 is east); elevation is degrees above the horizon, negative below it; range is in km; range rate is in km/s, positive while receding. Source: GPKit: Observer.swift:64-73.
  50. E-11 A TLE's fields by column, as GPKit reads them: line 1 has the line number (1), catalog number (3–7), classification (8), international designator (10–17), epoch (19–32), first and second derivatives of mean motion (34–43, 45–52), B* (54–61), ephemeris type (63), element set number (65–68) and checksum (69); line 2 the line number, catalog number (3–7), inclination (9–16), right ascension of the ascending node (18–25), eccentricity (27–33), argument of perigee (35–42), mean anomaly (44–51), mean motion (53–63), revolution number (64–68) and checksum (69); each line 69 characters. Source: GPKit v1.0.0 (01545c6, the version GP Passes 1.0 resolves: Passes Modules/Package.resolved at 3557f71): Sources/GPKit/TLE.swift:51 (69 characters), :67-68, :72, :75, :78-79, :86-88, :98-112 (the 0-based slices), :184 (the line number).
  51. E-08 The example pass's track for the sky view: azimuth and elevation every 10 s from rise to set, 70 points; its events reproduce E-03 (rise 07:45:08.5 UTC at 17.49°, highest 07:50:54.7 at 48.21°, 99.4°, 667.9 km; set 07:56:36.7 at 181.08°). Source: tools/sky_track.py, Skyfield 1.55 (the corpus's .venv), E-01's lines, offline; output sites/passes/data/saramago-track.json.