README covers all 57 functions across 9 domains with quick-start examples, capability matrix, body ID tables, and performance benchmarks. Links to Starlight docs site for detailed reference. Adds Docker Compose deployment for docs site behind caddy-docker-proxy with dev/prod modes and Vite HMR support through reverse proxy.
231 lines
7.7 KiB
Markdown
231 lines
7.7 KiB
Markdown
# pg_orbit
|
|
|
|
Solar system computation for PostgreSQL.
|
|
|
|
pg_orbit moves orbital mechanics inside your database. Track satellites, compute
|
|
planet positions, observe 19 planetary moons, predict Jupiter radio bursts, and
|
|
plan interplanetary trajectories — all from standard SQL. Think PostGIS, but for
|
|
objects in space.
|
|
|
|
57 functions. 7 custom types. All `PARALLEL SAFE`. Zero external dependencies at
|
|
runtime.
|
|
|
|
## Installation
|
|
|
|
### Docker (recommended)
|
|
|
|
```bash
|
|
docker run -d --name pg_orbit \
|
|
-e POSTGRES_PASSWORD=orbit \
|
|
-p 5499:5432 \
|
|
git.supported.systems/warehack.ing/pg_orbit:pg17
|
|
```
|
|
|
|
```bash
|
|
psql -h localhost -p 5499 -U postgres -c "CREATE EXTENSION pg_orbit;"
|
|
```
|
|
|
|
### Build from Source
|
|
|
|
Requires PostgreSQL 17 development headers and a C/C++ toolchain.
|
|
|
|
```bash
|
|
git clone https://git.supported.systems/warehack.ing/pg_orbit.git
|
|
cd pg_orbit
|
|
git submodule update --init
|
|
make PG_CONFIG=/usr/bin/pg_config
|
|
sudo make install PG_CONFIG=/usr/bin/pg_config
|
|
```
|
|
|
|
```sql
|
|
CREATE EXTENSION pg_orbit;
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
**Where is Jupiter right now?**
|
|
|
|
```sql
|
|
SELECT topo_azimuth(t) AS az,
|
|
topo_elevation(t) AS el,
|
|
topo_range(t) / 149597870.7 AS distance_au
|
|
FROM planet_observe(5, '40.0N 105.3W 1655m'::observer, now()) t;
|
|
```
|
|
|
|
**What's the entire solar system doing?**
|
|
|
|
```sql
|
|
SELECT body_id,
|
|
CASE body_id
|
|
WHEN 1 THEN 'Mercury' WHEN 2 THEN 'Venus'
|
|
WHEN 3 THEN 'Earth' WHEN 4 THEN 'Mars'
|
|
WHEN 5 THEN 'Jupiter' WHEN 6 THEN 'Saturn'
|
|
WHEN 7 THEN 'Uranus' WHEN 8 THEN 'Neptune'
|
|
END AS name,
|
|
round(helio_distance(planet_heliocentric(body_id, now()))::numeric, 4) AS distance_au
|
|
FROM generate_series(1, 8) AS body_id;
|
|
```
|
|
|
|
**Predict ISS passes over your location:**
|
|
|
|
```sql
|
|
WITH iss AS (
|
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle AS tle
|
|
)
|
|
SELECT pass_aos(p) AS rise_time,
|
|
pass_max_el(p) AS max_elevation,
|
|
pass_los(p) AS set_time
|
|
FROM iss, predict_passes(tle, '40.0N 105.3W 1655m'::observer,
|
|
now(), now() + interval '24 hours', 10.0) p;
|
|
```
|
|
|
|
**When will Jupiter produce radio bursts tonight?**
|
|
|
|
```sql
|
|
SELECT t,
|
|
round(jupiter_burst_probability(
|
|
io_phase_angle(t),
|
|
jupiter_cml('40.0N 105.3W 1655m'::observer, t)
|
|
)::numeric, 3) AS burst_prob
|
|
FROM generate_series(now(), now() + interval '12 hours', interval '10 minutes') AS t
|
|
WHERE jupiter_burst_probability(
|
|
io_phase_angle(t),
|
|
jupiter_cml('40.0N 105.3W 1655m'::observer, t)
|
|
) > 0.3;
|
|
```
|
|
|
|
**Plan an Earth-Mars transfer:**
|
|
|
|
```sql
|
|
SELECT round(c3_departure::numeric, 2) AS c3_depart_km2s2,
|
|
round(tof_days::numeric, 1) AS flight_days,
|
|
round(transfer_sma::numeric, 4) AS sma_au
|
|
FROM lambert_transfer(3, 4, '2028-10-01'::timestamptz, '2029-06-15'::timestamptz);
|
|
```
|
|
|
|
## What It Covers
|
|
|
|
| Domain | Theory | Key Functions | Accuracy |
|
|
|---|---|---|---|
|
|
| Satellites | SGP4/SDP4 (Brouwer 1959) | `observe()`, `predict_passes()` | ~1 km (LEO, fresh TLE) |
|
|
| Planets | VSOP87 (Bretagnon 1988) | `planet_observe()`, `planet_heliocentric()` | ~1 arcsecond |
|
|
| Sun | VSOP87 (Earth vector, inverted) | `sun_observe()` | ~1 arcsecond |
|
|
| Moon | ELP2000-82B (Chapront 1988) | `moon_observe()` | ~10 arcseconds |
|
|
| Planetary moons | L1.2, TASS17, GUST86, MarsSat | `galilean_observe()`, etc. | ~1-10 arcseconds |
|
|
| Stars | J2000 catalog + precession | `star_observe()` | Limited by catalog |
|
|
| Comets/asteroids | Two-body Keplerian | `kepler_propagate()`, `comet_observe()` | Varies with eccentricity |
|
|
| Jupiter radio | Carr et al. (1983) sources | `jupiter_burst_probability()` | Empirical probability |
|
|
| Transfers | Lambert (Izzo 2015) | `lambert_transfer()`, `lambert_c3()` | Ballistic two-body |
|
|
|
|
## Types
|
|
|
|
| Type | Bytes | Description |
|
|
|------|-------|-------------|
|
|
| `tle` | 112 | Parsed mean orbital elements for SGP4/SDP4 propagation |
|
|
| `eci_position` | 48 | Position and velocity in TEME frame (km, km/s) |
|
|
| `geodetic` | 24 | Latitude, longitude, altitude on WGS-84 ellipsoid |
|
|
| `topocentric` | 32 | Azimuth, elevation, range, range rate relative to observer |
|
|
| `observer` | 24 | Ground location. Input: `'40.0N 105.3W 1655m'` or decimal degrees |
|
|
| `pass_event` | 48 | Satellite pass with AOS/TCA/LOS times and azimuths |
|
|
| `heliocentric` | 24 | Position in AU, ecliptic J2000 frame |
|
|
|
|
All types are fixed-size with `STORAGE = plain`. No TOAST overhead.
|
|
|
|
## Body IDs
|
|
|
|
Planets follow the VSOP87 convention. Planetary moons use per-family indexing.
|
|
|
|
| ID | Planet | | Galilean (0-3) | Saturn (0-7) | Uranus (0-4) | Mars (0-1) |
|
|
|----|--------|-|----------------|--------------|--------------|------------|
|
|
| 1 | Mercury | | 0: Io | 0: Mimas | 0: Miranda | 0: Phobos |
|
|
| 2 | Venus | | 1: Europa | 1: Enceladus | 1: Ariel | 1: Deimos |
|
|
| 3 | Earth | | 2: Ganymede | 2: Tethys | 2: Umbriel | |
|
|
| 4 | Mars | | 3: Callisto | 3: Dione | 3: Titania | |
|
|
| 5 | Jupiter | | | 4: Rhea | 4: Oberon | |
|
|
| 6 | Saturn | | | 5: Titan | | |
|
|
| 7 | Uranus | | | 6: Iapetus | | |
|
|
| 8 | Neptune | | | 7: Hyperion | | |
|
|
|
|
## GiST Indexing
|
|
|
|
The `tle_ops` operator class indexes TLEs by altitude band for conjunction screening:
|
|
|
|
```sql
|
|
CREATE INDEX ON satellites USING gist (tle);
|
|
|
|
-- Find objects in overlapping altitude shells
|
|
SELECT a.name, b.name
|
|
FROM satellites a, satellites b
|
|
WHERE a.tle && b.tle AND a.norad_id < b.norad_id;
|
|
|
|
-- K-nearest-neighbor by altitude separation
|
|
SELECT name, round((tle <-> iss.tle)::numeric, 0) AS alt_sep_km
|
|
FROM satellites, (SELECT tle FROM satellites WHERE norad_id = 25544) iss
|
|
ORDER BY tle <-> iss.tle
|
|
LIMIT 20;
|
|
```
|
|
|
|
## Performance
|
|
|
|
Measured on PostgreSQL 17, single backend:
|
|
|
|
| Operation | Count | Time | Rate |
|
|
|---|---|---|---|
|
|
| TLE propagation (SGP4) | 12,000 | 17ms | 706K/sec |
|
|
| Planet observation (VSOP87) | 875 | 57ms | 15.4K/sec |
|
|
| Moon observation (Galilean) | 1,000 | 63ms | 15.9K/sec |
|
|
| Star observation | 500 | 0.7ms | 714K/sec |
|
|
| Lambert transfer solve | 100 | 0.1ms | 800K/sec |
|
|
| Pork chop plot (150x150) | 22,500 | 8.3s | 2.7K/sec |
|
|
|
|
## Testing
|
|
|
|
11 regression test suites covering all domains:
|
|
|
|
```bash
|
|
make installcheck PG_CONFIG=/usr/bin/pg_config
|
|
```
|
|
|
|
Tests: TLE parsing, SGP4/SDP4 propagation, coordinate transforms, pass prediction,
|
|
GiST indexing, convenience functions, star observation, Keplerian propagation,
|
|
planet observation, moon observation, and Lambert transfers.
|
|
|
|
## Documentation
|
|
|
|
Full documentation at the [pg_orbit docs site](https://pg-orbit.supported.systems),
|
|
built with [Starlight](https://starlight.astro.build). Includes guides, workflow
|
|
translations (from Skyfield, JPL Horizons, GMAT, Radio Jupiter Pro), complete
|
|
function reference, architecture notes, and benchmarks.
|
|
|
|
## What pg_orbit Is Not
|
|
|
|
**Not a GUI.** Use Stellarium, GPredict, or STK for visualization.
|
|
|
|
**Not sub-arcsecond.** VSOP87 gives ~1 arcsecond — good for observation planning,
|
|
not for dish pointing at GHz frequencies. For that, use SPICE or Skyfield with DE441.
|
|
|
|
**Not a TLE source.** Bring your own from Space-Track, CelesTrak, or any provider.
|
|
|
|
**Not a replacement for SPICE.** No BSP kernels, no aberration corrections at IAU 2000A
|
|
level. pg_orbit trades those last few milliarcseconds for SQL-speed computation joined
|
|
with your existing data.
|
|
|
|
**Not a full mission design tool.** The Lambert solver handles ballistic two-body
|
|
transfers. For low-thrust, gravity assists, or multi-body optimization, use GMAT.
|
|
|
|
## Upgrading from v0.1.0
|
|
|
|
```sql
|
|
ALTER EXTENSION pg_orbit UPDATE TO '0.2.0';
|
|
```
|
|
|
|
Adds all solar system functions while preserving existing TLE data and satellite functions.
|
|
|
|
## License
|
|
|
|
[PostgreSQL License](LICENSE). Copyright (c) 2025, Ryan Malloy.
|
|
|
|
The bundled [sat_code](https://github.com/Bill-Gray/sat_code) library is separately
|
|
licensed under the MIT license.
|