Getting started

Install

Swift Package Manager, in Package.swiftK-70:

dependencies: [
    .package(url: "https://github.com/hneogy/GPKit.git", from: "1.0.0"),
],
targets: [
    .target(name: "YourTarget", dependencies: ["GPKit"]),
]

In Xcode: File → Add Package Dependencies, and the same URL. Versions follow semantic versioning.K-70

Requirements for 1.0.0: macOS 13, iOS 16, tvOS 16 or visionOS 1, or Linux. 1.0.0 doesn't build for watchOS.K-04K-05 GPKit's main, not yet in a release, adds watchOS 9, Windows, Android and WebAssembly.K-91K-92

1. Read element sets

Give GPKit the bytes your provider served (Data, [UInt8], any collection of bytes, or a String) and say which format they're inK-71b:

import GPKit

let file = try ElementSets.read(bytes, as: .json)      // .csv, .json, .xml, .kvn or .tle

for set in file.elementSets {
    set.catalogNumber        // 25544: an Int, whatever its width
    set.objectID             // "1998-067A", or nil when the source has none
    set.epoch                // 1998-11-20T06:49:59.999808, kept as integers
    set.meanMotion           // 16.05064833, the digits the source wrote
    set.meanMotion.double    // the nearest Double, for computing
}

for refusal in file.refusals {
    print(refusal)           // NORAD_CAT_ID "A0000" is not an integer of up to nine digits (record 2 of the file)
}

2. Propagate

let propagator = try Propagator(set)                        // refuses an element set that is not for SGP4

let state = try propagator.state(at: time)                  // time is an Epoch, UTC
state.position                                              // kilometres from the Earth's centre, TEME
state.velocity                                              // kilometres per second

try propagator.state(minutesFromEpoch: 90)                  // or by minutes from the element set's epoch

let beneath = try propagator.position(at: time)             // the point of the WGS-84 ellipsoid beneath the satellite
beneath.latitude; beneath.longitude                         // degrees, north and east positive
beneath.altitude                                            // kilometres above that point

K-74 Distances are in kilometres and speeds in kilometres per second. The one exception is an Observer's height, which is in metres.K-15

Time. An Epoch is UTC. Epoch(date) and epoch.date convert to and from Foundation's Date, to the microsecond. Without Foundation, use Epoch(unixTime:) and unixTime. advanced(by:) and seconds(since:) work in seconds.K-74

3. Find passes

let home = Observer(latitude: 40.0, longitude: -75.0, height: 100)       // degrees, degrees, and a height in metres

let passes = propagator.passes(over: home, from: now, for: .seconds(86_400))   // the next day, above the horizon
for pass in passes {
    pass.rise?.time                     // nil when the satellite was already up at the start
    pass.culmination.look.elevation     // degrees, at its highest
    pass.set?.look.azimuth              // degrees clockwise from north; nil when still up at the end
}
passes.failure                          // nil when the whole day was searched

K-72 Add above: 10 for a minimum elevation in degrees.K-10K-53K-77

4. Look angles and Doppler

let look = try propagator.look(from: home, at: time)
look.azimuth; look.elevation            // degrees
look.range; look.rangeRate              // km, and km/s (positive while receding)

look.received(from: 437_000_000)        // what to tune a receiver to, for a downlink on 437 MHz
look.transmit(toBeReceivedAt: 145_900_000)   // what to tune a transmitter to, for an uplink on 145.9 MHz

K-10K-16K-75K-77

5. Write a TLE

let lines = try TLE.write(set)
lines.line1        // "1 A0000U ...": five digits below 100000, Alpha-5 from 100000 to 339999
lines.text         // the two lines, or three with the name

K-29 A catalog number above 339999 can't go in a TLE, so write refuses it. Keep that record as OMM.K-29K-23

Building and testing GPKit itself

swift build
swift test

With the Command Line Tools and no Xcode, the test build needs the testing plugin path. See the README.K-76


API overview → · How it works → · GitHub →

Sources

  1. K-70 The install snippet (SPM, from: "1.0.0"). Source: GPKit: README.md:29-38.
  2. K-04 Platforms: macOS 13, iOS 16, tvOS 16, visionOS 1. Linux is built in CI. Source: GPKit: Package.swift:9; .github/workflows/ci.yml:16.
  3. K-05 No watchOS, because Int is 32 bits on most watches. Source: GPKit: Package.swift:7-8.
  4. K-91 watchOS 9 and later: GPKit builds for watchOS (arm64 and arm64_32), in CI on every push; what doesn't fit a 32-bit Int is counted in Int64. Source: GPKit main 11b7ab4 (PR #25, 2026-11-07): CHANGELOG.md:61, :65-68; README.md:19-21; .github/workflows/ci.yml:208.
  5. K-92 GPKit builds and its tests run on Windows, on Android (in an emulator, swift.org's Android SDK for Swift 6.4.2) and for WebAssembly (WASI, swift.org's Wasm SDK), in CI. Source: GPKit main 11b7ab4 (PR #25, 2026-11-07): CHANGELOG.md:65-69, :72-73; README.md:19-23; .github/workflows/ci.yml:141-160, :163, :186-194.
  6. K-71b The Reading example (ElementSets.read, elementSets, refusals, the printed refusal). Source: GPKit: README.md:42-62.
  7. K-25 Refusals carry a kind (19 kinds), a message, a field and a line or record. One bad record costs one record. Source: GPKit: Refusal.swift:7-89; README.md:81-83.
  8. K-26 A file cut short is refused whole. Source: GPKit: README.md:85-87; test OMMTests.swift:120.
  9. K-73 A provider's "No GP data found" is not an error: no entries, providerMessage holds it. Source: GPKit: README.md:98-99.
  10. K-21 The checksum is verified by default; .ignore skips only the checksum. Source: GPKit: TLE.swift:8-34; README.md:93-96; test TLETests.swift:77-99.
  11. K-74 The Propagating example and Epoch conversions (state(at:), state(minutesFromEpoch:), position(at:), Epoch(date), unixTime, advanced(by:), seconds(since:)). Source: GPKit: README.md:159-186.
  12. K-15 Kilometres everywhere except an Observer's height, which is in metres. Source: GPKit: README.md:173-174.
  13. K-72 The Passes example (Observer with height in metres, passes(over:from:for:), rise/culmination/set). Source: GPKit: README.md:190-207.
  14. K-10 The public API (signatures). Source: GPKit: docs/public-api.swift (generated by tools/public-api.sh).
  15. K-53 No refraction; angles are geometric. Source: GPKit: README.md:216-217.
  16. K-77 The /docs/ lines not in the README compile and run: look(from:at:), look.azimuth, .elevation, .range, .rangeRate, and passes(over:from:for:above: 10). Source: 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.
  17. 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.
  18. K-75 The Doppler lines look.received(from: 437_000_000) and look.transmit(toBeReceivedAt: 145_900_000). Source: GPKit: README.md:205-206.
  19. K-29 The TLE writer uses exact decimal rounding, Alpha-5 from 100000, and refuses numbers above 339999. Source: GPKit: TLE.swift:242-354; README.md:121-133.
  20. 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.
  21. K-76 Building and testing: swift build, swift test, and the Command Line Tools plugin path. Source: GPKit: README.md:267-281.