Rename pg_orbit to pg_orrery
An existing product called PG Orbit (a mobile PostgreSQL client) creates a naming conflict. pg_orrery — a database orrery built from Keplerian parameters and SQL instead of brass gears. Build system: control file, Makefile, Dockerfile, docker init script. C source: GUC prefix, PG_FUNCTION_INFO_V1 symbol, header guards, ereport prefixes, comments across ~30 files including vendored SGP4. SQL: all 5 install/migration scripts, function name pg_orrery_ephemeris_info. Tests: 9 SQL suites, 8 expected outputs, standalone DE reader test. Documentation: CLAUDE.md, README.md, DESIGN.md, Starlight site infra, 36 MDX pages, OG renderer, logo SVG, docker-compose, agent threads. All 13 regression suites pass. Docs site builds (37 pages).
This commit is contained in:
parent
cd338c3c64
commit
3915d1784f
56
CLAUDE.md
56
CLAUDE.md
@ -1,11 +1,11 @@
|
|||||||
# pg_orbit — Solar System Computation for PostgreSQL
|
# pg_orrery — A Database Orrery for PostgreSQL
|
||||||
|
|
||||||
## What This Is
|
## What This Is
|
||||||
A PostgreSQL extension that moves orbital mechanics inside the database — the way PostGIS did for geography. Native C extension using PGXS, 68 SQL functions, 7 custom types, covering satellites (SGP4/SDP4), planets (VSOP87 + optional JPL DE441), Moon (ELP2000-82B), 19 planetary moons (L1.2/TASS17/GUST86/MarsSat), stars, comets, Jupiter radio bursts, and interplanetary Lambert transfers.
|
A database orrery — celestial mechanics types and functions for PostgreSQL. Native C extension using PGXS, 68 SQL functions, 7 custom types, covering satellites (SGP4/SDP4), planets (VSOP87 + optional JPL DE441), Moon (ELP2000-82B), 19 planetary moons (L1.2/TASS17/GUST86/MarsSat), stars, comets, Jupiter radio bursts, and interplanetary Lambert transfers.
|
||||||
|
|
||||||
**Current version:** 0.3.0 on branch `phase/solar-system-expansion`
|
**Current version:** 0.3.0 on branch `phase/solar-system-expansion`
|
||||||
**Repository:** https://git.supported.systems/warehack.ing/pg_orbit
|
**Repository:** https://git.supported.systems/warehack.ing/pg_orrery
|
||||||
**Documentation:** https://pg-orbit.warehack.ing
|
**Documentation:** https://pg-orrery.warehack.ing
|
||||||
|
|
||||||
## Build System
|
## Build System
|
||||||
```bash
|
```bash
|
||||||
@ -18,25 +18,25 @@ Requires: PostgreSQL 17 development headers, GCC, Make.
|
|||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
```bash
|
```bash
|
||||||
make docker-build # Build standalone image (pg17 + pg_orbit)
|
make docker-build # Build standalone image (pg17 + pg_orrery)
|
||||||
make docker-test # Smoke test the image
|
make docker-test # Smoke test the image
|
||||||
make docker-push # Push to git.supported.systems registry
|
make docker-push # Push to git.supported.systems registry
|
||||||
```
|
```
|
||||||
|
|
||||||
Image: `git.supported.systems/warehack.ing/pg_orbit:pg17`
|
Image: `git.supported.systems/warehack.ing/pg_orrery:pg17`
|
||||||
|
|
||||||
## Project Layout
|
## Project Layout
|
||||||
```
|
```
|
||||||
pg_orbit.control # Extension metadata (version 0.3.0)
|
pg_orrery.control # Extension metadata (version 0.3.0)
|
||||||
Makefile # PGXS build + Docker targets
|
Makefile # PGXS build + Docker targets
|
||||||
sql/
|
sql/
|
||||||
pg_orbit--0.1.0.sql # v0.1.0: satellite types/functions/operators
|
pg_orrery--0.1.0.sql # v0.1.0: satellite types/functions/operators
|
||||||
pg_orbit--0.2.0.sql # v0.2.0: solar system (57 functions)
|
pg_orrery--0.2.0.sql # v0.2.0: solar system (57 functions)
|
||||||
pg_orbit--0.3.0.sql # v0.3.0: complete extension (68 functions)
|
pg_orrery--0.3.0.sql # v0.3.0: complete extension (68 functions)
|
||||||
pg_orbit--0.1.0--0.2.0.sql # Migration: v0.1.0 → v0.2.0 (adds solar system)
|
pg_orrery--0.1.0--0.2.0.sql # Migration: v0.1.0 → v0.2.0 (adds solar system)
|
||||||
pg_orbit--0.2.0--0.3.0.sql # Migration: v0.2.0 → v0.3.0 (adds DE ephemeris)
|
pg_orrery--0.2.0--0.3.0.sql # Migration: v0.2.0 → v0.3.0 (adds DE ephemeris)
|
||||||
src/
|
src/
|
||||||
pg_orbit.c # PG_MODULE_MAGIC + _PG_init() (GUC registration)
|
pg_orrery.c # PG_MODULE_MAGIC + _PG_init() (GUC registration)
|
||||||
types.h # All struct definitions + constants + DE body ID mapping
|
types.h # All struct definitions + constants + DE body ID mapping
|
||||||
astro_math.h # Shared astronomical helpers + observe_from_geocentric()
|
astro_math.h # Shared astronomical helpers + observe_from_geocentric()
|
||||||
# --- Satellite (v0.1.0) ---
|
# --- Satellite (v0.1.0) ---
|
||||||
@ -119,7 +119,7 @@ All types are fixed-size, `STORAGE = plain`, `ALIGNMENT = double`. No TOAST over
|
|||||||
| Transfers | Lambert (Izzo 2015) | `lambert_transfer()`, `lambert_c3()` | 2 |
|
| Transfers | Lambert (Izzo 2015) | `lambert_transfer()`, `lambert_c3()` | 2 |
|
||||||
| DE ephemeris | JPL DE440/441 (optional) | `planet_observe_de()`, `moon_observe_de()` | 11 |
|
| DE ephemeris | JPL DE440/441 (optional) | `planet_observe_de()`, `moon_observe_de()` | 11 |
|
||||||
| GiST index | Altitude-band approximation | `&&` (overlap), `<->` (distance) | 8 |
|
| GiST index | Altitude-band approximation | `&&` (overlap), `<->` (distance) | 8 |
|
||||||
| Diagnostics | -- | `pg_orbit_ephemeris_info()` | 1 |
|
| Diagnostics | -- | `pg_orrery_ephemeris_info()` | 1 |
|
||||||
|
|
||||||
All functions are `PARALLEL SAFE`. VSOP87/ELP82B functions are `IMMUTABLE` (compiled-in coefficients). DE functions are `STABLE` (external file dependency).
|
All functions are `PARALLEL SAFE`. VSOP87/ELP82B functions are `IMMUTABLE` (compiled-in coefficients). DE functions are `STABLE` (external file dependency).
|
||||||
|
|
||||||
@ -191,16 +191,16 @@ v0.3.0 adds optional JPL DE440/441 ephemeris support (~0.1 milliarcsecond accura
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Set the path to a JPL DE binary file (requires superuser)
|
-- Set the path to a JPL DE binary file (requires superuser)
|
||||||
ALTER SYSTEM SET pg_orbit.ephemeris_path = '/var/lib/postgres/de441.bin';
|
ALTER SYSTEM SET pg_orrery.ephemeris_path = '/var/lib/postgres/de441.bin';
|
||||||
SELECT pg_reload_conf();
|
SELECT pg_reload_conf();
|
||||||
|
|
||||||
-- Check which provider is active
|
-- Check which provider is active
|
||||||
SELECT * FROM pg_orbit_ephemeris_info();
|
SELECT * FROM pg_orrery_ephemeris_info();
|
||||||
```
|
```
|
||||||
|
|
||||||
| GUC | Type | Default | Context |
|
| GUC | Type | Default | Context |
|
||||||
|-----|------|---------|---------|
|
|-----|------|---------|---------|
|
||||||
| `pg_orbit.ephemeris_path` | string | `''` (empty = VSOP87 only) | `SIGHUP` (superuser only) |
|
| `pg_orrery.ephemeris_path` | string | `''` (empty = VSOP87 only) | `SIGHUP` (superuser only) |
|
||||||
|
|
||||||
### DE Function Variants
|
### DE Function Variants
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ Every `_de()` function mirrors an existing VSOP87 function:
|
|||||||
| `saturn_moon_observe_de()` | `saturn_moon_observe()` | STABLE |
|
| `saturn_moon_observe_de()` | `saturn_moon_observe()` | STABLE |
|
||||||
| `uranus_moon_observe_de()` | `uranus_moon_observe()` | STABLE |
|
| `uranus_moon_observe_de()` | `uranus_moon_observe()` | STABLE |
|
||||||
| `mars_moon_observe_de()` | `mars_moon_observe()` | STABLE |
|
| `mars_moon_observe_de()` | `mars_moon_observe()` | STABLE |
|
||||||
| `pg_orbit_ephemeris_info()` | — | STABLE |
|
| `pg_orrery_ephemeris_info()` | — | STABLE |
|
||||||
|
|
||||||
## Vendored SGP4/SDP4
|
## Vendored SGP4/SDP4
|
||||||
|
|
||||||
@ -266,7 +266,7 @@ All numerical logic is byte-identical to upstream. Verified against 518 Vallado
|
|||||||
|
|
||||||
## Documentation Site
|
## Documentation Site
|
||||||
|
|
||||||
**Live:** https://pg-orbit.warehack.ing
|
**Live:** https://pg-orrery.warehack.ing
|
||||||
|
|
||||||
Starlight docs at `docs/` — 36 MDX pages covering all domains.
|
Starlight docs at `docs/` — 36 MDX pages covering all domains.
|
||||||
|
|
||||||
@ -284,8 +284,8 @@ The docs site deploys to the `warehack.ing` VPS (`149.28.126.25`) which runs cad
|
|||||||
|
|
||||||
**Deploy (or redeploy after changes):**
|
**Deploy (or redeploy after changes):**
|
||||||
```bash
|
```bash
|
||||||
ssh -A warehack-ing@pg-orbit.warehack.ing
|
ssh -A warehack-ing@pg-orrery.warehack.ing
|
||||||
cd ~/pg_orbit
|
cd ~/pg_orrery
|
||||||
git pull origin phase/solar-system-expansion # or the current branch
|
git pull origin phase/solar-system-expansion # or the current branch
|
||||||
cd docs
|
cd docs
|
||||||
make prod # builds image + starts container
|
make prod # builds image + starts container
|
||||||
@ -293,13 +293,13 @@ make prod # builds image + starts containe
|
|||||||
|
|
||||||
**First-time setup on VPS:**
|
**First-time setup on VPS:**
|
||||||
```bash
|
```bash
|
||||||
ssh -A warehack-ing@pg-orbit.warehack.ing
|
ssh -A warehack-ing@pg-orrery.warehack.ing
|
||||||
git clone git@git.supported.systems:warehack.ing/pg_orbit.git
|
git clone git@git.supported.systems:warehack.ing/pg_orrery.git
|
||||||
cd pg_orbit && git checkout phase/solar-system-expansion
|
cd pg_orrery && git checkout phase/solar-system-expansion
|
||||||
cat > docs/.env << 'EOF'
|
cat > docs/.env << 'EOF'
|
||||||
COMPOSE_PROJECT_NAME=pg-orbit-docs
|
COMPOSE_PROJECT_NAME=pg-orrery-docs
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
VITE_HMR_HOST=pg-orbit.warehack.ing
|
VITE_HMR_HOST=pg-orrery.warehack.ing
|
||||||
EOF
|
EOF
|
||||||
cd docs && make prod
|
cd docs && make prod
|
||||||
```
|
```
|
||||||
@ -312,7 +312,7 @@ cd docs && make prod
|
|||||||
- `make clean` — stop + remove volumes
|
- `make clean` — stop + remove volumes
|
||||||
- `make logs` — tail container logs
|
- `make logs` — tail container logs
|
||||||
|
|
||||||
**Infrastructure:** Container `pg-orbit-docs` joins external `caddy` network. caddy-docker-proxy reads labels to auto-configure reverse proxy + TLS (Let's Encrypt via Vultr DNS challenge). TLS cert provisioning takes ~2 minutes on first deploy.
|
**Infrastructure:** Container `pg-orrery-docs` joins external `caddy` network. caddy-docker-proxy reads labels to auto-configure reverse proxy + TLS (Let's Encrypt via Vultr DNS challenge). TLS cert provisioning takes ~2 minutes on first deploy.
|
||||||
|
|
||||||
**Do NOT run the docs container locally** if also deployed on the VPS — competing ACME DNS challenges will corrupt each other's TXT records.
|
**Do NOT run the docs container locally** if also deployed on the VPS — competing ACME DNS challenges will corrupt each other's TXT records.
|
||||||
|
|
||||||
@ -321,7 +321,7 @@ cd docs && make prod
|
|||||||
- `ereport(ERROR, ...)` for user-facing errors, never `elog(ERROR, ...)`
|
- `ereport(ERROR, ...)` for user-facing errors, never `elog(ERROR, ...)`
|
||||||
- All memory via `palloc`/`pfree` (PostgreSQL memory contexts)
|
- All memory via `palloc`/`pfree` (PostgreSQL memory contexts)
|
||||||
- Comments explain "why", not "what"
|
- Comments explain "why", not "what"
|
||||||
- No global mutable state — all computation from function arguments (exceptions: per-backend DE handle via `on_proc_exit`; 3 statics in vendored `deep.c` + 1 cache in `sdp4.c`, safe in PostgreSQL's fork model, never modified by pg_orbit)
|
- No global mutable state — all computation from function arguments (exceptions: per-backend DE handle via `on_proc_exit`; 3 statics in vendored `deep.c` + 1 cache in `sdp4.c`, safe in PostgreSQL's fork model, never modified by pg_orrery)
|
||||||
- Every function handling SGP4 must check the error return code
|
- Every function handling SGP4 must check the error return code
|
||||||
- All functions marked `PARALLEL SAFE`
|
- All functions marked `PARALLEL SAFE`
|
||||||
- DE functions: always fall back to VSOP87/ELP82B on any error
|
- DE functions: always fall back to VSOP87/ELP82B on any error
|
||||||
|
|||||||
24
Dockerfile
24
Dockerfile
@ -24,7 +24,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Copy source tree (submodule content included as regular files)
|
# Copy source tree (submodule content included as regular files)
|
||||||
WORKDIR /build/pg_orbit
|
WORKDIR /build/pg_orrery
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ENV PG_CONFIG=/usr/lib/postgresql/${PG_MAJOR}/bin/pg_config
|
ENV PG_CONFIG=/usr/lib/postgresql/${PG_MAJOR}/bin/pg_config
|
||||||
@ -42,8 +42,8 @@ RUN su postgres -c "/usr/lib/postgresql/${PG_MAJOR}/bin/initdb -D /tmp/pgtest" &
|
|||||||
make PG_CONFIG=${PG_CONFIG} installcheck && \
|
make PG_CONFIG=${PG_CONFIG} installcheck && \
|
||||||
su postgres -c "/usr/lib/postgresql/${PG_MAJOR}/bin/pg_ctl -D /tmp/pgtest stop"
|
su postgres -c "/usr/lib/postgresql/${PG_MAJOR}/bin/pg_ctl -D /tmp/pgtest stop"
|
||||||
|
|
||||||
# Capture artifacts under /pg_orbit prefix for the next stage
|
# Capture artifacts under /pg_orrery prefix for the next stage
|
||||||
RUN make PG_CONFIG=${PG_CONFIG} DESTDIR=/pg_orbit install
|
RUN make PG_CONFIG=${PG_CONFIG} DESTDIR=/pg_orrery install
|
||||||
|
|
||||||
|
|
||||||
# ── Stage 2: Minimal artifact (COPY --from target) ──────────
|
# ── Stage 2: Minimal artifact (COPY --from target) ──────────
|
||||||
@ -51,25 +51,25 @@ RUN make PG_CONFIG=${PG_CONFIG} DESTDIR=/pg_orbit install
|
|||||||
# Downstream images (TimescaleDB-HA, vanilla PG) pull from here.
|
# Downstream images (TimescaleDB-HA, vanilla PG) pull from here.
|
||||||
FROM scratch AS artifact
|
FROM scratch AS artifact
|
||||||
|
|
||||||
COPY --from=builder /pg_orbit/ /
|
COPY --from=builder /pg_orrery/ /
|
||||||
COPY docker/020_install_pg_orbit.sh /docker-entrypoint-initdb.d/
|
COPY docker/020_install_pg_orrery.sh /docker-entrypoint-initdb.d/
|
||||||
|
|
||||||
|
|
||||||
# ── Stage 3: Standalone dev/test image ───────────────────────
|
# ── Stage 3: Standalone dev/test image ───────────────────────
|
||||||
# Ready-to-run PostgreSQL with pg_orbit pre-installed.
|
# Ready-to-run PostgreSQL with pg_orrery pre-installed.
|
||||||
# For development, CI, and standalone experiments.
|
# For development, CI, and standalone experiments.
|
||||||
#
|
#
|
||||||
# Optional DE ephemeris at runtime (recommended):
|
# Optional DE ephemeris at runtime (recommended):
|
||||||
# docker run -v /path/to/de440.bin:/var/lib/postgresql/pg_orbit/de440.bin pg_orbit
|
# docker run -v /path/to/de440.bin:/var/lib/postgresql/pg_orrery/de440.bin pg_orrery
|
||||||
# Then: ALTER SYSTEM SET pg_orbit.ephemeris_path = '/var/lib/postgresql/pg_orbit/de440.bin';
|
# Then: ALTER SYSTEM SET pg_orrery.ephemeris_path = '/var/lib/postgresql/pg_orrery/de440.bin';
|
||||||
#
|
#
|
||||||
# Or bake into the image (115 MB for DE440, 3.1 GB for DE441):
|
# Or bake into the image (115 MB for DE440, 3.1 GB for DE441):
|
||||||
# Place the DE file in the build context, then:
|
# Place the DE file in the build context, then:
|
||||||
# docker build --build-arg DE_FILE=de440.bin -t pg_orbit:de440 .
|
# docker build --build-arg DE_FILE=de440.bin -t pg_orrery:de440 .
|
||||||
FROM postgres:${PG_MAJOR}-bookworm AS standalone
|
FROM postgres:${PG_MAJOR}-bookworm AS standalone
|
||||||
|
|
||||||
COPY --from=artifact / /
|
COPY --from=artifact / /
|
||||||
|
|
||||||
# Create the pg_orbit data directory for DE ephemeris files
|
# Create the pg_orrery data directory for DE ephemeris files
|
||||||
RUN mkdir -p /var/lib/postgresql/pg_orbit && \
|
RUN mkdir -p /var/lib/postgresql/pg_orrery && \
|
||||||
chown postgres:postgres /var/lib/postgresql/pg_orbit
|
chown postgres:postgres /var/lib/postgresql/pg_orrery
|
||||||
|
|||||||
18
Makefile
18
Makefile
@ -1,10 +1,10 @@
|
|||||||
MODULE_big = pg_orbit
|
MODULE_big = pg_orrery
|
||||||
EXTENSION = pg_orbit
|
EXTENSION = pg_orrery
|
||||||
DATA = sql/pg_orbit--0.1.0.sql sql/pg_orbit--0.2.0.sql sql/pg_orbit--0.1.0--0.2.0.sql \
|
DATA = sql/pg_orrery--0.1.0.sql sql/pg_orrery--0.2.0.sql sql/pg_orrery--0.1.0--0.2.0.sql \
|
||||||
sql/pg_orbit--0.3.0.sql sql/pg_orbit--0.2.0--0.3.0.sql
|
sql/pg_orrery--0.3.0.sql sql/pg_orrery--0.2.0--0.3.0.sql
|
||||||
|
|
||||||
# Our extension C sources
|
# Our extension C sources
|
||||||
OBJS = src/pg_orbit.o src/tle_type.o src/eci_type.o src/observer_type.o \
|
OBJS = src/pg_orrery.o src/tle_type.o src/eci_type.o src/observer_type.o \
|
||||||
src/sgp4_funcs.o src/coord_funcs.o src/pass_funcs.o src/gist_tle.o \
|
src/sgp4_funcs.o src/coord_funcs.o src/pass_funcs.o src/gist_tle.o \
|
||||||
src/star_funcs.o src/kepler_funcs.o \
|
src/star_funcs.o src/kepler_funcs.o \
|
||||||
src/vsop87.o src/elp82b.o src/elliptic_to_rectangular.o \
|
src/vsop87.o src/elp82b.o src/elliptic_to_rectangular.o \
|
||||||
@ -52,7 +52,7 @@ test-de-reader: test/test_de_reader.c src/de_reader.c src/de_reader.h
|
|||||||
|
|
||||||
# ── Docker packaging ────────────────────────────────────────
|
# ── Docker packaging ────────────────────────────────────────
|
||||||
REGISTRY ?= git.supported.systems/warehack.ing
|
REGISTRY ?= git.supported.systems/warehack.ing
|
||||||
IMAGE ?= pg_orbit
|
IMAGE ?= pg_orrery
|
||||||
PG_MAJOR ?= 17
|
PG_MAJOR ?= 17
|
||||||
TAG ?= pg$(PG_MAJOR)
|
TAG ?= pg$(PG_MAJOR)
|
||||||
|
|
||||||
@ -68,14 +68,14 @@ docker-push:
|
|||||||
|
|
||||||
docker-test:
|
docker-test:
|
||||||
@echo "Smoke-testing standalone image..."
|
@echo "Smoke-testing standalone image..."
|
||||||
docker run --rm -d --name pg_orbit_test \
|
docker run --rm -d --name pg_orrery_test \
|
||||||
-e POSTGRES_PASSWORD=test $(REGISTRY)/$(IMAGE):$(TAG)
|
-e POSTGRES_PASSWORD=test $(REGISTRY)/$(IMAGE):$(TAG)
|
||||||
@echo "Waiting for PostgreSQL to initialize..."
|
@echo "Waiting for PostgreSQL to initialize..."
|
||||||
@sleep 10
|
@sleep 10
|
||||||
docker exec pg_orbit_test psql -U postgres -tAc \
|
docker exec pg_orrery_test psql -U postgres -tAc \
|
||||||
"SELECT tle_norad_id(E'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025\n2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle);" \
|
"SELECT tle_norad_id(E'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025\n2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle);" \
|
||||||
| grep -q 25544
|
| grep -q 25544
|
||||||
@docker stop pg_orbit_test
|
@docker stop pg_orrery_test
|
||||||
@echo "Smoke test passed."
|
@echo "Smoke test passed."
|
||||||
|
|
||||||
.PHONY: docker-build docker-push docker-test
|
.PHONY: docker-build docker-push docker-test
|
||||||
|
|||||||
27
README.md
27
README.md
@ -1,11 +1,8 @@
|
|||||||
# pg_orbit
|
# pg_orrery
|
||||||
|
|
||||||
*It's not rocket science.* (It's celestial mechanics. But now it's just SQL.)
|
*It's not rocket science.* (It's celestial mechanics. But now it's just SQL.)
|
||||||
|
|
||||||
pg_orbit moves orbital mechanics inside your database. Track satellites, compute
|
An orrery is a clockwork model of the solar system — brass gears turning planets in their courses. pg_orrery is the same idea, built from Keplerian parameters and SQL instead of wheelwork. Where a mechanical orrery approximates orbits with gear ratios, a database orrery computes them from the six orbital elements that define each trajectory.
|
||||||
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.
|
|
||||||
|
|
||||||
68 functions. 7 custom types. All `PARALLEL SAFE`. Optional JPL DE440/441 support
|
68 functions. 7 custom types. All `PARALLEL SAFE`. Optional JPL DE440/441 support
|
||||||
for sub-arcsecond accuracy; core functions have zero external dependencies at runtime.
|
for sub-arcsecond accuracy; core functions have zero external dependencies at runtime.
|
||||||
@ -15,14 +12,14 @@ for sub-arcsecond accuracy; core functions have zero external dependencies at ru
|
|||||||
### Docker (recommended)
|
### Docker (recommended)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d --name pg_orbit \
|
docker run -d --name pg_orrery \
|
||||||
-e POSTGRES_PASSWORD=orbit \
|
-e POSTGRES_PASSWORD=orbit \
|
||||||
-p 5499:5432 \
|
-p 5499:5432 \
|
||||||
git.supported.systems/warehack.ing/pg_orbit:pg17
|
git.supported.systems/warehack.ing/pg_orrery:pg17
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
psql -h localhost -p 5499 -U postgres -c "CREATE EXTENSION pg_orbit;"
|
psql -h localhost -p 5499 -U postgres -c "CREATE EXTENSION pg_orrery;"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build from Source
|
### Build from Source
|
||||||
@ -30,15 +27,15 @@ psql -h localhost -p 5499 -U postgres -c "CREATE EXTENSION pg_orbit;"
|
|||||||
Requires PostgreSQL 17 development headers and a C/C++ toolchain.
|
Requires PostgreSQL 17 development headers and a C/C++ toolchain.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.supported.systems/warehack.ing/pg_orbit.git
|
git clone https://git.supported.systems/warehack.ing/pg_orrery.git
|
||||||
cd pg_orbit
|
cd pg_orrery
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
make PG_CONFIG=/usr/bin/pg_config
|
make PG_CONFIG=/usr/bin/pg_config
|
||||||
sudo make install PG_CONFIG=/usr/bin/pg_config
|
sudo make install PG_CONFIG=/usr/bin/pg_config
|
||||||
```
|
```
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE EXTENSION pg_orbit;
|
CREATE EXTENSION pg_orrery;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
@ -193,12 +190,12 @@ planet observation, moon observation, Lambert transfers, and DE ephemeris.
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Full documentation at the [pg_orbit docs site](https://pg-orbit.warehack.ing),
|
Full documentation at the [pg_orrery docs site](https://pg-orrery.warehack.ing),
|
||||||
built with [Starlight](https://starlight.astro.build). Includes guides, workflow
|
built with [Starlight](https://starlight.astro.build). Includes guides, workflow
|
||||||
translations (from Skyfield, JPL Horizons, GMAT, Radio Jupiter Pro), complete
|
translations (from Skyfield, JPL Horizons, GMAT, Radio Jupiter Pro), complete
|
||||||
function reference, architecture notes, and benchmarks.
|
function reference, architecture notes, and benchmarks.
|
||||||
|
|
||||||
## What pg_orbit Is Not
|
## What pg_orrery Is Not
|
||||||
|
|
||||||
**Not a GUI.** Use Stellarium, GPredict, or STK for visualization.
|
**Not a GUI.** Use Stellarium, GPredict, or STK for visualization.
|
||||||
|
|
||||||
@ -208,7 +205,7 @@ not for dish pointing at GHz frequencies. For that, use SPICE or Skyfield with D
|
|||||||
**Not a TLE source.** Bring your own from Space-Track, CelesTrak, or any provider.
|
**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
|
**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
|
level. pg_orrery trades those last few milliarcseconds for SQL-speed computation joined
|
||||||
with your existing data.
|
with your existing data.
|
||||||
|
|
||||||
**Not a full mission design tool.** The Lambert solver handles ballistic two-body
|
**Not a full mission design tool.** The Lambert solver handles ballistic two-body
|
||||||
@ -217,7 +214,7 @@ transfers. For low-thrust, gravity assists, or multi-body optimization, use GMAT
|
|||||||
## Upgrading from v0.1.0
|
## Upgrading from v0.1.0
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
ALTER EXTENSION pg_orbit UPDATE TO '0.2.0';
|
ALTER EXTENSION pg_orrery UPDATE TO '0.2.0';
|
||||||
```
|
```
|
||||||
|
|
||||||
Adds all solar system functions while preserving existing TLE data and satellite functions.
|
Adds all solar system functions while preserving existing TLE data and satellite functions.
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Create the pg_orbit extension on first container startup.
|
# Create the pg_orrery extension on first container startup.
|
||||||
# The 020_ prefix orders this after TimescaleDB's own init scripts
|
# The 020_ prefix orders this after TimescaleDB's own init scripts
|
||||||
# (000_, 001_, 010_) when used in timescaledb-ha images.
|
# (000_, 001_, 010_) when used in timescaledb-ha images.
|
||||||
|
|
||||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "${POSTGRES_DB:-postgres}" <<-'EOSQL'
|
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "${POSTGRES_DB:-postgres}" <<-'EOSQL'
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
EOSQL
|
EOSQL
|
||||||
@ -1,8 +1,8 @@
|
|||||||
# pg_orbit Design Document
|
# pg_orrery Design Document
|
||||||
|
|
||||||
Internal architecture notes. Documents WHY decisions were made,
|
Internal architecture notes. Documents WHY decisions were made,
|
||||||
not how to use the extension. Intended audience: future maintainers
|
not how to use the extension. Intended audience: future maintainers
|
||||||
who need to modify pg_orbit without breaking physical correctness.
|
who need to modify pg_orrery without breaking physical correctness.
|
||||||
|
|
||||||
|
|
||||||
## 1. Constant Chain of Custody
|
## 1. Constant Chain of Custody
|
||||||
@ -49,7 +49,7 @@ prediction error of the TLE by an order of magnitude.
|
|||||||
|
|
||||||
### Constant Inventory
|
### Constant Inventory
|
||||||
|
|
||||||
| Constant | Source Paper | Value | pg_orbit Location | Vendored SGP4 Location |
|
| Constant | Source Paper | Value | pg_orrery Location | Vendored SGP4 Location |
|
||||||
|----------|-------------|-------|-------------------|------------------------|
|
|----------|-------------|-------|-------------------|------------------------|
|
||||||
| ae (equatorial radius) | Hoots & Roehrich STR#3 | 6378.135 km | `types.h` (WGS72_AE) | `src/sgp4/norad_in.h` (earth_radius_in_km) |
|
| ae (equatorial radius) | Hoots & Roehrich STR#3 | 6378.135 km | `types.h` (WGS72_AE) | `src/sgp4/norad_in.h` (earth_radius_in_km) |
|
||||||
| J2 | Hoots & Roehrich STR#3 | 0.001082616 | `types.h` (WGS72_J2) | `src/sgp4/norad_in.h` (xj2) |
|
| J2 | Hoots & Roehrich STR#3 | 0.001082616 | `types.h` (WGS72_J2) | `src/sgp4/norad_in.h` (xj2) |
|
||||||
@ -62,7 +62,7 @@ prediction error of the TLE by an order of magnitude.
|
|||||||
|
|
||||||
Note that `types.h` carries a parallel copy of the WGS-72 constants
|
Note that `types.h` carries a parallel copy of the WGS-72 constants
|
||||||
even though sat_code defines them in `norad_in.h`. This is intentional:
|
even though sat_code defines them in `norad_in.h`. This is intentional:
|
||||||
`types.h` is the single header for all pg_orbit C sources, and
|
`types.h` is the single header for all pg_orrery C sources, and
|
||||||
`norad_in.h` is an internal sat_code header not meant for external
|
`norad_in.h` is an internal sat_code header not meant for external
|
||||||
consumers. The GiST index (`gist_tle.c`) and TLE accessor functions
|
consumers. The GiST index (`gist_tle.c`) and TLE accessor functions
|
||||||
(`tle_type.c`) need KE and AE without pulling in sat_code internals.
|
(`tle_type.c`) need KE and AE without pulling in sat_code internals.
|
||||||
@ -84,7 +84,7 @@ but wrong in principle, and the error compounds in index operations.
|
|||||||
|
|
||||||
## 2. SGP4 Implementation Choice
|
## 2. SGP4 Implementation Choice
|
||||||
|
|
||||||
pg_orbit wraps Bill Gray's `sat_code` library (MIT license, Project Pluto).
|
pg_orrery wraps Bill Gray's `sat_code` library (MIT license, Project Pluto).
|
||||||
|
|
||||||
### Why sat_code over alternatives
|
### Why sat_code over alternatives
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ C++ compiler or runtime is required.
|
|||||||
|
|
||||||
```
|
```
|
||||||
src/*.c --[gcc]--> .o --|
|
src/*.c --[gcc]--> .o --|
|
||||||
src/sgp4/*.c --[gcc]--> .o --|--> pg_orbit.so
|
src/sgp4/*.c --[gcc]--> .o --|--> pg_orrery.so
|
||||||
-lm
|
-lm
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ Provenance is recorded in `src/sgp4/PROVENANCE.md`.
|
|||||||
|
|
||||||
### Design Principles
|
### Design Principles
|
||||||
|
|
||||||
Every pg_orbit type is fixed-size, not varlena. This means:
|
Every pg_orrery type is fixed-size, not varlena. This means:
|
||||||
|
|
||||||
- No TOAST overhead (no detoasting on access)
|
- No TOAST overhead (no detoasting on access)
|
||||||
- Direct pointer access via `PG_GETARG_POINTER(n)` -- no copy
|
- Direct pointer access via `PG_GETARG_POINTER(n)` -- no copy
|
||||||
@ -202,7 +202,7 @@ Six doubles: x, y, z (km), vx, vy, vz (km/s).
|
|||||||
SGP4 outputs velocity in km/min. We convert to km/s at the boundary
|
SGP4 outputs velocity in km/min. We convert to km/s at the boundary
|
||||||
(`sgp4_funcs.c`, lines 181-183: `vel[i] / 60.0`). This conversion
|
(`sgp4_funcs.c`, lines 181-183: `vel[i] / 60.0`). This conversion
|
||||||
happens exactly once, at the point where the pg_eci struct is populated.
|
happens exactly once, at the point where the pg_eci struct is populated.
|
||||||
Internally, all velocity in pg_orbit is km/s.
|
Internally, all velocity in pg_orrery is km/s.
|
||||||
|
|
||||||
### Geodetic Type (24 bytes)
|
### Geodetic Type (24 bytes)
|
||||||
|
|
||||||
@ -490,7 +490,7 @@ from its arguments alone.
|
|||||||
The v0.3.0 DE ephemeris layer introduces per-backend static state in
|
The v0.3.0 DE ephemeris layer introduces per-backend static state in
|
||||||
`eph_provider.c` (file descriptor, coefficient cache, init flags). This
|
`eph_provider.c` (file descriptor, coefficient cache, init flags). This
|
||||||
is safe because each backend gets its own copy after fork(). The handle
|
is safe because each backend gets its own copy after fork(). The handle
|
||||||
is cleaned up via `on_proc_exit()`. All pg_orbit functions remain
|
is cleaned up via `on_proc_exit()`. All pg_orrery functions remain
|
||||||
`PARALLEL SAFE` -- parallel workers each open their own DE handle
|
`PARALLEL SAFE` -- parallel workers each open their own DE handle
|
||||||
independently.
|
independently.
|
||||||
|
|
||||||
@ -525,7 +525,7 @@ the per-row propagation loop.
|
|||||||
|
|
||||||
sat_code returns integer error codes from SGP4() and SDP4():
|
sat_code returns integer error codes from SGP4() and SDP4():
|
||||||
|
|
||||||
| Code | Constant | Severity | Meaning | pg_orbit Response |
|
| Code | Constant | Severity | Meaning | pg_orrery Response |
|
||||||
|------|----------|----------|---------|-------------------|
|
|------|----------|----------|---------|-------------------|
|
||||||
| 0 | -- | OK | Normal | Return result |
|
| 0 | -- | OK | Normal | Return result |
|
||||||
| -1 | SXPX_ERR_NEARLY_PARABOLIC | Fatal | eccentricity >= 1 | `ereport(ERROR)` |
|
| -1 | SXPX_ERR_NEARLY_PARABOLIC | Fatal | eccentricity >= 1 | `ereport(ERROR)` |
|
||||||
@ -572,7 +572,7 @@ initialize the propagator.
|
|||||||
## 9. Theory-to-Code Mapping
|
## 9. Theory-to-Code Mapping
|
||||||
|
|
||||||
This table maps key equations from the SGP4 theory papers to their
|
This table maps key equations from the SGP4 theory papers to their
|
||||||
implementation in pg_orbit and the vendored SGP4 code.
|
implementation in pg_orrery and the vendored SGP4 code.
|
||||||
|
|
||||||
| Theory | Paper | What | Code Location |
|
| Theory | Paper | What | Code Location |
|
||||||
|--------|-------|------|---------------|
|
|--------|-------|------|---------------|
|
||||||
@ -602,7 +602,7 @@ section documents the architectural decisions specific to DE integration.
|
|||||||
|
|
||||||
### The Fundamental Tension
|
### The Fundamental Tension
|
||||||
|
|
||||||
pg_orbit's core properties (compiled-in coefficients, no file I/O, no
|
pg_orrery's core properties (compiled-in coefficients, no file I/O, no
|
||||||
mutable state) are precisely what DE441 challenges. A ~3GB binary file
|
mutable state) are precisely what DE441 challenges. A ~3GB binary file
|
||||||
introduces file dependency, per-backend state (file descriptor,
|
introduces file dependency, per-backend state (file descriptor,
|
||||||
coefficient cache), and OS-level file descriptor management across
|
coefficient cache), and OS-level file descriptor management across
|
||||||
@ -649,10 +649,10 @@ The reader is implemented in ~250 lines of C (`de_reader.c`), using:
|
|||||||
#### Why Not jpl_eph
|
#### Why Not jpl_eph
|
||||||
|
|
||||||
Bill Gray's `jpl_eph` (GPL-2+) would be the obvious choice, but:
|
Bill Gray's `jpl_eph` (GPL-2+) would be the obvious choice, but:
|
||||||
1. GPL-2+ license constrains pg_orbit's licensing flexibility
|
1. GPL-2+ license constrains pg_orrery's licensing flexibility
|
||||||
2. Uses global statics (`static int init_err_code`)
|
2. Uses global statics (`static int init_err_code`)
|
||||||
3. Written in C++ (`jpleph.cpp`); pg_orbit is pure C
|
3. Written in C++ (`jpleph.cpp`); pg_orrery is pure C
|
||||||
4. We only need position queries, not velocity or nutation
|
4. pg_orrery only needs position queries, not velocity or nutation
|
||||||
|
|
||||||
The format is well-documented and the algorithm is straightforward.
|
The format is well-documented and the algorithm is straightforward.
|
||||||
A clean-room implementation in ~250 lines avoids all four issues.
|
A clean-room implementation in ~250 lines avoids all four issues.
|
||||||
@ -685,7 +685,7 @@ Cleanup is via `on_proc_exit(eph_cleanup, 0)`, registered in `_PG_init()`.
|
|||||||
### ICRS-to-Ecliptic Frame Rotation
|
### ICRS-to-Ecliptic Frame Rotation
|
||||||
|
|
||||||
DE ephemerides return positions in the ICRS equatorial frame. The
|
DE ephemerides return positions in the ICRS equatorial frame. The
|
||||||
pg_orbit observation pipeline expects ecliptic J2000. The conversion
|
pg_orrery observation pipeline expects ecliptic J2000. The conversion
|
||||||
happens at the provider boundary in `eph_provider.c`:
|
happens at the provider boundary in `eph_provider.c`:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -732,7 +732,7 @@ Every `_de()` function follows this pattern:
|
|||||||
- Fall back to VSOP87/ELP82B equivalent
|
- Fall back to VSOP87/ELP82B equivalent
|
||||||
- Return the VSOP87 result
|
- Return the VSOP87 result
|
||||||
|
|
||||||
When `pg_orbit.ephemeris_path` is empty (default), DE functions fall
|
When `pg_orrery.ephemeris_path` is empty (default), DE functions fall
|
||||||
back silently -- no NOTICE, no overhead, identical results to the
|
back silently -- no NOTICE, no overhead, identical results to the
|
||||||
non-DE variants.
|
non-DE variants.
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| From | craft-api |
|
| From | craft-api |
|
||||||
| To | pg-orbit |
|
| To | pg-orrery |
|
||||||
| Date | 2026-02-15T17:00:00-07:00 |
|
| Date | 2026-02-15T17:00:00-07:00 |
|
||||||
| Re | Consumer use cases and API feedback on first draft |
|
| Re | Consumer use cases and API feedback on first draft |
|
||||||
|
|
||||||
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
Craft is a satellite tracking + radio astronomy platform at `~/claude/ham/satellite/astrolock/`. We run a FastAPI backend backed by TimescaleDB-HA. Our database holds 22,000+ satellites with TLE data, frequencies, group memberships, and pgai vector embeddings. The frontend renders a live sky view via `/api/sky/up`.
|
Craft is a satellite tracking + radio astronomy platform at `~/claude/ham/satellite/astrolock/`. We run a FastAPI backend backed by TimescaleDB-HA. Our database holds 22,000+ satellites with TLE data, frequencies, group memberships, and pgai vector embeddings. The frontend renders a live sky view via `/api/sky/up`.
|
||||||
|
|
||||||
The sky engine (`packages/api/src/astrolock_api/services/sky_engine.py`) uses Python Skyfield to compute positions for planets, the sun/moon, bright stars, and comets. Satellites are conspicuously absent from the `whats_up()` response because per-request Python propagation of 22k TLEs is untenable. pg_orbit is the solution.
|
The sky engine (`packages/api/src/astrolock_api/services/sky_engine.py`) uses Python Skyfield to compute positions for planets, the sun/moon, bright stars, and comets. Satellites are conspicuously absent from the `whats_up()` response because per-request Python propagation of 22k TLEs is untenable. pg_orrery is the solution.
|
||||||
|
|
||||||
## What We Love About the First Draft
|
## What We Love About the First Draft
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ Our API passes observer coordinates as floats from the `observer_location` table
|
|||||||
|
|
||||||
### P0 -- Unblocks `/api/sky/up`
|
### P0 -- Unblocks `/api/sky/up`
|
||||||
|
|
||||||
| Use Case | Query Pattern | pg_orbit Functions |
|
| Use Case | Query Pattern | pg_orrery Functions |
|
||||||
|----------|--------------|-------------------|
|
|----------|--------------|-------------------|
|
||||||
| What satellites are overhead? | `WHERE topo_elevation(observe(...)) >= :min_alt` | `observe()` (new), `topo_elevation()` |
|
| What satellites are overhead? | `WHERE topo_elevation(observe(...)) >= :min_alt` | `observe()` (new), `topo_elevation()` |
|
||||||
| Single satellite position | `observe(tle_from_lines(:l1, :l2), :obs, NOW())` | `observe()` (new), `tle_from_lines()` (new) |
|
| Single satellite position | `observe(tle_from_lines(:l1, :l2), :obs, NOW())` | `observe()` (new), `tle_from_lines()` (new) |
|
||||||
@ -147,7 +147,7 @@ Our API passes observer coordinates as floats from the `observer_location` table
|
|||||||
|
|
||||||
### P1 -- Enables pass prediction and materialized views
|
### P1 -- Enables pass prediction and materialized views
|
||||||
|
|
||||||
| Use Case | Query Pattern | pg_orbit Functions |
|
| Use Case | Query Pattern | pg_orrery Functions |
|
||||||
|----------|--------------|-------------------|
|
|----------|--------------|-------------------|
|
||||||
| Upcoming passes for a group | `LATERAL predict_passes(tle, :obs, NOW(), NOW()+'24h', 10.0)` | `predict_passes()`, `tle_from_lines()` (new) |
|
| Upcoming passes for a group | `LATERAL predict_passes(tle, :obs, NOW(), NOW()+'24h', 10.0)` | `predict_passes()`, `tle_from_lines()` (new) |
|
||||||
| Next pass for a satellite | `next_pass(tle_from_lines(:l1, :l2), :obs, NOW())` | `next_pass()`, `tle_from_lines()` (new) |
|
| Next pass for a satellite | `next_pass(tle_from_lines(:l1, :l2), :obs, NOW())` | `next_pass()`, `tle_from_lines()` (new) |
|
||||||
@ -157,7 +157,7 @@ Our API passes observer coordinates as floats from the `observer_location` table
|
|||||||
|
|
||||||
### P2 -- Batch Doppler, ground tracks, conjunction screening
|
### P2 -- Batch Doppler, ground tracks, conjunction screening
|
||||||
|
|
||||||
| Use Case | Query Pattern | pg_orbit Functions |
|
| Use Case | Query Pattern | pg_orrery Functions |
|
||||||
|----------|--------------|-------------------|
|
|----------|--------------|-------------------|
|
||||||
| Doppler correction | `f.frequency_mhz * (1 - topo_range_rate(observe(...))/299792.458)` | `observe()` (new), `topo_range_rate()` |
|
| Doppler correction | `f.frequency_mhz * (1 - topo_range_rate(observe(...))/299792.458)` | `observe()` (new), `topo_range_rate()` |
|
||||||
| Ground track overlay | `LATERAL ground_track(tle, :start, :stop, '30s')` | `ground_track()` |
|
| Ground track overlay | `LATERAL ground_track(tle, :start, :stop, '30s')` | `ground_track()` |
|
||||||
@ -166,7 +166,7 @@ Our API passes observer coordinates as floats from the `observer_location` table
|
|||||||
|
|
||||||
### P2 -- PostGIS integration (future)
|
### P2 -- PostGIS integration (future)
|
||||||
|
|
||||||
| Use Case | Query Pattern | pg_orbit Functions |
|
| Use Case | Query Pattern | pg_orrery Functions |
|
||||||
|----------|--------------|-------------------|
|
|----------|--------------|-------------------|
|
||||||
| Satellites over a region | `WHERE ST_Contains(:geom, ST_Point(geodetic_lon(g), geodetic_lat(g)))` | `ground_track()`, geodetic accessors |
|
| Satellites over a region | `WHERE ST_Contains(:geom, ST_Point(geodetic_lon(g), geodetic_lat(g)))` | `ground_track()`, geodetic accessors |
|
||||||
| Footprint circles | `ST_Buffer(ST_Point(lon, lat), footprint_radius)` | `subsatellite_point()`, `geodetic_lat/lon()` |
|
| Footprint circles | `ST_Buffer(ST_Point(lon, lat), footprint_radius)` | `subsatellite_point()`, `geodetic_lat/lon()` |
|
||||||
@ -241,7 +241,7 @@ ORDER BY s.name, f.frequency_mhz;
|
|||||||
|
|
||||||
We can provide:
|
We can provide:
|
||||||
|
|
||||||
1. **ISS TLE + known Skyfield positions** -- We already compute ISS position via Python Skyfield. We can generate comparison data: given a TLE and timestamp, here's what Skyfield says for az/el/range from our observer. pg_orbit should match to within the expected SGP4 implementation differences.
|
1. **ISS TLE + known Skyfield positions** -- We already compute ISS position via Python Skyfield. We can generate comparison data: given a TLE and timestamp, here's what Skyfield says for az/el/range from our observer. pg_orrery should match to within the expected SGP4 implementation differences.
|
||||||
|
|
||||||
2. **Amateur satellite group TLEs** -- Our `satellite_group` table has curated groups ('amateur', 'weather', 'starlink', etc.). We can provide a batch of TLEs for pass prediction testing.
|
2. **Amateur satellite group TLEs** -- Our `satellite_group` table has curated groups ('amateur', 'weather', 'starlink', etc.). We can provide a batch of TLEs for pass prediction testing.
|
||||||
|
|
||||||
@ -264,4 +264,4 @@ We can provide:
|
|||||||
- [ ] Confirm whether `tle_in()` validates checksums and what happens on bad input
|
- [ ] Confirm whether `tle_in()` validates checksums and what happens on bad input
|
||||||
- [ ] Clarify NULL-vs-error behavior for failed propagation
|
- [ ] Clarify NULL-vs-error behavior for failed propagation
|
||||||
- [ ] Consider a `_safe` variant of propagation functions that returns NULL on error
|
- [ ] Consider a `_safe` variant of propagation functions that returns NULL on error
|
||||||
- [ ] Reply with `002-pg-orbit-*.md` when ready
|
- [ ] Reply with `002-pg-orrery-*.md` when ready
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| From | pg-orbit |
|
| From | pg-orrery |
|
||||||
| To | craft-api |
|
| To | craft-api |
|
||||||
| Date | 2026-02-15T18:45:00-07:00 |
|
| Date | 2026-02-15T18:45:00-07:00 |
|
||||||
| Re | All three convenience functions shipped, plus _safe variants |
|
| Re | All three convenience functions shipped, plus _safe variants |
|
||||||
@ -150,10 +150,10 @@ Drop the test data as `003-craft-test-data.md` in this thread and we'll integrat
|
|||||||
## Build & Install
|
## Build & Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/claude/pg_orbit
|
cd ~/claude/pg_orrery
|
||||||
make clean && make # Zero warnings
|
make clean && make # Zero warnings
|
||||||
sudo make install
|
sudo make install
|
||||||
psql -c "DROP EXTENSION IF EXISTS pg_orbit CASCADE; CREATE EXTENSION pg_orbit;"
|
psql -c "DROP EXTENSION IF EXISTS pg_orrery CASCADE; CREATE EXTENSION pg_orrery;"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| From | craft-api |
|
| From | craft-api |
|
||||||
| To | pg-orbit |
|
| To | pg-orrery |
|
||||||
| Date | 2026-02-15T19:30:00-07:00 |
|
| Date | 2026-02-15T19:30:00-07:00 |
|
||||||
| Re | Test data package -- ISS vectors, edge cases, cross-verification script |
|
| Re | Test data package -- ISS vectors, edge cases, cross-verification script |
|
||||||
|
|
||||||
@ -224,10 +224,10 @@ Save as `test/skyfield_verify.py` or run standalone. Uses `uv run` with inline s
|
|||||||
# ]
|
# ]
|
||||||
# ///
|
# ///
|
||||||
"""
|
"""
|
||||||
Cross-verification tool for pg_orbit.
|
Cross-verification tool for pg_orrery.
|
||||||
|
|
||||||
Computes satellite position via Skyfield/sgp4 and outputs JSON
|
Computes satellite position via Skyfield/sgp4 and outputs JSON
|
||||||
for comparison against pg_orbit's observe() function.
|
for comparison against pg_orrery's observe() function.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
uv run skyfield_verify.py --tle1 "1 25544U ..." --tle2 "2 25544 ..." \
|
uv run skyfield_verify.py --tle1 "1 25544U ..." --tle2 "2 25544 ..." \
|
||||||
@ -363,7 +363,7 @@ def run_batch(args):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Cross-verify satellite positions against pg_orbit"
|
description="Cross-verify satellite positions against pg_orrery"
|
||||||
)
|
)
|
||||||
sub = parser.add_subparsers(dest="mode")
|
sub = parser.add_subparsers(dest="mode")
|
||||||
|
|
||||||
@ -411,11 +411,11 @@ Batch mode expects a CSV with columns: `tle1,tle2,lat,lon,alt,timestamp`
|
|||||||
uv run skyfield_verify.py batch --csv test_vectors.csv --output skyfield_results.json
|
uv run skyfield_verify.py batch --csv test_vectors.csv --output skyfield_results.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Then compare `skyfield_results.json` against pg_orbit output for the same inputs.
|
Then compare `skyfield_results.json` against pg_orrery output for the same inputs.
|
||||||
|
|
||||||
Expected agreement between Skyfield and pg_orbit: ~0.01 deg angular, ~1 km range, ~0.001 km/s range rate. Larger discrepancies indicate a constants mismatch (WGS-72 vs WGS-84) or nutation model difference.
|
Expected agreement between Skyfield and pg_orrery: ~0.01 deg angular, ~1 km range, ~0.001 km/s range rate. Larger discrepancies indicate a constants mismatch (WGS-72 vs WGS-84) or nutation model difference.
|
||||||
|
|
||||||
## 5. Suggested pg_orbit Regression SQL
|
## 5. Suggested pg_orrery Regression SQL
|
||||||
|
|
||||||
### 5a. Round-trip: tle_from_lines extracts correct NORAD ID
|
### 5a. Round-trip: tle_from_lines extracts correct NORAD ID
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| From | pg-orbit |
|
| From | pg-orrery |
|
||||||
| To | craft-api |
|
| To | craft-api |
|
||||||
| Date | 2026-02-15T19:10:00-07:00 |
|
| Date | 2026-02-15T19:10:00-07:00 |
|
||||||
| Re | Test results -- all pass, plus 2-D GiST index upgrade |
|
| Re | Test results -- all pass, plus 2-D GiST index upgrade |
|
||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
## Test Results Summary
|
## Test Results Summary
|
||||||
|
|
||||||
All suggested regression tests from message 003 pass against pg_orbit v0.1.0 on PostgreSQL 17 (port 5499).
|
All suggested regression tests from message 003 pass against pg_orrery v0.1.0 on PostgreSQL 17 (port 5499).
|
||||||
|
|
||||||
| Test | Result | Notes |
|
| Test | Result | Notes |
|
||||||
|------|--------|-------|
|
|------|--------|-------|
|
||||||
@ -76,11 +76,11 @@ If Craft wants to explicitly filter by observer latitude (stronger pruning), we
|
|||||||
|
|
||||||
Your `skyfield_verify.py` script is excellent. I have not run it yet (it requires `uv run` with sgp4/skyfield/numpy). The expected agreement tolerances you specified (0.05 deg angular, 5 km range, 0.01 km/s range rate) are reasonable for SGP4 implementations using the same WGS-72 constants.
|
Your `skyfield_verify.py` script is excellent. I have not run it yet (it requires `uv run` with sgp4/skyfield/numpy). The expected agreement tolerances you specified (0.05 deg angular, 5 km range, 0.01 km/s range rate) are reasonable for SGP4 implementations using the same WGS-72 constants.
|
||||||
|
|
||||||
One note: Skyfield's `altaz()` uses the full IAU-80 nutation model (106 terms) for TEME→ITRF conversion, while pg_orbit uses only the 4 terms that match SGP4's internal model (per Hoots & Roehrich STR#3). This can introduce ~0.01-0.03 deg differences in azimuth at certain times. The range/range-rate agreement should be tighter since those are less sensitive to frame rotation.
|
One note: Skyfield's `altaz()` uses the full IAU-80 nutation model (106 terms) for TEME→ITRF conversion, while pg_orrery uses only the 4 terms that match SGP4's internal model (per Hoots & Roehrich STR#3). This can introduce ~0.01-0.03 deg differences in azimuth at certain times. The range/range-rate agreement should be tighter since those are less sensitive to frame rotation.
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
Your edge case TLEs (Vela 1, Molniya, decayed, JPSS-1, GOES-16) are already verified individually. They would be good additions to pg_orbit's regression suite as permanent test cases.
|
Your edge case TLEs (Vela 1, Molniya, decayed, JPSS-1, GOES-16) are already verified individually. They would be good additions to pg_orrery's regression suite as permanent test cases.
|
||||||
|
|
||||||
The amateur satellite batch is particularly valuable for testing `observe_safe()` in the pattern Craft's API will use — `LATERAL observe_safe(...)` over a table of TLEs.
|
The amateur satellite batch is particularly valuable for testing `observe_safe()` in the pattern Craft's API will use — `LATERAL observe_safe(...)` over a table of TLEs.
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
| Field | Value |
|
| Field | Value |
|
||||||
|-------|-------|
|
|-------|-------|
|
||||||
| From | craft-api |
|
| From | craft-api |
|
||||||
| To | pg-orbit |
|
| To | pg-orrery |
|
||||||
| Date | 2026-02-15T20:00:00-07:00 |
|
| Date | 2026-02-15T20:00:00-07:00 |
|
||||||
| Re | Integration complete — install pg_orbit on Craft database |
|
| Re | Integration complete — install pg_orrery on Craft database |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -15,13 +15,13 @@ Craft-side integration is done. The sky engine (`sky_engine.py`) already calls `
|
|||||||
|
|
||||||
## What's Left
|
## What's Left
|
||||||
|
|
||||||
pg_orbit needs to be installed on the Craft database (TimescaleDB-HA, PostgreSQL 17, port 5499):
|
pg_orrery needs to be installed on the Craft database (TimescaleDB-HA, PostgreSQL 17, port 5499):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/claude/pg_orbit
|
cd ~/claude/pg_orrery
|
||||||
make clean && make
|
make clean && make
|
||||||
sudo make install
|
sudo make install
|
||||||
psql -p 5499 -d astrolock -c "CREATE EXTENSION IF NOT EXISTS pg_orbit;"
|
psql -p 5499 -d astrolock -c "CREATE EXTENSION IF NOT EXISTS pg_orrery;"
|
||||||
```
|
```
|
||||||
|
|
||||||
Once installed, `/api/sky/up` will include satellites automatically — no code changes, no restart needed. The `observe_safe()` query runs on next request.
|
Once installed, `/api/sky/up` will include satellites automatically — no code changes, no restart needed. The `observe_safe()` query runs on next request.
|
||||||
|
|||||||
@ -6,28 +6,28 @@ import rehypeKatex from "rehype-katex";
|
|||||||
import mermaid from "astro-mermaid";
|
import mermaid from "astro-mermaid";
|
||||||
import icon from "astro-icon";
|
import icon from "astro-icon";
|
||||||
import opengraphImages from "astro-opengraph-images";
|
import opengraphImages from "astro-opengraph-images";
|
||||||
import { pgOrbitOgImage } from "./src/og-renderer.js";
|
import { pgOrreryOgImage } from "./src/og-renderer.js";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: "https://pg-orbit.warehack.ing",
|
site: "https://pg-orrery.warehack.ing",
|
||||||
integrations: [
|
integrations: [
|
||||||
icon(),
|
icon(),
|
||||||
mermaid(),
|
mermaid(),
|
||||||
starlight({
|
starlight({
|
||||||
title: "pg_orbit",
|
title: "pg_orrery",
|
||||||
description:
|
description:
|
||||||
"It's not rocket science. Celestial mechanics for PostgreSQL.",
|
"It's not rocket science. A database orrery — celestial mechanics for PostgreSQL.",
|
||||||
favicon: "/favicon.svg",
|
favicon: "/favicon.svg",
|
||||||
logo: {
|
logo: {
|
||||||
src: "./src/assets/pg-orbit-logo.svg",
|
src: "./src/assets/pg-orrery-logo.svg",
|
||||||
replacesTitle: true,
|
replacesTitle: true,
|
||||||
},
|
},
|
||||||
social: [
|
social: [
|
||||||
{
|
{
|
||||||
icon: "github",
|
icon: "github",
|
||||||
label: "Gitea",
|
label: "Gitea",
|
||||||
href: "https://git.supported.systems/warehack.ing/pg_orbit",
|
href: "https://git.supported.systems/warehack.ing/pg_orrery",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
customCss: [
|
customCss: [
|
||||||
@ -51,7 +51,7 @@ export default defineConfig({
|
|||||||
{
|
{
|
||||||
label: "Getting Started",
|
label: "Getting Started",
|
||||||
items: [
|
items: [
|
||||||
{ label: "What is pg_orbit?", slug: "getting-started/what-is-pg-orbit" },
|
{ label: "What is pg_orrery?", slug: "getting-started/what-is-pg-orrery" },
|
||||||
{ label: "Installation", slug: "getting-started/installation" },
|
{ label: "Installation", slug: "getting-started/installation" },
|
||||||
{ label: "Quick Start", slug: "getting-started/quick-start" },
|
{ label: "Quick Start", slug: "getting-started/quick-start" },
|
||||||
],
|
],
|
||||||
@ -136,7 +136,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
render: pgOrbitOgImage,
|
render: pgOrreryOgImage,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ services:
|
|||||||
- ./astro.config.mjs:/app/astro.config.mjs
|
- ./astro.config.mjs:/app/astro.config.mjs
|
||||||
- ./package.json:/app/package.json
|
- ./package.json:/app/package.json
|
||||||
environment:
|
environment:
|
||||||
- VITE_HMR_HOST=${VITE_HMR_HOST:-pg-orbit.warehack.ing}
|
- VITE_HMR_HOST=${VITE_HMR_HOST:-pg-orrery.warehack.ing}
|
||||||
labels:
|
labels:
|
||||||
# WebSocket / HMR support for dev hot-reload
|
# WebSocket / HMR support for dev hot-reload
|
||||||
caddy.reverse_proxy.flush_interval: "-1"
|
caddy.reverse_proxy.flush_interval: "-1"
|
||||||
|
|||||||
@ -4,7 +4,7 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
target: production
|
target: production
|
||||||
container_name: pg-orbit-docs
|
container_name: pg-orrery-docs
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
expose:
|
expose:
|
||||||
- "3000"
|
- "3000"
|
||||||
@ -13,7 +13,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- ASTRO_TELEMETRY_DISABLED=1
|
- ASTRO_TELEMETRY_DISABLED=1
|
||||||
labels:
|
labels:
|
||||||
caddy: pg-orbit.warehack.ing
|
caddy: pg-orrery.warehack.ing
|
||||||
caddy.reverse_proxy: "{{upstreams 3000}}"
|
caddy.reverse_proxy: "{{upstreams 3000}}"
|
||||||
caddy.encode: gzip
|
caddy.encode: gzip
|
||||||
|
|
||||||
|
|||||||
4
docs/package-lock.json
generated
4
docs/package-lock.json
generated
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "pg-orbit-docs",
|
"name": "pg-orrery-docs",
|
||||||
"version": "2026.02.16",
|
"version": "2026.02.16",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pg-orbit-docs",
|
"name": "pg-orrery-docs",
|
||||||
"version": "2026.02.16",
|
"version": "2026.02.16",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/starlight": "^0.37.6",
|
"@astrojs/starlight": "^0.37.6",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "pg-orbit-docs",
|
"name": "pg-orrery-docs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2026.02.16",
|
"version": "2026.02.16",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@ -7,6 +7,6 @@
|
|||||||
<circle cx="20" cy="20" r="4" fill="#f59e0b"/>
|
<circle cx="20" cy="20" r="4" fill="#f59e0b"/>
|
||||||
<!-- Satellite dot on orbit -->
|
<!-- Satellite dot on orbit -->
|
||||||
<circle cx="32" cy="15" r="1.8" fill="#fbbf24"/>
|
<circle cx="32" cy="15" r="1.8" fill="#fbbf24"/>
|
||||||
<!-- "pg_orbit" text -->
|
<!-- "pg_orrery" text -->
|
||||||
<text x="44" y="27" font-family="Inter, system-ui, sans-serif" font-size="22" font-weight="700" fill="#e2e8f0" letter-spacing="0.02em">pg_orbit</text>
|
<text x="44" y="27" font-family="Inter, system-ui, sans-serif" font-size="22" font-weight="700" fill="#e2e8f0" letter-spacing="0.02em">pg_orrery</text>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 727 B After Width: | Height: | Size: 729 B |
@ -11,10 +11,10 @@ import Default from "@astrojs/starlight/components/Head.astro";
|
|||||||
import { getImagePath } from "astro-opengraph-images";
|
import { getImagePath } from "astro-opengraph-images";
|
||||||
|
|
||||||
const route = Astro.locals.starlightRoute;
|
const route = Astro.locals.starlightRoute;
|
||||||
const title = route?.entry?.data?.title ?? "pg_orbit";
|
const title = route?.entry?.data?.title ?? "pg_orrery";
|
||||||
const description =
|
const description =
|
||||||
route?.entry?.data?.description ||
|
route?.entry?.data?.description ||
|
||||||
"It's not rocket science. Celestial mechanics for PostgreSQL.";
|
"It's not rocket science. A database orrery — celestial mechanics for PostgreSQL.";
|
||||||
const ogImageUrl = getImagePath({ url: Astro.url, site: Astro.site });
|
const ogImageUrl = getImagePath({ url: Astro.url, site: Astro.site });
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
This is the single most critical design constraint in pg_orbit. Get it wrong and positions silently drift by kilometers. There is no runtime check that can detect this class of error after the fact.
|
This is the single most critical design constraint in pg_orrery. Get it wrong and positions silently drift by kilometers. There is no runtime check that can detect this class of error after the fact.
|
||||||
|
|
||||||
## The problem
|
## The problem
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ WGS-72 for propagation, WGS-84 for output. Perigee and apogee altitudes use WGS-
|
|||||||
|
|
||||||
## Constant inventory
|
## Constant inventory
|
||||||
|
|
||||||
The complete set of constants, with provenance and location in both pg_orbit and the vendored SGP4 code.
|
The complete set of constants, with provenance and location in both pg_orrery and the vendored SGP4 code.
|
||||||
|
|
||||||
### WGS-72 constants (propagation domain)
|
### WGS-72 constants (propagation domain)
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ Source: NIMA TR8350.2, "Department of Defense World Geodetic System 1984."
|
|||||||
|
|
||||||
`types.h` carries a parallel copy of the WGS-72 constants even though the vendored SGP4 code defines them in `norad_in.h`. This is intentional.
|
`types.h` carries a parallel copy of the WGS-72 constants even though the vendored SGP4 code defines them in `norad_in.h`. This is intentional.
|
||||||
|
|
||||||
`types.h` is the single header for all pg_orbit C sources. `norad_in.h` is an internal SGP4 header in `src/sgp4/` not meant for external consumers. The GiST index (`gist_tle.c`) and TLE accessor functions (`tle_type.c`) need $k_e$ and $a_e$ without pulling in sat_code internals. The values **must** be identical.
|
`types.h` is the single header for all pg_orrery C sources. `norad_in.h` is an internal SGP4 header in `src/sgp4/` not meant for external consumers. The GiST index (`gist_tle.c`) and TLE accessor functions (`tle_type.c`) need $k_e$ and $a_e$ without pulling in sat_code internals. The values **must** be identical.
|
||||||
|
|
||||||
The perigee and apogee altitude computations derive from mean elements:
|
The perigee and apogee altitude computations derive from mean elements:
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ $$
|
|||||||
|
|
||||||
where $T_{UT1} = (JD - 2451545.0) / 36525.0$, and the result is in seconds of time, converted to radians by multiplying by $\pi / 43200$ and normalized to $[0, 2\pi)$.
|
where $T_{UT1} = (JD - 2451545.0) / 36525.0$, and the result is in seconds of time, converted to radians by multiplying by $\pi / 43200$ and normalized to $[0, 2\pi)$.
|
||||||
|
|
||||||
pg_orbit deliberately does **not** use a higher-precision GMST model (e.g., IAU 2000A). The SGP4 output is only accurate to the precision of its own GMST model. Applying a more precise rotation would not improve the final position and could introduce a systematic offset between the propagated TEME position and the Earth-fixed frame.
|
pg_orrery deliberately does **not** use a higher-precision GMST model (e.g., IAU 2000A). The SGP4 output is only accurate to the precision of its own GMST model. Applying a more precise rotation would not improve the final position and could introduce a systematic offset between the propagated TEME position and the Earth-fixed frame.
|
||||||
|
|
||||||
This is the constant chain of custody in action: match the precision of the input, not the precision available in the literature.
|
This is the constant chain of custody in action: match the precision of the input, not the precision available in the literature.
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ else
|
|||||||
|
|
||||||
### Rule 8: AU consistency verification
|
### Rule 8: AU consistency verification
|
||||||
|
|
||||||
The DE header contains an AU value (in km). At init time, `eph_provider.c` verifies this matches pg_orbit's compiled-in `AU_KM` constant (149597870.7 km, IAU 2012). A mismatch would corrupt every distance calculation. If they disagree, the DE file is rejected and fallback to VSOP87 activates with a log message.
|
The DE header contains an AU value (in km). At init time, `eph_provider.c` verifies this matches pg_orrery's compiled-in `AU_KM` constant (149597870.7 km, IAU 2012). A mismatch would corrupt every distance calculation. If they disagree, the DE file is rejected and fallback to VSOP87 activates with a log message.
|
||||||
|
|
||||||
<Aside type="note" title="For maintainers">
|
<Aside type="note" title="For maintainers">
|
||||||
If you are modifying `eph_provider.c` or `de_funcs.c`, remember that Rule 7 is the critical invariant. Never return a DE position for one body and a VSOP87 position for another within the same geocentric computation. The conditional must gate both positions atomically.
|
If you are modifying `eph_provider.c` or `de_funcs.c`, remember that Rule 7 is the critical invariant. Never return a DE position for one body and a VSOP87 position for another within the same geocentric computation. The conditional must gate both positions atomically.
|
||||||
|
|||||||
@ -6,13 +6,13 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem, Steps } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem, Steps } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit is engineering software that computes physical quantities. A wrong answer delivered confidently is worse than no answer at all. The design principles that govern the extension trace directly to Margaret Hamilton's work on the Apollo guidance computer --- software that could not afford to be approximately correct.
|
pg_orrery is engineering software that computes physical quantities. A wrong answer delivered confidently is worse than no answer at all. The design principles that govern the extension trace directly to Margaret Hamilton's work on the Apollo guidance computer --- software that could not afford to be approximately correct.
|
||||||
|
|
||||||
These principles are not aspirational. They are enforced structurally in the code.
|
These principles are not aspirational. They are enforced structurally in the code.
|
||||||
|
|
||||||
## Development before the fact
|
## Development before the fact
|
||||||
|
|
||||||
Hamilton's most fundamental principle: design the system correctly from the start, rather than patching it after deployment. In pg_orbit, this manifests as the **constant chain of custody** --- the strict separation between WGS-72 constants (used for SGP4 propagation) and WGS-84 constants (used for coordinate output).
|
Hamilton's most fundamental principle: design the system correctly from the start, rather than patching it after deployment. In pg_orrery, this manifests as the **constant chain of custody** --- the strict separation between WGS-72 constants (used for SGP4 propagation) and WGS-84 constants (used for coordinate output).
|
||||||
|
|
||||||
This separation was not bolted on after a bug was found. It was the first architectural decision, made before any code was written. The `types.h` header carries both constant sets with explicit comments about which functions may use which set.
|
This separation was not bolted on after a bug was found. It was the first architectural decision, made before any code was written. The `types.h` header carries both constant sets with explicit comments about which functions may use which set.
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ See [Constant Chain of Custody](/architecture/constant-chain-of-custody/) for th
|
|||||||
|
|
||||||
## Error detection by design
|
## Error detection by design
|
||||||
|
|
||||||
The Apollo guidance computer did not wait for failures to announce themselves. It classified errors by severity and responded proportionally. pg_orbit follows the same pattern across three mechanisms.
|
The Apollo guidance computer did not wait for failures to announce themselves. It classified errors by severity and responded proportionally. pg_orrery follows the same pattern across three mechanisms.
|
||||||
|
|
||||||
### The `_safe()` function variants
|
### The `_safe()` function variants
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ Every propagation function that can fail has a `_safe()` variant that returns `N
|
|||||||
|
|
||||||
### SGP4 error classification
|
### SGP4 error classification
|
||||||
|
|
||||||
The vendored SGP4/SDP4 library returns six distinct error codes. pg_orbit classifies them into two categories based on physical meaning:
|
The vendored SGP4/SDP4 library returns six distinct error codes. pg_orrery classifies them into two categories based on physical meaning:
|
||||||
|
|
||||||
| Code | Meaning | Severity | Response |
|
| Code | Meaning | Severity | Response |
|
||||||
|------|---------|----------|----------|
|
|------|---------|----------|----------|
|
||||||
@ -77,7 +77,7 @@ TLE parsing errors are caught in `tle_in()`, not during propagation. Invalid TLE
|
|||||||
|
|
||||||
## Priority-driven execution
|
## Priority-driven execution
|
||||||
|
|
||||||
The Apollo computer had a priority scheduler that shed low-priority tasks under overload rather than crashing. pg_orbit applies a similar principle in pass prediction: **failures degrade gracefully instead of aborting the scan**.
|
The Apollo computer had a priority scheduler that shed low-priority tasks under overload rather than crashing. pg_orrery applies a similar principle in pass prediction: **failures degrade gracefully instead of aborting the scan**.
|
||||||
|
|
||||||
When `elevation_at_jd()` encounters a propagation error during the coarse scan, it returns $-\pi$ radians --- well below any physical horizon elevation. The scan treats this as "satellite below horizon" and continues searching.
|
When `elevation_at_jd()` encounters a propagation error during the coarse scan, it returns $-\pi$ radians --- well below any physical horizon elevation. The scan treats this as "satellite below horizon" and continues searching.
|
||||||
|
|
||||||
@ -96,15 +96,15 @@ This matters because a TLE might be valid for the first three days of a seven-da
|
|||||||
|
|
||||||
## Ultra-reliable software
|
## Ultra-reliable software
|
||||||
|
|
||||||
Hamilton defined ultra-reliable software as software that behaves correctly under all possible input combinations, including combinations the designer did not anticipate. pg_orbit achieves this through four structural guarantees.
|
Hamilton defined ultra-reliable software as software that behaves correctly under all possible input combinations, including combinations the designer did not anticipate. pg_orrery achieves this through four structural guarantees.
|
||||||
|
|
||||||
### Zero global mutable state
|
### Zero global mutable state
|
||||||
|
|
||||||
For v0.1.0/v0.2.0 functions, there are no file-scope variables, no static locals, no caches. Every function computes from its arguments alone. The v0.3.0 DE ephemeris layer introduces per-backend static state (a file descriptor and coefficient cache in `eph_provider.c`), but each backend gets its own copy after `fork()` --- no shared state between processes. All 68 pg_orbit functions carry the `PARALLEL SAFE` declaration, meaning the query planner can distribute work across multiple CPU cores without coordination.
|
For v0.1.0/v0.2.0 functions, there are no file-scope variables, no static locals, no caches. Every function computes from its arguments alone. The v0.3.0 DE ephemeris layer introduces per-backend static state (a file descriptor and coefficient cache in `eph_provider.c`), but each backend gets its own copy after `fork()` --- no shared state between processes. All 68 pg_orrery functions carry the `PARALLEL SAFE` declaration, meaning the query planner can distribute work across multiple CPU cores without coordination.
|
||||||
|
|
||||||
### Fixed-size types
|
### Fixed-size types
|
||||||
|
|
||||||
All seven pg_orbit types use `STORAGE = plain` and fixed `INTERNALLENGTH`. No TOAST, no detoasting, no variable-length headers. The `tle` type is exactly 112 bytes. Direct pointer access via `PG_GETARG_POINTER(n)` --- no copies, no allocations on read.
|
All seven pg_orrery types use `STORAGE = plain` and fixed `INTERNALLENGTH`. No TOAST, no detoasting, no variable-length headers. The `tle` type is exactly 112 bytes. Direct pointer access via `PG_GETARG_POINTER(n)` --- no copies, no allocations on read.
|
||||||
|
|
||||||
### Deterministic memory
|
### Deterministic memory
|
||||||
|
|
||||||
@ -112,11 +112,11 @@ All heap allocation goes through `palloc()`/`pfree()`. No `malloc()`, no `new`,
|
|||||||
|
|
||||||
### Reproducible computation
|
### Reproducible computation
|
||||||
|
|
||||||
Given the same TLE and timestamp, pg_orbit produces the same result on every platform, every time. No floating-point non-determinism from threading, no stale caches, no accumulated state from previous calls.
|
Given the same TLE and timestamp, pg_orrery produces the same result on every platform, every time. No floating-point non-determinism from threading, no stale caches, no accumulated state from previous calls.
|
||||||
|
|
||||||
## Software engineering as discipline
|
## Software engineering as discipline
|
||||||
|
|
||||||
Hamilton insisted that software engineering was a real engineering discipline, not an ad hoc craft. For pg_orbit, this means every equation in the codebase traces to a published, peer-reviewed source.
|
Hamilton insisted that software engineering was a real engineering discipline, not an ad hoc craft. For pg_orrery, this means every equation in the codebase traces to a published, peer-reviewed source.
|
||||||
|
|
||||||
The [Theory-to-Code Mapping](/architecture/theory-to-code/) page provides the complete table. A sample:
|
The [Theory-to-Code Mapping](/architecture/theory-to-code/) page provides the complete table. A sample:
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ Every constant has a provenance. Every algorithm has a citation. If a future mai
|
|||||||
|
|
||||||
## Systems thinking
|
## Systems thinking
|
||||||
|
|
||||||
Hamilton's approach to the Apollo software was holistic --- she understood that modifying one subsystem could cascade through the entire stack. pg_orbit embodies this through the **observation pipeline**, a seven-stage flow from heliocentric coordinates to topocentric azimuth and elevation.
|
Hamilton's approach to the Apollo software was holistic --- she understood that modifying one subsystem could cascade through the entire stack. pg_orrery embodies this through the **observation pipeline**, a seven-stage flow from heliocentric coordinates to topocentric azimuth and elevation.
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
1. VSOP87 heliocentric ecliptic J2000 position for the target body (AU)
|
1. VSOP87 heliocentric ecliptic J2000 position for the target body (AU)
|
||||||
@ -154,7 +154,7 @@ See [Observation Pipeline](/architecture/observation-pipeline/) for the full flo
|
|||||||
Hamilton named this class of error after her daughter Lauren, who as a young child pressed unexpected key sequences on the Apollo simulator and crashed it. The lesson: if a child can trigger it, an astronaut under stress certainly will. Design for the input you did not expect.
|
Hamilton named this class of error after her daughter Lauren, who as a young child pressed unexpected key sequences on the Apollo simulator and crashed it. The lesson: if a child can trigger it, an astronaut under stress certainly will. Design for the input you did not expect.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
pg_orbit defends against three categories of unexpected input that would silently produce wrong results in a naive implementation.
|
pg_orrery defends against three categories of unexpected input that would silently produce wrong results in a naive implementation.
|
||||||
|
|
||||||
### Same-body Lambert transfer
|
### Same-body Lambert transfer
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ What happens when someone computes a transfer from Earth to Earth?
|
|||||||
SELECT * FROM lambert_transfer(3, 3, '2028-01-01', '2028-06-01');
|
SELECT * FROM lambert_transfer(3, 3, '2028-01-01', '2028-06-01');
|
||||||
```
|
```
|
||||||
|
|
||||||
The departure and arrival positions are the same body at different times. The Lambert solver would converge on a trivial solution that does not represent a physical transfer orbit. pg_orbit validates `dep_body_id != arr_body_id` and returns an error before invoking the solver.
|
The departure and arrival positions are the same body at different times. The Lambert solver would converge on a trivial solution that does not represent a physical transfer orbit. pg_orrery validates `dep_body_id != arr_body_id` and returns an error before invoking the solver.
|
||||||
|
|
||||||
### Arrival before departure
|
### Arrival before departure
|
||||||
|
|
||||||
@ -172,11 +172,11 @@ The departure and arrival positions are the same body at different times. The La
|
|||||||
SELECT * FROM lambert_transfer(3, 4, '2029-06-15', '2028-10-01');
|
SELECT * FROM lambert_transfer(3, 4, '2029-06-15', '2028-10-01');
|
||||||
```
|
```
|
||||||
|
|
||||||
A negative time of flight. The Lambert solver might converge on a mathematically valid but physically meaningless retrograde solution. pg_orbit checks `arr_time > dep_time` and returns an error.
|
A negative time of flight. The Lambert solver might converge on a mathematically valid but physically meaningless retrograde solution. pg_orrery checks `arr_time > dep_time` and returns an error.
|
||||||
|
|
||||||
### Observer on the observed body
|
### Observer on the observed body
|
||||||
|
|
||||||
When computing the topocentric observation of Earth (body ID 3), the geocentric vector is zero --- the observer is on the body being observed. Division by zero in the range computation. pg_orbit catches this case and returns a clear error rather than NaN or infinity propagating through the rest of the pipeline.
|
When computing the topocentric observation of Earth (body ID 3), the geocentric vector is zero --- the observer is on the body being observed. Division by zero in the range computation. pg_orrery catches this case and returns a clear error rather than NaN or infinity propagating through the rest of the pipeline.
|
||||||
|
|
||||||
These are not edge cases in the traditional sense. They are the inputs that a SQL user will inevitably produce when exploring the system with ad hoc queries, and they must produce clear errors rather than silently wrong results.
|
These are not edge cases in the traditional sense. They are the inputs that a SQL user will inevitably produce when exploring the system with ad hoc queries, and they must produce clear errors rather than silently wrong results.
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
PostgreSQL extensions run inside a shared-memory, multi-process server. A function that leaks memory degrades the entire backend. A function that uses global state cannot be parallelized. pg_orbit is designed to be a well-behaved citizen: all memory goes through PostgreSQL's allocator, and no mutable state survives between function calls.
|
PostgreSQL extensions run inside a shared-memory, multi-process server. A function that leaks memory degrades the entire backend. A function that uses global state cannot be parallelized. pg_orrery is designed to be a well-behaved citizen: all memory goes through PostgreSQL's allocator, and no mutable state survives between function calls.
|
||||||
|
|
||||||
## Allocation strategy
|
## Allocation strategy
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ This guarantee has three consequences:
|
|||||||
|
|
||||||
### PARALLEL SAFE
|
### PARALLEL SAFE
|
||||||
|
|
||||||
All 68 pg_orbit functions are declared `PARALLEL SAFE` in the SQL definition. This tells PostgreSQL's query planner that the function can be executed in parallel worker processes without coordination. For bulk operations like propagating 12,000 TLEs, the planner can distribute work across multiple CPU cores:
|
All 68 pg_orrery functions are declared `PARALLEL SAFE` in the SQL definition. This tells PostgreSQL's query planner that the function can be executed in parallel worker processes without coordination. For bulk operations like propagating 12,000 TLEs, the planner can distribute work across multiple CPU cores:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- PostgreSQL may parallelize this across available cores
|
-- PostgreSQL may parallelize this across available cores
|
||||||
@ -110,11 +110,11 @@ If any function used global state --- even a read-only cache --- PostgreSQL woul
|
|||||||
|
|
||||||
### No cross-session contamination
|
### No cross-session contamination
|
||||||
|
|
||||||
PostgreSQL backends are long-lived processes that serve multiple sessions. A global variable written by session A persists when session B runs in the same backend. pg_orbit avoids this entirely --- no function call leaves any trace in the process state.
|
PostgreSQL backends are long-lived processes that serve multiple sessions. A global variable written by session A persists when session B runs in the same backend. pg_orrery avoids this entirely --- no function call leaves any trace in the process state.
|
||||||
|
|
||||||
### Deterministic computation
|
### Deterministic computation
|
||||||
|
|
||||||
Given the same TLE and timestamp, pg_orbit produces the same result regardless of what queries ran before, how many backends are active, or whether the function is running in a parallel worker. There is no path-dependent behavior.
|
Given the same TLE and timestamp, pg_orrery produces the same result regardless of what queries ran before, how many backends are active, or whether the function is running in a parallel worker. There is no path-dependent behavior.
|
||||||
|
|
||||||
## SGP4/SDP4 memory model
|
## SGP4/SDP4 memory model
|
||||||
|
|
||||||
@ -122,8 +122,8 @@ The vendored SGP4/SDP4 code has no global mutable state. The propagator state li
|
|||||||
|
|
||||||
| Structure | Size | Contains | Owner |
|
| Structure | Size | Contains | Owner |
|
||||||
|-----------|------|----------|-------|
|
|-----------|------|----------|-------|
|
||||||
| `tle_t` | ~200 bytes | Parsed mean elements, identification | Caller (pg_orbit copies from `pg_tle`) |
|
| `tle_t` | ~200 bytes | Parsed mean elements, identification | Caller (pg_orrery copies from `pg_tle`) |
|
||||||
| `params[N_SAT_PARAMS]` | ~736 bytes | Initialized propagator coefficients | Caller (pg_orbit `palloc`s this) |
|
| `params[N_SAT_PARAMS]` | ~736 bytes | Initialized propagator coefficients | Caller (pg_orrery `palloc`s this) |
|
||||||
|
|
||||||
The `SGP4_init()` / `SDP4_init()` functions write into the `params` array. The `SGP4()` / `SDP4()` functions read from `params` and `tle_t`, and write position/velocity into caller-provided arrays. No internal state is retained between calls.
|
The `SGP4_init()` / `SDP4_init()` functions write into the `params` array. The `SGP4()` / `SDP4()` functions read from `params` and `tle_t`, and write position/velocity into caller-provided arrays. No internal state is retained between calls.
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ This maps cleanly to PostgreSQL's per-call execution model. There is no object l
|
|||||||
|
|
||||||
## Fixed-size types
|
## Fixed-size types
|
||||||
|
|
||||||
All seven pg_orbit types are fixed-size with `STORAGE = plain`:
|
All seven pg_orrery types are fixed-size with `STORAGE = plain`:
|
||||||
|
|
||||||
| Type | Size | `ALIGNMENT` | TOAST? |
|
| Type | Size | `ALIGNMENT` | TOAST? |
|
||||||
|------|------|-------------|--------|
|
|------|------|-------------|--------|
|
||||||
@ -170,7 +170,7 @@ For a typical catalog query propagating 12,000 TLEs:
|
|||||||
| Result `pg_eci` | 48 bytes | 48 bytes (returned, then freed) |
|
| Result `pg_eci` | 48 bytes | 48 bytes (returned, then freed) |
|
||||||
| **Total transient** | **~1 KB** | **~1 KB** |
|
| **Total transient** | **~1 KB** | **~1 KB** |
|
||||||
|
|
||||||
The 736-byte `params` array is the largest per-call allocation. It is freed before the function returns. At no point does pg_orbit hold allocations proportional to the number of rows being processed --- each row is computed and returned independently.
|
The 736-byte `params` array is the largest per-call allocation. It is freed before the function returns. At no point does pg_orrery hold allocations proportional to the number of rows being processed --- each row is computed and returned independently.
|
||||||
|
|
||||||
<Aside type="caution" title="SRF exception">
|
<Aside type="caution" title="SRF exception">
|
||||||
Set-returning functions hold their context struct for the lifetime of the SRF call. For `predict_passes()` over a 7-day window, this is ~1 KB for the duration of the scan. The context is freed when the SRF completes.
|
Set-returning functions hold their context struct for the lifetime of the SRF call. For `predict_passes()` over a 7-day window, this is ~1 KB for the duration of the scan. The context is freed when the SRF completes.
|
||||||
@ -178,13 +178,13 @@ Set-returning functions hold their context struct for the lifetime of the SRF ca
|
|||||||
|
|
||||||
## Error recovery
|
## Error recovery
|
||||||
|
|
||||||
When `ereport(ERROR)` fires inside a pg_orbit function, PostgreSQL's error recovery mechanism:
|
When `ereport(ERROR)` fires inside a pg_orrery function, PostgreSQL's error recovery mechanism:
|
||||||
|
|
||||||
1. Unwinds the call stack via `longjmp`
|
1. Unwinds the call stack via `longjmp`
|
||||||
2. Frees the current memory context (including any `palloc`'d memory)
|
2. Frees the current memory context (including any `palloc`'d memory)
|
||||||
3. Rolls back the current transaction
|
3. Rolls back the current transaction
|
||||||
4. Returns an error message to the client
|
4. Returns an error message to the client
|
||||||
|
|
||||||
Because pg_orbit uses only `palloc` and has no global state for v0.1.0/v0.2.0 functions, there is nothing to clean up beyond what PostgreSQL's context system handles automatically. No sockets, no mutex locks, no C++ destructors.
|
Because pg_orrery uses only `palloc` and has no global state for v0.1.0/v0.2.0 functions, there is nothing to clean up beyond what PostgreSQL's context system handles automatically. No sockets, no mutex locks, no C++ destructors.
|
||||||
|
|
||||||
The v0.3.0 DE reader holds a file descriptor in per-backend static state. This is cleaned up via `on_proc_exit(eph_cleanup, 0)`, registered during `_PG_init()`. If `ereport(ERROR)` fires during a DE function, the file descriptor persists (it will be reused by the next DE call in the same backend) --- it is not leaked, just kept open for the backend's lifetime. The extension is always in a consistent state after error recovery.
|
The v0.3.0 DE reader holds a file descriptor in per-backend static state. This is cleaned up via `on_proc_exit(eph_cleanup, 0)`, registered during `_PG_init()`. If `ereport(ERROR)` fires during a DE function, the file descriptor persists (it will be reused by the next DE call in the same backend) --- it is not leaked, just kept open for the backend's lifetime. The extension is always in a consistent state after error recovery.
|
||||||
|
|||||||
@ -29,7 +29,7 @@ flowchart TD
|
|||||||
|
|
||||||
VSOP87 (Bretagnon & Francou, 1988) computes the target planet's position in the heliocentric ecliptic J2000 frame. The output is three Cartesian coordinates in AU.
|
VSOP87 (Bretagnon & Francou, 1988) computes the target planet's position in the heliocentric ecliptic J2000 frame. The output is three Cartesian coordinates in AU.
|
||||||
|
|
||||||
VSOP87 is a semi-analytical theory: it expands each coordinate as a sum of trigonometric series with polynomial time arguments. The truncated series used in pg_orbit provides ~1 arcsecond accuracy for the inner planets and ~1-2 arcseconds for the outer planets over the period 2000 BCE to 6000 CE.
|
VSOP87 is a semi-analytical theory: it expands each coordinate as a sum of trigonometric series with polynomial time arguments. The truncated series used in pg_orrery provides ~1 arcsecond accuracy for the inner planets and ~1-2 arcseconds for the outer planets over the period 2000 BCE to 6000 CE.
|
||||||
|
|
||||||
For the Sun, this stage returns $(0, 0, 0)$ --- the Sun is at the origin of heliocentric coordinates. The Sun's apparent position is computed by inverting Earth's heliocentric position.
|
For the Sun, this stage returns $(0, 0, 0)$ --- the Sun is at the origin of heliocentric coordinates. The Sun's apparent position is computed by inverting Earth's heliocentric position.
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ flowchart TD
|
|||||||
|
|
||||||
The equatorial coordinate system itself rotates slowly due to lunisolar and planetary precession. Right ascension and declination at J2000 must be precessed to the epoch of observation.
|
The equatorial coordinate system itself rotates slowly due to lunisolar and planetary precession. Right ascension and declination at J2000 must be precessed to the epoch of observation.
|
||||||
|
|
||||||
pg_orbit uses the IAU 1976 precession model (Lieske et al., 1977), which expresses the three Euler angles $\zeta_A$, $z_A$, and $\theta_A$ as cubic polynomials in centuries from J2000:
|
pg_orrery uses the IAU 1976 precession model (Lieske et al., 1977), which expresses the three Euler angles $\zeta_A$, $z_A$, and $\theta_A$ as cubic polynomials in centuries from J2000:
|
||||||
|
|
||||||
$$
|
$$
|
||||||
\zeta_A = 0\overset{\prime\prime}{.}6406161 \cdot T + 0\overset{\prime\prime}{.}0000839 \cdot T^2 + 0\overset{\prime\prime}{.}0000050 \cdot T^3
|
\zeta_A = 0\overset{\prime\prime}{.}6406161 \cdot T + 0\overset{\prime\prime}{.}0000839 \cdot T^2 + 0\overset{\prime\prime}{.}0000050 \cdot T^3
|
||||||
@ -181,11 +181,11 @@ Several simplifications are deliberate.
|
|||||||
The pipeline uses precession but not nutation. For 1-arcsecond VSOP87 positions, the ~9-arcsecond nutation correction is below the noise floor of the ephemeris. Adding nutation would increase computation cost without improving practical accuracy.
|
The pipeline uses precession but not nutation. For 1-arcsecond VSOP87 positions, the ~9-arcsecond nutation correction is below the noise floor of the ephemeris. Adding nutation would increase computation cost without improving practical accuracy.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
**No aberration correction.** Annual aberration shifts apparent positions by up to 20 arcseconds, but for observation planning (which quadrant of the sky is Jupiter in tonight?) this is irrelevant. For sub-arcsecond planet positions, pg_orbit v0.3.0 supports [optional DE440/441 ephemeris files](/guides/de-ephemeris/); for apparent position corrections (aberration, light-time), use SPICE or Skyfield.
|
**No aberration correction.** Annual aberration shifts apparent positions by up to 20 arcseconds, but for observation planning (which quadrant of the sky is Jupiter in tonight?) this is irrelevant. For sub-arcsecond planet positions, pg_orrery v0.3.0 supports [optional DE440/441 ephemeris files](/guides/de-ephemeris/); for apparent position corrections (aberration, light-time), use SPICE or Skyfield.
|
||||||
|
|
||||||
**No light-time iteration.** The positions returned are geometric, not apparent. Light-time corrections of a few minutes for the outer planets shift the apparent position by a fraction of an arcsecond at most --- again, below the VSOP87 accuracy floor.
|
**No light-time iteration.** The positions returned are geometric, not apparent. Light-time corrections of a few minutes for the outer planets shift the apparent position by a fraction of an arcsecond at most --- again, below the VSOP87 accuracy floor.
|
||||||
|
|
||||||
**No atmospheric refraction.** Refraction near the horizon can shift apparent elevation by half a degree. pg_orbit reports geometric elevation; the user must apply refraction corrections for their local conditions if needed. This is a deliberate choice --- refraction depends on temperature, pressure, and humidity that pg_orbit does not model.
|
**No atmospheric refraction.** Refraction near the horizon can shift apparent elevation by half a degree. pg_orrery reports geometric elevation; the user must apply refraction corrections for their local conditions if needed. This is a deliberate choice --- refraction depends on temperature, pressure, and humidity that pg_orrery does not model.
|
||||||
|
|
||||||
## Extending the pipeline
|
## Extending the pipeline
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit vendors Bill Gray's `sat_code` library (MIT license, Project Pluto) for SGP4/SDP4 propagation. The relevant source files are vendored into `src/sgp4/` with `.cpp` extensions renamed to `.c` --- the code contains zero C++ features and compiles as pure C99. This page covers why sat_code was chosen, how it integrates with PostgreSQL's build and execution model, and the error handling contract between the two codebases.
|
pg_orrery vendors Bill Gray's `sat_code` library (MIT license, Project Pluto) for SGP4/SDP4 propagation. The relevant source files are vendored into `src/sgp4/` with `.cpp` extensions renamed to `.c` --- the code contains zero C++ features and compiles as pure C99. This page covers why sat_code was chosen, how it integrates with PostgreSQL's build and execution model, and the error handling contract between the two codebases.
|
||||||
|
|
||||||
## Why sat_code
|
## Why sat_code
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ Three SGP4 implementations were evaluated. The choice came down to one question:
|
|||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="sat_code (chosen)">
|
<TabItem label="sat_code (chosen)">
|
||||||
**Pure C.** Despite upstream's `.cpp` file extensions, the code contains zero C++ features. pg_orbit vendors the files as `.c` and compiles them with `gcc`. The public API in `norad.h` is a flat C function interface: `SGP4_init()`, `SGP4()`, `SDP4_init()`, `SDP4()`, `parse_elements()`, `select_ephemeris()`.
|
**Pure C.** Despite upstream's `.cpp` file extensions, the code contains zero C++ features. pg_orrery vendors the files as `.c` and compiles them with `gcc`. The public API in `norad.h` is a flat C function interface: `SGP4_init()`, `SGP4()`, `SDP4_init()`, `SDP4()`, `parse_elements()`, `select_ephemeris()`.
|
||||||
|
|
||||||
**No global mutable state.** The propagator state lives in a caller-allocated `double params[N_SAT_PARAMS]` array. This maps directly to PostgreSQL's `palloc`-based memory model.
|
**No global mutable state.** The propagator state lives in a caller-allocated `double params[N_SAT_PARAMS]` array. This maps directly to PostgreSQL's `palloc`-based memory model.
|
||||||
|
|
||||||
@ -37,11 +37,11 @@ Three SGP4 implementations were evaluated. The choice came down to one question:
|
|||||||
|
|
||||||
## Compilation
|
## Compilation
|
||||||
|
|
||||||
sat_code's upstream files use `.cpp` extensions but contain no C++ features --- no classes, templates, namespaces, exceptions, or STL. The vendored copies in `src/sgp4/` are renamed to `.c` and compile with `gcc` alongside the rest of pg_orbit. There is no C/C++ boundary, no `g++`, and no `-lstdc++`.
|
sat_code's upstream files use `.cpp` extensions but contain no C++ features --- no classes, templates, namespaces, exceptions, or STL. The vendored copies in `src/sgp4/` are renamed to `.c` and compile with `gcc` alongside the rest of pg_orrery. There is no C/C++ boundary, no `g++`, and no `-lstdc++`.
|
||||||
|
|
||||||
```
|
```
|
||||||
src/*.c --[gcc]--> .o --|
|
src/*.c --[gcc]--> .o --|
|
||||||
src/sgp4/*.c --[gcc]--> .o --|--> pg_orbit.so
|
src/sgp4/*.c --[gcc]--> .o --|--> pg_orrery.so
|
||||||
-lm
|
-lm
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -67,18 +67,18 @@ PGXS handles the `-fPIC` flag and pattern rules for `.c` to `.o` compilation, so
|
|||||||
|
|
||||||
### Header inclusion
|
### Header inclusion
|
||||||
|
|
||||||
pg_orbit's C files include `norad.h` directly:
|
pg_orrery's C files include `norad.h` directly:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#include "norad.h" /* vendored SGP4 public API */
|
#include "norad.h" /* vendored SGP4 public API */
|
||||||
#include "types.h" /* pg_orbit types and WGS-72/84 constants */
|
#include "types.h" /* pg_orrery types and WGS-72/84 constants */
|
||||||
```
|
```
|
||||||
|
|
||||||
The `PG_CPPFLAGS = -I$(SGP4_DIR)` flag makes `norad.h` available without a path prefix.
|
The `PG_CPPFLAGS = -I$(SGP4_DIR)` flag makes `norad.h` available without a path prefix.
|
||||||
|
|
||||||
## The SGP4 API surface
|
## The SGP4 API surface
|
||||||
|
|
||||||
pg_orbit uses a small subset of sat_code's public functions.
|
pg_orrery uses a small subset of sat_code's public functions.
|
||||||
|
|
||||||
### Initialization
|
### Initialization
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ void SGP4_init(double *params, const tle_t *tle);
|
|||||||
void SDP4_init(double *params, const tle_t *tle);
|
void SDP4_init(double *params, const tle_t *tle);
|
||||||
```
|
```
|
||||||
|
|
||||||
Compute the propagator initialization coefficients and store them in the caller-allocated `params` array. This is the expensive step (~5x the cost of a single propagation), so pg_orbit performs it once per TLE and reuses the `params` array for SRF functions that propagate the same TLE to multiple times.
|
Compute the propagator initialization coefficients and store them in the caller-allocated `params` array. This is the expensive step (~5x the cost of a single propagation), so pg_orrery performs it once per TLE and reuses the `params` array for SRF functions that propagate the same TLE to multiple times.
|
||||||
|
|
||||||
### Propagation
|
### Propagation
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ int SDP4(double tsince, const tle_t *tle, const double *params,
|
|||||||
Propagate to `tsince` minutes from epoch. Write position (km) and velocity (km/min) into caller-provided arrays. Return 0 on success or a negative error code.
|
Propagate to `tsince` minutes from epoch. Write position (km) and velocity (km/min) into caller-provided arrays. Return 0 on success or a negative error code.
|
||||||
|
|
||||||
<Aside type="note" title="Velocity units">
|
<Aside type="note" title="Velocity units">
|
||||||
sat_code outputs velocity in km/min. pg_orbit converts to km/s at the boundary --- exactly once, in `sgp4_funcs.c` when populating the `pg_eci` struct. The conversion is `vel[i] / 60.0`. All downstream pg_orbit functions work in km/s.
|
sat_code outputs velocity in km/min. pg_orrery converts to km/s at the boundary --- exactly once, in `sgp4_funcs.c` when populating the `pg_eci` struct. The conversion is `vel[i] / 60.0`. All downstream pg_orrery functions work in km/s.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
### TLE parsing
|
### TLE parsing
|
||||||
@ -116,7 +116,7 @@ sat_code outputs velocity in km/min. pg_orbit converts to km/s at the boundary -
|
|||||||
int parse_elements(const char *line1, const char *line2, tle_t *tle);
|
int parse_elements(const char *line1, const char *line2, tle_t *tle);
|
||||||
```
|
```
|
||||||
|
|
||||||
Parse two-line element text into a `tle_t` struct. Returns 0 on success. pg_orbit calls this in `tle_in()` to validate input at storage time.
|
Parse two-line element text into a `tle_t` struct. Returns 0 on success. pg_orrery calls this in `tle_in()` to validate input at storage time.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void write_elements_in_tle_format(char *obuff, const tle_t *tle);
|
void write_elements_in_tle_format(char *obuff, const tle_t *tle);
|
||||||
@ -126,7 +126,7 @@ Reconstruct text from parsed elements. Used in `tle_out()` for display.
|
|||||||
|
|
||||||
## TLE struct conversion
|
## TLE struct conversion
|
||||||
|
|
||||||
pg_orbit stores TLEs in its own `pg_tle` struct (112 bytes, designed for PostgreSQL tuple storage). sat_code uses `tle_t` (a larger struct with additional fields for its own purposes). The conversion between them is a field-by-field copy with no unit conversion --- both use radians, radians/minute, and Julian dates.
|
pg_orrery stores TLEs in its own `pg_tle` struct (112 bytes, designed for PostgreSQL tuple storage). sat_code uses `tle_t` (a larger struct with additional fields for its own purposes). The conversion between them is a field-by-field copy with no unit conversion --- both use radians, radians/minute, and Julian dates.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
static void
|
static void
|
||||||
@ -156,9 +156,9 @@ This conversion is duplicated in `sgp4_funcs.c`, `coord_funcs.c`, and `pass_func
|
|||||||
|
|
||||||
## Error codes
|
## Error codes
|
||||||
|
|
||||||
sat_code returns integer error codes from `SGP4()` and `SDP4()`. pg_orbit classifies them by physical meaning and responds accordingly.
|
sat_code returns integer error codes from `SGP4()` and `SDP4()`. pg_orrery classifies them by physical meaning and responds accordingly.
|
||||||
|
|
||||||
| Code | sat_code constant | Physical meaning | pg_orbit response |
|
| Code | sat_code constant | Physical meaning | pg_orrery response |
|
||||||
|------|-------------------|------------------|-------------------|
|
|------|-------------------|------------------|-------------------|
|
||||||
| 0 | --- | Normal propagation | Return result |
|
| 0 | --- | Normal propagation | Return result |
|
||||||
| -1 | `SXPX_ERR_NEARLY_PARABOLIC` | Eccentricity $\geq 1$ | `ereport(ERROR)` |
|
| -1 | `SXPX_ERR_NEARLY_PARABOLIC` | Eccentricity $\geq 1$ | `ereport(ERROR)` |
|
||||||
@ -189,11 +189,11 @@ The pass prediction context is the most interesting. A TLE valid for part of a s
|
|||||||
|
|
||||||
## Build integration
|
## Build integration
|
||||||
|
|
||||||
sat_code is vendored into `src/sgp4/` --- the minimal set of source files needed for SGP4/SDP4 propagation, committed directly into the pg_orbit repository. A `PROVENANCE.md` file in that directory records the upstream repository, the exact commit hash, and every modification made during vendoring.
|
sat_code is vendored into `src/sgp4/` --- the minimal set of source files needed for SGP4/SDP4 propagation, committed directly into the pg_orrery repository. A `PROVENANCE.md` file in that directory records the upstream repository, the exact commit hash, and every modification made during vendoring.
|
||||||
|
|
||||||
This approach provides:
|
This approach provides:
|
||||||
|
|
||||||
- **Pinned version.** The vendored commit is recorded in `src/sgp4/PROVENANCE.md`. Upstream changes do not affect pg_orbit until the files are explicitly re-vendored.
|
- **Pinned version.** The vendored commit is recorded in `src/sgp4/PROVENANCE.md`. Upstream changes do not affect pg_orrery until the files are explicitly re-vendored.
|
||||||
- **Clear provenance.** `PROVENANCE.md` documents the upstream repository (github.com/Bill-Gray/sat_code), commit hash, the `.cpp` to `.c` rename rationale, and a line-by-line list of every modification.
|
- **Clear provenance.** `PROVENANCE.md` documents the upstream repository (github.com/Bill-Gray/sat_code), commit hash, the `.cpp` to `.c` rename rationale, and a line-by-line list of every modification.
|
||||||
- **No submodule complexity.** Cloning the repository gets a complete, buildable tree. No `git submodule update --init` step, no risk of missing submodule state.
|
- **No submodule complexity.** Cloning the repository gets a complete, buildable tree. No `git submodule update --init` step, no risk of missing submodule state.
|
||||||
- **Pure C build.** Renaming `.cpp` to `.c` eliminates the `g++` and `-lstdc++` dependencies. The entire extension compiles with a single C compiler.
|
- **Pure C build.** Renaming `.cpp` to `.c` eliminates the `g++` and `-lstdc++` dependencies. The entire extension compiles with a single C compiler.
|
||||||
@ -214,4 +214,4 @@ This approach provides:
|
|||||||
| `PROVENANCE.md` | Upstream commit, modifications, verification notes |
|
| `PROVENANCE.md` | Upstream commit, modifications, verification notes |
|
||||||
| `LICENSE` | MIT license from upstream |
|
| `LICENSE` | MIT license from upstream |
|
||||||
|
|
||||||
Other sat_code files (obs_eph.cpp, sat_id.cpp, etc.) are not vendored. pg_orbit uses sat_code strictly as a propagation library, not as a satellite identification or observation planning tool.
|
Other sat_code files (obs_eph.cpp, sat_id.cpp, etc.) are not vendored. pg_orrery uses sat_code strictly as a propagation library, not as a satellite identification or observation planning tool.
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Every equation in pg_orbit traces to a published, peer-reviewed source. This page provides the complete mapping between the celestial mechanics literature and the source files that implement each theory.
|
Every equation in pg_orrery traces to a published, peer-reviewed source. This page provides the complete mapping between the celestial mechanics literature and the source files that implement each theory.
|
||||||
|
|
||||||
If a constant, algorithm, or formula appears in the code without a citation, that is a defect to be corrected.
|
If a constant, algorithm, or formula appears in the code without a citation, that is a defect to be corrected.
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ This is the canonical SGP4/SDP4 reference. All subsequent implementations, inclu
|
|||||||
|
|
||||||
Bretagnon, P. & Francou, G. (1988). "Planetary Theories in Rectangular and Spherical Variables. VSOP87 Solutions." *Astronomy & Astrophysics*, 202, 309-315.
|
Bretagnon, P. & Francou, G. (1988). "Planetary Theories in Rectangular and Spherical Variables. VSOP87 Solutions." *Astronomy & Astrophysics*, 202, 309-315.
|
||||||
|
|
||||||
pg_orbit uses the VSOP87 rectangular ecliptic J2000 variant. The truncated coefficient tables provide full accuracy within the validity range of the theory (roughly 4000 BCE to 8000 CE for the inner planets, with degradation for the outer planets beyond $\pm$2000 years from J2000).
|
pg_orrery uses the VSOP87 rectangular ecliptic J2000 variant. The truncated coefficient tables provide full accuracy within the validity range of the theory (roughly 4000 BCE to 8000 CE for the inner planets, with degradation for the outer planets beyond $\pm$2000 years from J2000).
|
||||||
|
|
||||||
### ELP2000-82B
|
### ELP2000-82B
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ The 82B revision is the version implemented. It provides geocentric ecliptic coo
|
|||||||
|
|
||||||
- Izzo, D. (2015). "Revisiting Lambert's Problem." *Celestial Mechanics and Dynamical Astronomy*, 121, 1-15.
|
- Izzo, D. (2015). "Revisiting Lambert's Problem." *Celestial Mechanics and Dynamical Astronomy*, 121, 1-15.
|
||||||
|
|
||||||
The Izzo solver uses Householder iterations for fast convergence and handles both short-way and long-way transfers. pg_orbit uses the prograde (short-way) solution by default.
|
The Izzo solver uses Householder iterations for fast convergence and handles both short-way and long-way transfers. pg_orrery uses the prograde (short-way) solution by default.
|
||||||
|
|
||||||
## Radio emission
|
## Radio emission
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ Sample from the verification suite:
|
|||||||
# Minutes: 0.00 Expected X: -33110.816260 Y: 26044.993650 Z: -20.725400
|
# Minutes: 0.00 Expected X: -33110.816260 Y: 26044.993650 Z: -20.725400
|
||||||
```
|
```
|
||||||
|
|
||||||
These vectors cover the full range of orbit types that pg_orbit handles: LEO (SGP4), MEO (SGP4), GEO (SDP4), high-eccentricity Molniya (SDP4), and deep-space GPS (SDP4). Any implementation that matches all 518 vectors is functionally equivalent to the Vallado reference.
|
These vectors cover the full range of orbit types that pg_orrery handles: LEO (SGP4), MEO (SGP4), GEO (SDP4), high-eccentricity Molniya (SDP4), and deep-space GPS (SDP4). Any implementation that matches all 518 vectors is functionally equivalent to the Vallado reference.
|
||||||
|
|
||||||
## Source file index
|
## Source file index
|
||||||
|
|
||||||
|
|||||||
@ -8,25 +8,25 @@ import { Tabs, TabItem, Steps, Aside } from "@astrojs/starlight/components";
|
|||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="Docker (recommended)">
|
<TabItem label="Docker (recommended)">
|
||||||
The fastest way to get pg_orbit running. The Docker image ships PostgreSQL 17 with pg_orbit pre-compiled.
|
The fastest way to get pg_orrery running. The Docker image ships PostgreSQL 17 with pg_orrery pre-compiled.
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
1. Pull the image:
|
1. Pull the image:
|
||||||
```bash
|
```bash
|
||||||
docker pull git.supported.systems/warehack.ing/pg_orbit:pg17
|
docker pull git.supported.systems/warehack.ing/pg_orrery:pg17
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Start the container:
|
2. Start the container:
|
||||||
```bash
|
```bash
|
||||||
docker run -d --name pg_orbit \
|
docker run -d --name pg_orrery \
|
||||||
-e POSTGRES_PASSWORD=orbit \
|
-e POSTGRES_PASSWORD=orbit \
|
||||||
-p 5499:5432 \
|
-p 5499:5432 \
|
||||||
git.supported.systems/warehack.ing/pg_orbit:pg17
|
git.supported.systems/warehack.ing/pg_orrery:pg17
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Connect and enable the extension:
|
3. Connect and enable the extension:
|
||||||
```bash
|
```bash
|
||||||
psql -h localhost -p 5499 -U postgres -c "CREATE EXTENSION pg_orbit;"
|
psql -h localhost -p 5499 -U postgres -c "CREATE EXTENSION pg_orrery;"
|
||||||
```
|
```
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
@ -41,8 +41,8 @@ import { Tabs, TabItem, Steps, Aside } from "@astrojs/starlight/components";
|
|||||||
<Steps>
|
<Steps>
|
||||||
1. Clone the repository:
|
1. Clone the repository:
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.supported.systems/warehack.ing/pg_orbit.git
|
git clone https://git.supported.systems/warehack.ing/pg_orrery.git
|
||||||
cd pg_orbit
|
cd pg_orrery
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ import { Tabs, TabItem, Steps, Aside } from "@astrojs/starlight/components";
|
|||||||
|
|
||||||
3. Enable in your database:
|
3. Enable in your database:
|
||||||
```sql
|
```sql
|
||||||
CREATE EXTENSION pg_orbit;
|
CREATE EXTENSION pg_orrery;
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Verify installation:
|
4. Verify installation:
|
||||||
@ -64,7 +64,7 @@ import { Tabs, TabItem, Steps, Aside } from "@astrojs/starlight/components";
|
|||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
<Aside type="note">
|
<Aside type="note">
|
||||||
The build compiles pure C throughout --- both pg_orbit and the vendored SGP4/SDP4 library in `src/sgp4/`. No C++ compiler or runtime is required.
|
The build compiles pure C throughout --- both pg_orrery and the vendored SGP4/SDP4 library in `src/sgp4/`. No C++ compiler or runtime is required.
|
||||||
</Aside>
|
</Aside>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ import { Tabs, TabItem, Steps, Aside } from "@astrojs/starlight/components";
|
|||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: git.supported.systems/warehack.ing/pg_orbit:pg17
|
image: git.supported.systems/warehack.ing/pg_orrery:pg17
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-orbit}
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-orbit}
|
||||||
POSTGRES_DB: ${POSTGRES_DB:-orbit}
|
POSTGRES_DB: ${POSTGRES_DB:-orbit}
|
||||||
@ -90,7 +90,7 @@ import { Tabs, TabItem, Steps, Aside } from "@astrojs/starlight/components";
|
|||||||
Then:
|
Then:
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
psql -h localhost -p 5499 -U postgres -d orbit -c "CREATE EXTENSION pg_orbit;"
|
psql -h localhost -p 5499 -U postgres -d orbit -c "CREATE EXTENSION pg_orrery;"
|
||||||
```
|
```
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
@ -111,10 +111,10 @@ If you have a previous version installed, upgrade in place:
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- From v0.1.0 (satellite-only) to v0.2.0 (solar system)
|
-- From v0.1.0 (satellite-only) to v0.2.0 (solar system)
|
||||||
ALTER EXTENSION pg_orbit UPDATE TO '0.2.0';
|
ALTER EXTENSION pg_orrery UPDATE TO '0.2.0';
|
||||||
|
|
||||||
-- From v0.2.0 to v0.3.0 (DE ephemeris support)
|
-- From v0.2.0 to v0.3.0 (DE ephemeris support)
|
||||||
ALTER EXTENSION pg_orbit UPDATE TO '0.3.0';
|
ALTER EXTENSION pg_orrery UPDATE TO '0.3.0';
|
||||||
```
|
```
|
||||||
|
|
||||||
Each migration adds new functions while preserving existing data and functions.
|
Each migration adds new functions while preserving existing data and functions.
|
||||||
|
|||||||
@ -6,10 +6,10 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Five queries that show what pg_orbit can do. Each builds on the previous — from a single planet observation to planning an interplanetary trajectory.
|
Five queries that show what pg_orrery can do. Each builds on the previous — from a single planet observation to planning an interplanetary trajectory.
|
||||||
|
|
||||||
<Aside type="tip">
|
<Aside type="tip">
|
||||||
All examples assume you have pg_orbit installed and `CREATE EXTENSION pg_orbit;` has been run. See [Installation](/getting-started/installation/) if you need to set that up first.
|
All examples assume you have pg_orrery installed and `CREATE EXTENSION pg_orrery;` has been run. See [Installation](/getting-started/installation/) if you need to set that up first.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
@ -108,7 +108,7 @@ All examples assume you have pg_orbit installed and `CREATE EXTENSION pg_orbit;`
|
|||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
You've seen the five domains pg_orbit covers. For deeper dives:
|
You've seen the five domains pg_orrery covers. For deeper dives:
|
||||||
|
|
||||||
- **[Tracking Satellites](/guides/tracking-satellites/)** — batch observation, conjunction screening, pass prediction workflows
|
- **[Tracking Satellites](/guides/tracking-satellites/)** — batch observation, conjunction screening, pass prediction workflows
|
||||||
- **[Observing the Solar System](/guides/observing-solar-system/)** — "what's up tonight?" queries, rise/set times, conjunctions
|
- **[Observing the Solar System](/guides/observing-solar-system/)** — "what's up tonight?" queries, rise/set times, conjunctions
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
---
|
---
|
||||||
title: What is pg_orbit?
|
title: What is pg_orrery?
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 1
|
order: 1
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Card, CardGrid, Aside } from "@astrojs/starlight/components";
|
import { Card, CardGrid, Aside } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit is a PostgreSQL extension that moves orbital mechanics computation inside your database. Instead of computing satellite positions in Python, planet coordinates in C++, or transfer orbits in MATLAB and then importing the results — the computation happens where your data already lives.
|
An orrery is a clockwork model of the solar system — brass gears turning planets in their courses. pg_orrery is the same idea, built from Keplerian parameters and SQL instead of wheelwork. Where a mechanical orrery approximates orbits with gear ratios, a database orrery computes them from the six orbital elements that define each trajectory.
|
||||||
|
|
||||||
## The "PostGIS for space" analogy
|
## The "PostGIS for space" analogy
|
||||||
|
|
||||||
PostGIS added spatial awareness to PostgreSQL — suddenly your database understood geometry, distance, and containment. pg_orbit does the same for celestial mechanics. Your database understands orbits, observation geometry, and the relationships between objects in the solar system. You can JOIN orbital computation results with any other table, filter with WHERE clauses, and let PostgreSQL's query planner parallelize the work.
|
PostGIS added spatial awareness to PostgreSQL — suddenly your database understood geometry, distance, and containment. pg_orrery does the same for celestial mechanics. Your database understands orbits, observation geometry, and the relationships between objects in the solar system. You can JOIN orbital computation results with any other table, filter with WHERE clauses, and let PostgreSQL's query planner parallelize the work.
|
||||||
|
|
||||||
## What it covers
|
## What it covers
|
||||||
|
|
||||||
@ -47,18 +47,18 @@ PostGIS added spatial awareness to PostgreSQL — suddenly your database underst
|
|||||||
</Card>
|
</Card>
|
||||||
</CardGrid>
|
</CardGrid>
|
||||||
|
|
||||||
## What pg_orbit is NOT
|
## What pg_orrery is NOT
|
||||||
|
|
||||||
<Aside type="caution" title="Honest limitations">
|
<Aside type="caution" title="Honest limitations">
|
||||||
pg_orbit is a computation engine, not a complete application. Understanding what it doesn't do is as important as knowing what it does.
|
pg_orrery is a computation engine, not a complete application. Understanding what it doesn't do is as important as knowing what it does.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
**Not a GUI.** pg_orbit returns numbers. Use Stellarium, GPredict, or STK for visualization. Use any plotting library to render its output.
|
**Not a GUI.** pg_orrery returns numbers. Use Stellarium, GPredict, or STK for visualization. Use any plotting library to render its output.
|
||||||
|
|
||||||
**Not sub-arcsecond by default.** The built-in VSOP87 pipeline is accurate to about 1 arcsecond — sufficient for observation planning and visual astronomy. For precision work (dish pointing, occultation timing, astrometry), pg_orbit v0.3.0 supports [optional JPL DE440/441 ephemeris files](/guides/de-ephemeris/) that bring accuracy to ~0.1 milliarcsecond. DE is opt-in and requires a one-time GUC configuration.
|
**Not sub-arcsecond by default.** The built-in VSOP87 pipeline is accurate to about 1 arcsecond — sufficient for observation planning and visual astronomy. For precision work (dish pointing, occultation timing, astrometry), pg_orrery v0.3.0 supports [optional JPL DE440/441 ephemeris files](/guides/de-ephemeris/) that bring accuracy to ~0.1 milliarcsecond. DE is opt-in and requires a one-time GUC configuration.
|
||||||
|
|
||||||
**Not a TLE source.** Bring your own TLEs from Space-Track, CelesTrak, or any other provider. pg_orbit parses and propagates them; it doesn't fetch them.
|
**Not a TLE source.** Bring your own TLEs from Space-Track, CelesTrak, or any other provider. pg_orrery parses and propagates them; it doesn't fetch them.
|
||||||
|
|
||||||
**Not a replacement for SPICE.** No BSP kernel support, no light-time iteration, no aberration corrections at the IAU 2000A level. With DE enabled, pg_orbit matches SPICE on raw planet position accuracy — the remaining gap is in apparent-position corrections (aberration, light-time, nutation) that matter for sub-arcsecond apparent coordinates.
|
**Not a replacement for SPICE.** No BSP kernel support, no light-time iteration, no aberration corrections at the IAU 2000A level. With DE enabled, pg_orrery matches SPICE on raw planet position accuracy — the remaining gap is in apparent-position corrections (aberration, light-time, nutation) that matter for sub-arcsecond apparent coordinates.
|
||||||
|
|
||||||
**Not a full mission design tool.** The Lambert solver handles ballistic two-body transfers — no low-thrust trajectories, no gravity assists, no multi-body optimization. For full mission design, use GMAT or poliastro.
|
**Not a full mission design tool.** The Lambert solver handles ballistic two-body transfers — no low-thrust trajectories, no gravity assists, no multi-body optimization. For full mission design, use GMAT or poliastro.
|
||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit propagates comets and asteroids using two-body Keplerian mechanics. You provide six classical orbital elements from the Minor Planet Center (MPC) or any other source, and pg_orbit computes the body's heliocentric position at any time. Combined with Earth's position from VSOP87, you can observe the body from any location on Earth.
|
pg_orrery propagates comets and asteroids using two-body Keplerian mechanics. You provide six classical orbital elements from the Minor Planet Center (MPC) or any other source, and pg_orrery computes the body's heliocentric position at any time. Combined with Earth's position from VSOP87, you can observe the body from any location on Earth.
|
||||||
|
|
||||||
## How you do it today
|
## How you do it today
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ Tracking comets and asteroids typically involves:
|
|||||||
|
|
||||||
The pattern is familiar: download elements, propagate in Python or C, transform to observer coordinates, and import results into your database.
|
The pattern is familiar: download elements, propagate in Python or C, transform to observer coordinates, and import results into your database.
|
||||||
|
|
||||||
## What changes with pg_orbit
|
## What changes with pg_orrery
|
||||||
|
|
||||||
Two functions handle comet/asteroid computation:
|
Two functions handle comet/asteroid computation:
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ The parameters map directly to MPC orbital element format:
|
|||||||
| `Omega` | Longitude of ascending node | degrees |
|
| `Omega` | Longitude of ascending node | degrees |
|
||||||
| `T` | Perihelion time | Julian date |
|
| `T` | Perihelion time | Julian date |
|
||||||
|
|
||||||
## What pg_orbit does not replace
|
## What pg_orrery does not replace
|
||||||
|
|
||||||
<Aside type="caution" title="Two-body limitations">
|
<Aside type="caution" title="Two-body limitations">
|
||||||
Keplerian propagation assumes the body is influenced only by the Sun. Real small bodies experience planetary perturbations that accumulate over time.
|
Keplerian propagation assumes the body is influenced only by the Sun. Real small bodies experience planetary perturbations that accumulate over time.
|
||||||
@ -51,8 +51,8 @@ Keplerian propagation assumes the body is influenced only by the Sun. Real small
|
|||||||
|
|
||||||
- **No perturbations.** Jupiter alone can shift a comet's position by degrees over a few years. Two-body propagation is most accurate near perihelion, within a few months of the elements' epoch.
|
- **No perturbations.** Jupiter alone can shift a comet's position by degrees over a few years. Two-body propagation is most accurate near perihelion, within a few months of the elements' epoch.
|
||||||
- **No non-gravitational forces.** Comet outgassing produces accelerations not captured by Keplerian mechanics. For long-period comets far from the Sun, this is negligible. For short-period comets near perihelion, it matters.
|
- **No non-gravitational forces.** Comet outgassing produces accelerations not captured by Keplerian mechanics. For long-period comets far from the Sun, this is negligible. For short-period comets near perihelion, it matters.
|
||||||
- **No magnitude estimation.** pg_orbit returns position only. Comet brightness depends on heliocentric distance, geocentric distance, and a magnitude slope parameter that varies per comet.
|
- **No magnitude estimation.** pg_orrery returns position only. Comet brightness depends on heliocentric distance, geocentric distance, and a magnitude slope parameter that varies per comet.
|
||||||
- **No orbit determination.** pg_orbit propagates known orbits. It does not fit orbits from observations.
|
- **No orbit determination.** pg_orrery propagates known orbits. It does not fit orbits from observations.
|
||||||
|
|
||||||
For MPC elements less than a few months old, two-body propagation is typically accurate to a few arcminutes for asteroids and tens of arcminutes for comets. Fresh elements give better results.
|
For MPC elements less than a few months old, two-body propagation is typically accurate to a few arcminutes for asteroids and tens of arcminutes for comets. Fresh elements give better results.
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ The body approaches from infinity, swings past the Sun at perihelion distance, a
|
|||||||
|
|
||||||
### Near-parabolic comet
|
### Near-parabolic comet
|
||||||
|
|
||||||
Many long-period comets have eccentricities very close to 1.0. pg_orbit handles the parabolic case (e=1.0 exactly) with a dedicated Barker equation solver:
|
Many long-period comets have eccentricities very close to 1.0. pg_orrery handles the parabolic case (e=1.0 exactly) with a dedicated Barker equation solver:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT round(helio_x(kepler_propagate(
|
SELECT round(helio_x(kepler_propagate(
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Conjunction screening identifies pairs of satellites that might approach each other closely enough to pose a collision risk. The brute-force approach -- computing pairwise distances for all objects in the catalog at every time step -- scales as O(n^2) and is impractical for large catalogs. pg_orbit solves this with a GiST index on the `tle` type that enables spatial filtering by altitude band and orbital inclination, reducing the candidate set before running full propagation.
|
Conjunction screening identifies pairs of satellites that might approach each other closely enough to pose a collision risk. The brute-force approach -- computing pairwise distances for all objects in the catalog at every time step -- scales as O(n^2) and is impractical for large catalogs. pg_orrery solves this with a GiST index on the `tle` type that enables spatial filtering by altitude band and orbital inclination, reducing the candidate set before running full propagation.
|
||||||
|
|
||||||
## How you do it today
|
## How you do it today
|
||||||
|
|
||||||
@ -19,9 +19,9 @@ Operational conjunction screening uses several established tools and data source
|
|||||||
|
|
||||||
The fundamental challenge: a catalog of 25,000+ tracked objects produces over 300 million unique pairs. Even checking each pair at a single epoch takes significant time. Checking over a 7-day window at 1-minute resolution is computationally prohibitive without pre-filtering.
|
The fundamental challenge: a catalog of 25,000+ tracked objects produces over 300 million unique pairs. Even checking each pair at a single epoch takes significant time. Checking over a 7-day window at 1-minute resolution is computationally prohibitive without pre-filtering.
|
||||||
|
|
||||||
## What changes with pg_orbit
|
## What changes with pg_orrery
|
||||||
|
|
||||||
pg_orbit attacks the problem in two stages:
|
pg_orrery attacks the problem in two stages:
|
||||||
|
|
||||||
**Stage 1: GiST index reduces candidates.** The GiST index on the `tle` column stores a 2-D key for each TLE: altitude band (perigee to apogee) and inclination range. The `&&` operator tests whether two TLEs occupy overlapping regions in this 2-D space. Only TLEs that share an altitude shell AND a similar inclination can possibly conjunct. This typically reduces 300 million pairs to a few thousand candidates.
|
**Stage 1: GiST index reduces candidates.** The GiST index on the `tle` column stores a 2-D key for each TLE: altitude band (perigee to apogee) and inclination range. The `&&` operator tests whether two TLEs occupy overlapping regions in this 2-D space. Only TLEs that share an altitude shell AND a similar inclination can possibly conjunct. This typically reduces 300 million pairs to a few thousand candidates.
|
||||||
|
|
||||||
@ -36,16 +36,16 @@ The two operators:
|
|||||||
|
|
||||||
The `&&` operator is used for overlap queries (find all objects in the same shell). The `<->` operator is used for nearest-neighbor queries (find the N closest objects by altitude separation).
|
The `&&` operator is used for overlap queries (find all objects in the same shell). The `<->` operator is used for nearest-neighbor queries (find the N closest objects by altitude separation).
|
||||||
|
|
||||||
## What pg_orbit does not replace
|
## What pg_orrery does not replace
|
||||||
|
|
||||||
<Aside type="caution" title="Screening, not assessment">
|
<Aside type="caution" title="Screening, not assessment">
|
||||||
GiST-based conjunction screening is a coarse filter. It finds candidates that share an orbital shell. It does not determine whether two objects will actually come close at a specific time.
|
GiST-based conjunction screening is a coarse filter. It finds candidates that share an orbital shell. It does not determine whether two objects will actually come close at a specific time.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
- **Not a probability of collision.** pg_orbit does not compute Pc (probability of collision). It identifies objects in overlapping orbital shells and computes distances at discrete time steps. For Pc calculation, use CARA (Conjunction Assessment Risk Analysis) methods.
|
- **Not a probability of collision.** pg_orrery does not compute Pc (probability of collision). It identifies objects in overlapping orbital shells and computes distances at discrete time steps. For Pc calculation, use CARA (Conjunction Assessment Risk Analysis) methods.
|
||||||
- **No covariance propagation.** SGP4 does not produce covariance matrices. The distance values have no uncertainty bounds. For operational conjunction assessment, use SP ephemerides with covariance (from CDMs or owner/operator data).
|
- **No covariance propagation.** SGP4 does not produce covariance matrices. The distance values have no uncertainty bounds. For operational conjunction assessment, use SP ephemerides with covariance (from CDMs or owner/operator data).
|
||||||
- **Altitude-band approximation.** The GiST key uses perigee-to-apogee altitude as a 1-D range and inclination as a second dimension. Two TLEs can share an altitude shell and never approach because their RAANs or phases are far apart. Always follow GiST filtering with full propagation.
|
- **Altitude-band approximation.** The GiST key uses perigee-to-apogee altitude as a 1-D range and inclination as a second dimension. Two TLEs can share an altitude shell and never approach because their RAANs or phases are far apart. Always follow GiST filtering with full propagation.
|
||||||
- **No maneuver planning.** pg_orbit identifies close approaches. It does not compute avoidance maneuvers (delta-v, timing, constraints).
|
- **No maneuver planning.** pg_orrery identifies close approaches. It does not compute avoidance maneuvers (delta-v, timing, constraints).
|
||||||
|
|
||||||
The workflow is: GiST narrows → `tle_distance()` verifies → operator/analyst decides.
|
The workflow is: GiST narrows → `tle_distance()` verifies → operator/analyst decides.
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit v0.3.0 adds optional support for JPL Development Ephemeris files (DE440/DE441), bringing positional accuracy from VSOP87's ~1 arcsecond to DE441's ~0.1 milliarcsecond. The upgrade path is designed so that nothing changes unless you opt in.
|
pg_orrery v0.3.0 adds optional support for JPL Development Ephemeris files (DE440/DE441), bringing positional accuracy from VSOP87's ~1 arcsecond to DE441's ~0.1 milliarcsecond. The upgrade path is designed so that nothing changes unless you opt in.
|
||||||
|
|
||||||
## When you need DE
|
## When you need DE
|
||||||
|
|
||||||
@ -37,22 +37,22 @@ If you don't configure a DE file, all `_de()` functions silently fall back to VS
|
|||||||
2. **Place the file** where PostgreSQL can read it:
|
2. **Place the file** where PostgreSQL can read it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /var/lib/postgres/pg_orbit
|
sudo mkdir -p /var/lib/postgres/pg_orrery
|
||||||
sudo cp linux_p1550p2650.440 /var/lib/postgres/pg_orbit/de440.bin
|
sudo cp linux_p1550p2650.440 /var/lib/postgres/pg_orrery/de440.bin
|
||||||
sudo chown postgres:postgres /var/lib/postgres/pg_orbit/de440.bin
|
sudo chown postgres:postgres /var/lib/postgres/pg_orrery/de440.bin
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Set the GUC** (requires superuser):
|
3. **Set the GUC** (requires superuser):
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
ALTER SYSTEM SET pg_orbit.ephemeris_path = '/var/lib/postgres/pg_orbit/de440.bin';
|
ALTER SYSTEM SET pg_orrery.ephemeris_path = '/var/lib/postgres/pg_orrery/de440.bin';
|
||||||
SELECT pg_reload_conf();
|
SELECT pg_reload_conf();
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Verify:**
|
4. **Verify:**
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT * FROM pg_orbit_ephemeris_info();
|
SELECT * FROM pg_orrery_ephemeris_info();
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see `provider = 'JPL_DE'` with the file's JD range and version.
|
You should see `provider = 'JPL_DE'` with the file's JD range and version.
|
||||||
@ -139,10 +139,10 @@ IMMUTABLE functions can be used in expression indexes and are constant-folded du
|
|||||||
|
|
||||||
## Diagnostics
|
## Diagnostics
|
||||||
|
|
||||||
The `pg_orbit_ephemeris_info()` function reports the current state:
|
The `pg_orrery_ephemeris_info()` function reports the current state:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT * FROM pg_orbit_ephemeris_info();
|
SELECT * FROM pg_orrery_ephemeris_info();
|
||||||
```
|
```
|
||||||
|
|
||||||
| Column | Type | Description |
|
| Column | Type | Description |
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit includes a Lambert solver for computing ballistic transfer orbits between any two planets. Given a departure body, arrival body, departure time, and arrival time, the solver returns the transfer orbit's energy characteristics: departure C3, arrival C3, v-infinity, time of flight, and transfer semi-major axis. The function processes about 800,000 solutions per second, which means pork chop plots -- the standard visualization for launch window analysis -- become SQL CROSS JOINs.
|
pg_orrery includes a Lambert solver for computing ballistic transfer orbits between any two planets. Given a departure body, arrival body, departure time, and arrival time, the solver returns the transfer orbit's energy characteristics: departure C3, arrival C3, v-infinity, time of flight, and transfer semi-major axis. The function processes about 800,000 solutions per second, which means pork chop plots -- the standard visualization for launch window analysis -- become SQL CROSS JOINs.
|
||||||
|
|
||||||
## How you do it today
|
## How you do it today
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ Interplanetary trajectory design is one of the more specialized areas of orbital
|
|||||||
|
|
||||||
For all of these, the workflow is: pick a departure date, pick an arrival date, run the solver, record the result. To build a pork chop plot, you sweep a grid of departure and arrival dates and collect results. In Python, this means nested loops. In GMAT, this means scripted batch runs.
|
For all of these, the workflow is: pick a departure date, pick an arrival date, run the solver, record the result. To build a pork chop plot, you sweep a grid of departure and arrival dates and collect results. In Python, this means nested loops. In GMAT, this means scripted batch runs.
|
||||||
|
|
||||||
## What changes with pg_orbit
|
## What changes with pg_orrery
|
||||||
|
|
||||||
Two functions handle the complete Lambert problem:
|
Two functions handle the complete Lambert problem:
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ The `lambert_transfer()` output fields:
|
|||||||
|
|
||||||
Body IDs match the VSOP87 convention: 1=Mercury, 2=Venus, 3=Earth, 4=Mars, 5=Jupiter, 6=Saturn, 7=Uranus, 8=Neptune. The departure body is almost always Earth (3), but the solver works for any planet-to-planet combination.
|
Body IDs match the VSOP87 convention: 1=Mercury, 2=Venus, 3=Earth, 4=Mars, 5=Jupiter, 6=Saturn, 7=Uranus, 8=Neptune. The departure body is almost always Earth (3), but the solver works for any planet-to-planet combination.
|
||||||
|
|
||||||
## What pg_orbit does not replace
|
## What pg_orrery does not replace
|
||||||
|
|
||||||
<Aside type="caution" title="Ballistic two-body only">
|
<Aside type="caution" title="Ballistic two-body only">
|
||||||
The Lambert solver computes the conic section connecting two positions in a central gravity field. Real interplanetary trajectories involve more physics.
|
The Lambert solver computes the conic section connecting two positions in a central gravity field. Real interplanetary trajectories involve more physics.
|
||||||
@ -51,7 +51,7 @@ The Lambert solver computes the conic section connecting two positions in a cent
|
|||||||
- **No low-thrust trajectories.** Ion drives and solar sails produce continuous thrust. Lambert assumes an instantaneous departure burn and coast to arrival.
|
- **No low-thrust trajectories.** Ion drives and solar sails produce continuous thrust. Lambert assumes an instantaneous departure burn and coast to arrival.
|
||||||
- **No three-body effects.** The solver uses heliocentric two-body mechanics. Sphere-of-influence transitions, Lagrange point dynamics, and lunar gravity assists are not modeled.
|
- **No three-body effects.** The solver uses heliocentric two-body mechanics. Sphere-of-influence transitions, Lagrange point dynamics, and lunar gravity assists are not modeled.
|
||||||
- **No launch vehicle constraints.** The solver returns C3, which determines the required launch energy. Mapping C3 to a specific rocket's payload capacity is a separate analysis.
|
- **No launch vehicle constraints.** The solver returns C3, which determines the required launch energy. Mapping C3 to a specific rocket's payload capacity is a separate analysis.
|
||||||
- **No aerocapture or entry design.** The arrival C3 determines how much delta-v is needed for orbit insertion, but pg_orbit does not compute the insertion burn itself.
|
- **No aerocapture or entry design.** The arrival C3 determines how much delta-v is needed for orbit insertion, but pg_orrery does not compute the insertion burn itself.
|
||||||
|
|
||||||
For mission design beyond first-order feasibility analysis, use GMAT or poliastro with patched-conic or N-body propagation.
|
For mission design beyond first-order feasibility analysis, use GMAT or poliastro with patched-conic or N-body propagation.
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,9 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Jupiter is the strongest radio source in the solar system after the Sun. Its decametric emissions (roughly 15-40 MHz) occur when Io passes through specific orbital positions relative to Jupiter's rotating magnetic field. pg_orbit computes the two geometry parameters that govern these bursts -- Io phase angle and Jupiter Central Meridian Longitude -- and maps them to an empirical burst probability using the Carr et al. (1983) source regions.
|
Jupiter is the strongest radio source in the solar system after the Sun. Its decametric emissions (roughly 15-40 MHz) occur when Io passes through specific orbital positions relative to Jupiter's rotating magnetic field. pg_orrery computes the two geometry parameters that govern these bursts -- Io phase angle and Jupiter Central Meridian Longitude -- and maps them to an empirical burst probability using the Carr et al. (1983) source regions.
|
||||||
|
|
||||||
This is the feature built for the Radio JOVE community. There are 500-1000 active Radio JOVE operators worldwide, and until pg_orbit, the standard prediction tool was Radio Jupiter Pro -- a Windows-only desktop application. Batch prediction, calendar generation, and integration with observation scheduling are now possible in SQL.
|
This is the feature built for the Radio JOVE community. There are 500-1000 active Radio JOVE operators worldwide, and until pg_orrery, the standard prediction tool was Radio Jupiter Pro -- a Windows-only desktop application. Batch prediction, calendar generation, and integration with observation scheduling are now possible in SQL.
|
||||||
|
|
||||||
## How you do it today
|
## How you do it today
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ Jupiter radio observation planning has relied on a small set of tools:
|
|||||||
|
|
||||||
The problem: planning an observation campaign over weeks or months means running Radio Jupiter Pro repeatedly for each night, eyeballing the probability, and writing down the good windows. There is no way to generate a calendar of optimal observation windows in one operation.
|
The problem: planning an observation campaign over weeks or months means running Radio Jupiter Pro repeatedly for each night, eyeballing the probability, and writing down the good windows. There is no way to generate a calendar of optimal observation windows in one operation.
|
||||||
|
|
||||||
## What changes with pg_orbit
|
## What changes with pg_orrery
|
||||||
|
|
||||||
Three functions cover the complete Jupiter radio prediction pipeline:
|
Three functions cover the complete Jupiter radio prediction pipeline:
|
||||||
|
|
||||||
@ -42,15 +42,15 @@ The probability function encodes four source regions from the Carr et al. (1983)
|
|||||||
|
|
||||||
Outside these regions, the probability is 0.0. Overlapping regions combine to the higher probability.
|
Outside these regions, the probability is 0.0. Overlapping regions combine to the higher probability.
|
||||||
|
|
||||||
## What pg_orbit does not replace
|
## What pg_orrery does not replace
|
||||||
|
|
||||||
<Aside type="caution" title="Empirical model">
|
<Aside type="caution" title="Empirical model">
|
||||||
The burst probability is a statistical average from decades of observations. Individual bursts are stochastic -- a high probability window can produce nothing, and occasional bursts appear outside predicted windows.
|
The burst probability is a statistical average from decades of observations. Individual bursts are stochastic -- a high probability window can produce nothing, and occasional bursts appear outside predicted windows.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
- **No signal detection.** pg_orbit predicts when bursts are likely, not whether one is occurring. Use Radio-SkyPipe or SDR software for actual signal capture.
|
- **No signal detection.** pg_orrery predicts when bursts are likely, not whether one is occurring. Use Radio-SkyPipe or SDR software for actual signal capture.
|
||||||
- **No frequency prediction.** The model predicts occurrence probability, not the specific frequency structure (L-bursts vs. S-bursts) or intensity.
|
- **No frequency prediction.** The model predicts occurrence probability, not the specific frequency structure (L-bursts vs. S-bursts) or intensity.
|
||||||
- **No RFI assessment.** Local radio interference is often the biggest obstacle to Jupiter observation. pg_orbit does not model your local RF environment.
|
- **No RFI assessment.** Local radio interference is often the biggest obstacle to Jupiter observation. pg_orrery does not model your local RF environment.
|
||||||
- **No receiver pointing.** At 20 MHz, most receivers use fixed dipole antennas. Pointing is not an issue, but Jupiter must be above the horizon. Combine with `planet_observe(5, ...)` to check elevation.
|
- **No receiver pointing.** At 20 MHz, most receivers use fixed dipole antennas. Pointing is not an issue, but Jupiter must be above the horizon. Combine with `planet_observe(5, ...)` to check elevation.
|
||||||
|
|
||||||
## Try it
|
## Try it
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit computes positions for all eight planets (VSOP87), the Sun, and the Moon (ELP2000-82B). Every observation returns the same `topocentric` type: azimuth, elevation, range, and range rate from a given observer at a given time. The solar system becomes queryable with standard SQL.
|
pg_orrery computes positions for all eight planets (VSOP87), the Sun, and the Moon (ELP2000-82B). Every observation returns the same `topocentric` type: azimuth, elevation, range, and range rate from a given observer at a given time. The solar system becomes queryable with standard SQL.
|
||||||
|
|
||||||
## How you do it today
|
## How you do it today
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ Knowing where planets are involves one of a few approaches:
|
|||||||
|
|
||||||
All of these produce results that live outside your database. If you want to correlate planet positions with weather data, observation logs, or satellite passes, you export, import, and join.
|
All of these produce results that live outside your database. If you want to correlate planet positions with weather data, observation logs, or satellite passes, you export, import, and join.
|
||||||
|
|
||||||
## What changes with pg_orbit
|
## What changes with pg_orrery
|
||||||
|
|
||||||
All planets, the Sun, and the Moon are available as SQL function calls. The functions take an observer and a timestamp, and return topocentric coordinates. You can sweep all eight planets, generate time series, filter by elevation, and join with other tables in the same query.
|
All planets, the Sun, and the Moon are available as SQL function calls. The functions take an observer and a timestamp, and return topocentric coordinates. You can sweep all eight planets, generate time series, filter by elevation, and join with other tables in the same query.
|
||||||
|
|
||||||
@ -34,16 +34,16 @@ Key functions:
|
|||||||
|
|
||||||
Body IDs follow the VSOP87 convention: 1=Mercury, 2=Venus, 3=Earth, 4=Mars, 5=Jupiter, 6=Saturn, 7=Uranus, 8=Neptune. Body 0 returns the Sun at the heliocentric origin (all zeros).
|
Body IDs follow the VSOP87 convention: 1=Mercury, 2=Venus, 3=Earth, 4=Mars, 5=Jupiter, 6=Saturn, 7=Uranus, 8=Neptune. Body 0 returns the Sun at the heliocentric origin (all zeros).
|
||||||
|
|
||||||
## What pg_orbit does not replace
|
## What pg_orrery does not replace
|
||||||
|
|
||||||
<Aside type="caution" title="Accuracy trade-offs">
|
<Aside type="caution" title="Accuracy trade-offs">
|
||||||
VSOP87 and ELP2000-82B are analytic theories. They trade the last bits of precision for computational speed and zero external data dependencies.
|
VSOP87 and ELP2000-82B are analytic theories. They trade the last bits of precision for computational speed and zero external data dependencies.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
- **VSOP87 accuracy is about 1 arcsecond.** JPL DE441 (used by Skyfield and SPICE) achieves 0.001 arcsecond. For visual observation planning, 1 arcsecond is more than sufficient. For precision astrometry or GHz dish pointing, pg_orbit v0.3.0 supports [optional DE440/441 ephemeris files](/guides/de-ephemeris/) with sub-milliarcsecond accuracy.
|
- **VSOP87 accuracy is about 1 arcsecond.** JPL DE441 (used by Skyfield and SPICE) achieves 0.001 arcsecond. For visual observation planning, 1 arcsecond is more than sufficient. For precision astrometry or GHz dish pointing, pg_orrery v0.3.0 supports [optional DE440/441 ephemeris files](/guides/de-ephemeris/) with sub-milliarcsecond accuracy.
|
||||||
- **ELP2000-82B accuracy is about 10 arcseconds** for the Moon. Good enough for knowing when the Moon is up, what phase it is in, and whether it will interfere with observations. Not sufficient for occultation timing.
|
- **ELP2000-82B accuracy is about 10 arcseconds** for the Moon. Good enough for knowing when the Moon is up, what phase it is in, and whether it will interfere with observations. Not sufficient for occultation timing.
|
||||||
- **No light-time iteration.** pg_orbit computes geometric positions, not apparent positions. The difference matters at the milliarcsecond level.
|
- **No light-time iteration.** pg_orrery computes geometric positions, not apparent positions. The difference matters at the milliarcsecond level.
|
||||||
- **No atmospheric refraction.** Objects near the horizon appear slightly higher than their geometric position. pg_orbit does not apply refraction corrections.
|
- **No atmospheric refraction.** Objects near the horizon appear slightly higher than their geometric position. pg_orrery does not apply refraction corrections.
|
||||||
|
|
||||||
## Try it
|
## Try it
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit computes positions for 19 planetary moons across four systems: the four Galilean moons of Jupiter, eight moons of Saturn, five moons of Uranus, and two moons of Mars. Each uses a dedicated analytic theory optimized for that system.
|
pg_orrery computes positions for 19 planetary moons across four systems: the four Galilean moons of Jupiter, eight moons of Saturn, five moons of Uranus, and two moons of Mars. Each uses a dedicated analytic theory optimized for that system.
|
||||||
|
|
||||||
## How you do it today
|
## How you do it today
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ Observing planetary moons usually means one of:
|
|||||||
|
|
||||||
The common limitation: getting positions for many moons at many times means many separate requests or script iterations. Comparing moon positions across systems (all of Jupiter's moons vs. all of Saturn's) requires stitching results together outside the ephemeris tool.
|
The common limitation: getting positions for many moons at many times means many separate requests or script iterations. Comparing moon positions across systems (all of Jupiter's moons vs. all of Saturn's) requires stitching results together outside the ephemeris tool.
|
||||||
|
|
||||||
## What changes with pg_orbit
|
## What changes with pg_orrery
|
||||||
|
|
||||||
Four observation functions cover all 19 moons:
|
Four observation functions cover all 19 moons:
|
||||||
|
|
||||||
@ -72,14 +72,14 @@ All functions return the same `topocentric` type. Every moon is identified by a
|
|||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
## What pg_orbit does not replace
|
## What pg_orrery does not replace
|
||||||
|
|
||||||
<Aside type="caution" title="Theory limitations">
|
<Aside type="caution" title="Theory limitations">
|
||||||
Each analytic theory has a valid time range and accuracy envelope. The theories embedded in pg_orbit are designed for current-epoch observation planning, not historical or far-future ephemeris work.
|
Each analytic theory has a valid time range and accuracy envelope. The theories embedded in pg_orrery are designed for current-epoch observation planning, not historical or far-future ephemeris work.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
- **Not sub-arcsecond.** The analytic theories produce positions accurate to a few arcseconds at best. For astrometric reduction or spacecraft navigation, use JPL ephemerides via SPICE or Skyfield.
|
- **Not sub-arcsecond.** The analytic theories produce positions accurate to a few arcseconds at best. For astrometric reduction or spacecraft navigation, use JPL ephemerides via SPICE or Skyfield.
|
||||||
- **No mutual events.** pg_orbit does not predict eclipses, occultations, or transits between moons. Use IMCCE's MULTISAT service for mutual event predictions.
|
- **No mutual events.** pg_orrery does not predict eclipses, occultations, or transits between moons. Use IMCCE's MULTISAT service for mutual event predictions.
|
||||||
- **No libration or physical ephemerides.** The functions return topocentric position only — no rotation state, no sub-observer longitude, no apparent disk size.
|
- **No libration or physical ephemerides.** The functions return topocentric position only — no rotation state, no sub-observer longitude, no apparent disk size.
|
||||||
- **19 moons, not hundreds.** Only the major moons with well-characterized analytic theories are included. Irregular satellites, small inner moons, and ring-embedded moonlets are not covered.
|
- **19 moons, not hundreds.** Only the major moons with well-characterized analytic theories are included. Irregular satellites, small inner moons, and ring-embedded moonlets are not covered.
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit computes topocentric positions for any star given its J2000 right ascension and declination. Feed it a star catalog table and you can observe hundreds of thousands of stars in a single query. The function applies IAU 1976 precession to bring J2000 coordinates to the observation epoch, then transforms to horizon coordinates for a given observer.
|
pg_orrery computes topocentric positions for any star given its J2000 right ascension and declination. Feed it a star catalog table and you can observe hundreds of thousands of stars in a single query. The function applies IAU 1976 precession to bring J2000 coordinates to the observation epoch, then transforms to horizon coordinates for a given observer.
|
||||||
|
|
||||||
## How you do it today
|
## How you do it today
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ Computing where a star appears in the sky involves:
|
|||||||
|
|
||||||
The bottleneck is the same as with planets: the computation happens outside your database. If your observation log, scheduling system, or data pipeline lives in PostgreSQL, you export catalog data, compute positions externally, and import the results.
|
The bottleneck is the same as with planets: the computation happens outside your database. If your observation log, scheduling system, or data pipeline lives in PostgreSQL, you export catalog data, compute positions externally, and import the results.
|
||||||
|
|
||||||
## What changes with pg_orbit
|
## What changes with pg_orrery
|
||||||
|
|
||||||
`star_observe()` takes J2000 RA (in hours) and Dec (in degrees), an observer, and a time. It returns a `topocentric` with azimuth, elevation, and zero range (stars are treated as infinitely distant). The function applies IAU 1976 precession and the standard equatorial-to-horizontal transform.
|
`star_observe()` takes J2000 RA (in hours) and Dec (in degrees), an observer, and a time. It returns a `topocentric` with azimuth, elevation, and zero range (stars are treated as infinitely distant). The function applies IAU 1976 precession and the standard equatorial-to-horizontal transform.
|
||||||
|
|
||||||
@ -27,14 +27,14 @@ The bottleneck is the same as with planets: the computation happens outside your
|
|||||||
|
|
||||||
The key performance characteristic: star observation processes at about 714,000 observations per second. A 100,000-star catalog can be fully observed from any location at any time in under 150ms.
|
The key performance characteristic: star observation processes at about 714,000 observations per second. A 100,000-star catalog can be fully observed from any location at any time in under 150ms.
|
||||||
|
|
||||||
## What pg_orbit does not replace
|
## What pg_orrery does not replace
|
||||||
|
|
||||||
<Aside type="caution" title="Precision boundaries">
|
<Aside type="caution" title="Precision boundaries">
|
||||||
Star observation in pg_orbit uses IAU 1976 precession only. The missing corrections are small for observation planning but significant for precision astrometry.
|
Star observation in pg_orrery uses IAU 1976 precession only. The missing corrections are small for observation planning but significant for precision astrometry.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
- **No nutation.** IAU 1976 precession alone introduces errors up to ~10 arcseconds over a few decades. For visual observation planning, this is negligible. For sub-arcsecond work, use SOFA/ERFA routines.
|
- **No nutation.** IAU 1976 precession alone introduces errors up to ~10 arcseconds over a few decades. For visual observation planning, this is negligible. For sub-arcsecond work, use SOFA/ERFA routines.
|
||||||
- **No proper motion.** Barnard's Star moves 10 arcseconds/year. pg_orbit treats catalog coordinates as fixed. If your catalog includes proper motion columns, you can pre-apply the correction in SQL before calling `star_observe()`.
|
- **No proper motion.** Barnard's Star moves 10 arcseconds/year. pg_orrery treats catalog coordinates as fixed. If your catalog includes proper motion columns, you can pre-apply the correction in SQL before calling `star_observe()`.
|
||||||
- **No aberration.** Annual aberration displaces star positions by up to ~20 arcseconds. This matters for precision pointing but not for finding stars at the eyepiece.
|
- **No aberration.** Annual aberration displaces star positions by up to ~20 arcseconds. This matters for precision pointing but not for finding stars at the eyepiece.
|
||||||
- **No parallax.** Stellar parallax is at most ~0.8 arcseconds (Proxima Centauri). Not a concern for observation planning.
|
- **No parallax.** Stellar parallax is at most ~0.8 arcseconds (Proxima Centauri). Not a concern for observation planning.
|
||||||
- **Range is zero.** Stars are treated as infinitely far. The `topo_range()` accessor returns 0 for star observations.
|
- **Range is zero.** Stars are treated as infinitely far. The `topo_range()` accessor returns 0 for star observations.
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Steps, Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Satellite tracking is the domain pg_orbit was originally built for. The core idea: instead of propagating TLEs one at a time in Python and then writing results to your database, move the propagation into the database itself. The satellite catalog becomes a live, queryable model of near-Earth space.
|
Satellite tracking is the domain pg_orrery was originally built for. The core idea: instead of propagating TLEs one at a time in Python and then writing results to your database, move the propagation into the database itself. The satellite catalog becomes a live, queryable model of near-Earth space.
|
||||||
|
|
||||||
## How you do it today
|
## How you do it today
|
||||||
|
|
||||||
@ -22,9 +22,9 @@ Tools like GPredict handle this with a GUI. Skyfield wraps python-sgp4 with a cl
|
|||||||
|
|
||||||
The bottleneck shows up when you need to process the catalog. Propagating 12,000 TLEs for a single epoch in Python takes seconds. Joining the results against a frequency database or an owner table requires exporting to CSV, loading into a database, and running the join. Pass prediction for a constellation of 100+ satellites means nested loops. Conjunction screening for the full catalog means O(n^2) pairwise comparisons.
|
The bottleneck shows up when you need to process the catalog. Propagating 12,000 TLEs for a single epoch in Python takes seconds. Joining the results against a frequency database or an owner table requires exporting to CSV, loading into a database, and running the join. Pass prediction for a constellation of 100+ satellites means nested loops. Conjunction screening for the full catalog means O(n^2) pairwise comparisons.
|
||||||
|
|
||||||
## What changes with pg_orbit
|
## What changes with pg_orrery
|
||||||
|
|
||||||
pg_orbit implements SGP4/SDP4 (Brouwer, 1959; Hoots & Roehrich, 1980) as native PostgreSQL functions. The `tle` type stores parsed mean elements directly in a column. Propagation, observation, and pass prediction are SQL function calls that operate on that column.
|
pg_orrery implements SGP4/SDP4 (Brouwer, 1959; Hoots & Roehrich, 1980) as native PostgreSQL functions. The `tle` type stores parsed mean elements directly in a column. Propagation, observation, and pass prediction are SQL function calls that operate on that column.
|
||||||
|
|
||||||
What this means in practice:
|
What this means in practice:
|
||||||
|
|
||||||
@ -35,17 +35,17 @@ What this means in practice:
|
|||||||
|
|
||||||
The `observe_safe()` function returns NULL instead of raising an error when a TLE has decayed or diverged. This keeps batch queries running even when the catalog contains stale elements.
|
The `observe_safe()` function returns NULL instead of raising an error when a TLE has decayed or diverged. This keeps batch queries running even when the catalog contains stale elements.
|
||||||
|
|
||||||
## What pg_orbit does not replace
|
## What pg_orrery does not replace
|
||||||
|
|
||||||
<Aside type="caution" title="Know the boundaries">
|
<Aside type="caution" title="Know the boundaries">
|
||||||
pg_orbit propagates TLEs and computes look angles. It does not replace the full satellite operations stack.
|
pg_orrery propagates TLEs and computes look angles. It does not replace the full satellite operations stack.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
- **No real-time GUI.** GPredict and STK provide map displays, polar plots, and Doppler displays. pg_orbit returns numbers. Use any visualization tool to render its output.
|
- **No real-time GUI.** GPredict and STK provide map displays, polar plots, and Doppler displays. pg_orrery returns numbers. Use any visualization tool to render its output.
|
||||||
- **No rotator control.** Hamlib drives antenna rotators. pg_orbit computes the azimuth and elevation values Hamlib would consume, but it has no hardware interface.
|
- **No rotator control.** Hamlib drives antenna rotators. pg_orrery computes the azimuth and elevation values Hamlib would consume, but it has no hardware interface.
|
||||||
- **No TLE fetching.** Bring your own TLEs from Space-Track, CelesTrak, or any provider. pg_orbit parses and propagates them.
|
- **No TLE fetching.** Bring your own TLEs from Space-Track, CelesTrak, or any provider. pg_orrery parses and propagates them.
|
||||||
- **No orbit determination.** pg_orbit propagates existing TLEs. It does not fit orbits from observations.
|
- **No orbit determination.** pg_orrery propagates existing TLEs. It does not fit orbits from observations.
|
||||||
- **No high-precision propagation.** SGP4/SDP4 accuracy degrades with TLE age. For operational conjunction assessment, use SP ephemerides or owner/operator-provided state vectors. pg_orbit's GiST screening finds candidates; you verify with better data.
|
- **No high-precision propagation.** SGP4/SDP4 accuracy degrades with TLE age. For operational conjunction assessment, use SP ephemerides or owner/operator-provided state vectors. pg_orrery's GiST screening finds candidates; you verify with better data.
|
||||||
|
|
||||||
## Try it
|
## Try it
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: pg_orbit Documentation
|
title: pg_orrery Documentation
|
||||||
description: Solar system computation for PostgreSQL
|
description: Solar system computation for PostgreSQL
|
||||||
template: splash
|
template: splash
|
||||||
hero:
|
hero:
|
||||||
@ -9,7 +9,7 @@ hero:
|
|||||||
alt: PostgreSQL elephant orbiting a planet
|
alt: PostgreSQL elephant orbiting a planet
|
||||||
actions:
|
actions:
|
||||||
- text: Get Started
|
- text: Get Started
|
||||||
link: /getting-started/what-is-pg-orbit/
|
link: /getting-started/what-is-pg-orrery/
|
||||||
icon: right-arrow
|
icon: right-arrow
|
||||||
variant: primary
|
variant: primary
|
||||||
- text: What's Different
|
- text: What's Different
|
||||||
@ -19,7 +19,7 @@ hero:
|
|||||||
|
|
||||||
import { Card, CardGrid, LinkCard } from "@astrojs/starlight/components";
|
import { Card, CardGrid, LinkCard } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
## What can pg_orbit do?
|
## What can pg_orrery do?
|
||||||
|
|
||||||
<CardGrid>
|
<CardGrid>
|
||||||
<Card title="Track anything in orbit" icon="rocket">
|
<Card title="Track anything in orbit" icon="rocket">
|
||||||
@ -60,7 +60,7 @@ import { Card, CardGrid, LinkCard } from "@astrojs/starlight/components";
|
|||||||
/>
|
/>
|
||||||
<LinkCard
|
<LinkCard
|
||||||
title="Workflow Translation"
|
title="Workflow Translation"
|
||||||
description="Side-by-side comparisons: how you do it today vs. how pg_orbit changes the game"
|
description="Side-by-side comparisons: how you do it today vs. how pg_orrery changes the game"
|
||||||
href="/workflow/from-skyfield/"
|
href="/workflow/from-skyfield/"
|
||||||
/>
|
/>
|
||||||
<LinkCard
|
<LinkCard
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Measured performance numbers for pg_orbit's core operations. Every number on this page was produced by running the listed SQL query against a live PostgreSQL 17 instance with a single backend, no parallel workers, and no connection pooling overhead.
|
Measured performance numbers for pg_orrery's core operations. Every number on this page was produced by running the listed SQL query against a live PostgreSQL 17 instance with a single backend, no parallel workers, and no connection pooling overhead.
|
||||||
|
|
||||||
<Aside type="note" title="Methodology">
|
<Aside type="note" title="Methodology">
|
||||||
All benchmarks use PostgreSQL's `EXPLAIN (ANALYZE, BUFFERS)` for timing. The numbers are wall-clock execution time for the query, not per-function overhead. Each benchmark was run three times; the reported value is the median. Cold start was avoided by running each query once before measurement.
|
All benchmarks use PostgreSQL's `EXPLAIN (ANALYZE, BUFFERS)` for timing. The numbers are wall-clock execution time for the query, not per-function overhead. Each benchmark was run three times; the reported value is the median. Cold start was avoided by running each query once before measurement.
|
||||||
@ -84,7 +84,7 @@ VSOP87 is ~45x slower than SGP4 per call because it evaluates large trigonometri
|
|||||||
|
|
||||||
### Per-planet breakdown
|
### Per-planet breakdown
|
||||||
|
|
||||||
The outer planets (Jupiter through Neptune) are slightly faster than the inner planets because their VSOP87 series have fewer significant terms at the truncation level pg_orbit uses.
|
The outer planets (Jupiter through Neptune) are slightly faster than the inner planets because their VSOP87 series have fewer significant terms at the truncation level pg_orrery uses.
|
||||||
|
|
||||||
## Galilean moon observation
|
## Galilean moon observation
|
||||||
|
|
||||||
@ -223,7 +223,7 @@ A 7-day window at 30-second coarse scan resolution requires ~20,160 propagation
|
|||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="Requirements">
|
<TabItem label="Requirements">
|
||||||
- PostgreSQL 17 with pg_orbit installed
|
- PostgreSQL 17 with pg_orrery installed
|
||||||
- A satellite catalog table with ~12,000 TLEs (available from CelesTrak)
|
- A satellite catalog table with ~12,000 TLEs (available from CelesTrak)
|
||||||
- A star catalog table (any subset of Hipparcos or Yale BSC)
|
- A star catalog table (any subset of Hipparcos or Yale BSC)
|
||||||
- No concurrent queries during measurement
|
- No concurrent queries during measurement
|
||||||
@ -231,7 +231,7 @@ A 7-day window at 30-second coarse scan resolution requires ~20,160 propagation
|
|||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="Setup">
|
<TabItem label="Setup">
|
||||||
```sql
|
```sql
|
||||||
CREATE EXTENSION pg_orbit;
|
CREATE EXTENSION pg_orrery;
|
||||||
|
|
||||||
-- Load a TLE catalog
|
-- Load a TLE catalog
|
||||||
CREATE TABLE satellite_catalog (tle tle);
|
CREATE TABLE satellite_catalog (tle tle);
|
||||||
@ -263,6 +263,6 @@ A 7-day window at 30-second coarse scan resolution requires ~20,160 propagation
|
|||||||
|
|
||||||
## What these numbers mean
|
## What these numbers mean
|
||||||
|
|
||||||
The benchmarks demonstrate that pg_orbit's computation cost is low enough to treat orbital mechanics as a SQL primitive. Propagating an entire satellite catalog takes less time than a typical index scan on a moderately-sized table. Planet observation is fast enough to generate ephemeris tables with `generate_series`. Pork chop plots are feasible as interactive queries rather than batch jobs.
|
The benchmarks demonstrate that pg_orrery's computation cost is low enough to treat orbital mechanics as a SQL primitive. Propagating an entire satellite catalog takes less time than a typical index scan on a moderately-sized table. Planet observation is fast enough to generate ephemeris tables with `generate_series`. Pork chop plots are feasible as interactive queries rather than batch jobs.
|
||||||
|
|
||||||
The numbers also show where the bottlenecks are: VSOP87 series evaluation dominates everything except star observation and raw SGP4 propagation. If a future optimization effort targets one component, it should be the VSOP87 evaluation loop.
|
The numbers also show where the bottlenecks are: VSOP87 series evaluation dominates everything except star observation and raw SGP4 propagation. If a future optimization effort targets one component, it should be the VSOP87 evaluation loop.
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Complete mapping of integer body identifiers used across all pg_orbit functions. Each function family uses its own ID space; the tables below document which IDs are valid for which functions.
|
Complete mapping of integer body identifiers used across all pg_orrery functions. Each function family uses its own ID space; the tables below document which IDs are valid for which functions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Physical constants, astronomical constants, and accuracy bounds for every computational theory used in pg_orbit. VSOP87/ELP2000-82B constants are compiled from their original sources and embedded at compile time. Optional JPL DE440/441 ephemeris support (v0.3.0+) reads constants from an external binary file.
|
Physical constants, astronomical constants, and accuracy bounds for every computational theory used in pg_orrery. VSOP87/ELP2000-82B constants are compiled from their original sources and embedded at compile time. Optional JPL DE440/441 ephemeris support (v0.3.0+) reads constants from an external binary file.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ The SGP4/SDP4 propagator uses WGS-72 constants internally. This matches the refe
|
|||||||
| J4 | J4 | -0.00000165597 | -- |
|
| J4 | J4 | -0.00000165597 | -- |
|
||||||
|
|
||||||
<Aside type="caution">
|
<Aside type="caution">
|
||||||
These WGS-72 values are **only** used inside the SGP4/SDP4 propagator. All coordinate output (geodetic, topocentric) uses WGS-84. Mixing WGS-72 and WGS-84 constants in the same computation is a common source of systematic error in satellite tracking software; pg_orbit handles this boundary correctly.
|
These WGS-72 values are **only** used inside the SGP4/SDP4 propagator. All coordinate output (geodetic, topocentric) uses WGS-84. Mixing WGS-72 and WGS-84 constants in the same computation is a common source of systematic error in satellite tracking software; pg_orrery handles this boundary correctly.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
### WGS-84 (Coordinate Output)
|
### WGS-84 (Coordinate Output)
|
||||||
@ -64,7 +64,7 @@ All geodetic and topocentric coordinate conversions use WGS-84 constants.
|
|||||||
|
|
||||||
## Theory Accuracy Bounds
|
## Theory Accuracy Bounds
|
||||||
|
|
||||||
Each computational theory in pg_orbit has well-characterized accuracy limits. The bounds below are drawn from the original theory publications and validated against JPL ephemerides where possible.
|
Each computational theory in pg_orrery has well-characterized accuracy limits. The bounds below are drawn from the original theory publications and validated against JPL ephemerides where possible.
|
||||||
|
|
||||||
### SGP4/SDP4 (Satellite Propagation)
|
### SGP4/SDP4 (Satellite Propagation)
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ For dates within a few centuries of J2000, VSOP87 is accurate to sub-arcsecond l
|
|||||||
|
|
||||||
### JPL DE440/441 (Optional, v0.3.0+)
|
### JPL DE440/441 (Optional, v0.3.0+)
|
||||||
|
|
||||||
When configured via the `pg_orbit.ephemeris_path` GUC, DE ephemeris positions are available through the `_de()` function variants.
|
When configured via the `pg_orrery.ephemeris_path` GUC, DE ephemeris positions are available through the `_de()` function variants.
|
||||||
|
|
||||||
| Property | DE440 | DE441 |
|
| Property | DE440 | DE441 |
|
||||||
|----------|-------|-------|
|
|----------|-------|-------|
|
||||||
@ -125,7 +125,7 @@ Accuracy relative to VLBI observations:
|
|||||||
| Moon | ~1 meter | Constrained by Lunar Laser Ranging |
|
| Moon | ~1 meter | Constrained by Lunar Laser Ranging |
|
||||||
|
|
||||||
<Aside type="note">
|
<Aside type="note">
|
||||||
DE positions are in the ICRS equatorial frame. pg_orbit applies the equatorial-to-ecliptic rotation at the provider boundary before feeding positions into the observation pipeline. Both target and Earth positions always come from the same provider (Rule 7 of the constant chain of custody).
|
DE positions are in the ICRS equatorial frame. pg_orrery applies the equatorial-to-ecliptic rotation at the provider boundary before feeding positions into the observation pipeline. Both target and Earth positions always come from the same provider (Rule 7 of the constant chain of custody).
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
**DE440 vs DE441:** DE440 is recommended for present-day and near-future work. It covers 1550-2650 and produces identical results to DE441 within that range, but is ~27x smaller. DE441's extended range (-13200 to +17191) is needed only for paleoclimate studies, historical astronomy, or very long-term solar system dynamics.
|
**DE440 vs DE441:** DE440 is recommended for present-day and near-future work. It covers 1550-2650 and produces identical results to DE441 within that range, but is ~27x smaller. DE441's extended range (-13200 to +17191) is needed only for paleoclimate studies, historical astronomy, or very long-term solar system dynamics.
|
||||||
|
|||||||
@ -6,9 +6,9 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Optional high-precision function variants that use JPL DE440/441 ephemeris files when configured via the `pg_orbit.ephemeris_path` GUC. Each function mirrors an existing VSOP87/ELP2000-82B counterpart with identical parameters and return types.
|
Optional high-precision function variants that use JPL DE440/441 ephemeris files when configured via the `pg_orrery.ephemeris_path` GUC. Each function mirrors an existing VSOP87/ELP2000-82B counterpart with identical parameters and return types.
|
||||||
|
|
||||||
All DE functions are `STABLE STRICT PARALLEL SAFE` (except `pg_orbit_ephemeris_info` which is `STABLE PARALLEL SAFE`, not STRICT). When DE is unavailable, they fall back to their VSOP87/ELP2000-82B equivalents.
|
All DE functions are `STABLE STRICT PARALLEL SAFE` (except `pg_orrery_ephemeris_info` which is `STABLE PARALLEL SAFE`, not STRICT). When DE is unavailable, they fall back to their VSOP87/ELP2000-82B equivalents.
|
||||||
|
|
||||||
See the [DE Ephemeris guide](/guides/de-ephemeris/) for setup and configuration.
|
See the [DE Ephemeris guide](/guides/de-ephemeris/) for setup and configuration.
|
||||||
|
|
||||||
@ -303,14 +303,14 @@ mars_moon_observe_de(moon_id int4, obs observer, t timestamptz) → topocentric
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## pg_orbit_ephemeris_info
|
## pg_orrery_ephemeris_info
|
||||||
|
|
||||||
Returns diagnostic information about the current ephemeris provider.
|
Returns diagnostic information about the current ephemeris provider.
|
||||||
|
|
||||||
### Signature
|
### Signature
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
pg_orbit_ephemeris_info() → RECORD(provider text, file_path text, start_jd float8, end_jd float8, version int4, au_km float8)
|
pg_orrery_ephemeris_info() → RECORD(provider text, file_path text, start_jd float8, end_jd float8, version int4, au_km float8)
|
||||||
```
|
```
|
||||||
|
|
||||||
<Aside type="note">
|
<Aside type="note">
|
||||||
@ -332,8 +332,8 @@ This function is `STABLE PARALLEL SAFE` but **not** `STRICT` --- it takes no arg
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Check current provider
|
-- Check current provider
|
||||||
SELECT (pg_orbit_ephemeris_info()).provider;
|
SELECT (pg_orrery_ephemeris_info()).provider;
|
||||||
|
|
||||||
-- Full diagnostic
|
-- Full diagnostic
|
||||||
SELECT * FROM pg_orbit_ephemeris_info();
|
SELECT * FROM pg_orrery_ephemeris_info();
|
||||||
```
|
```
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Functions for propagating TLEs, converting coordinate frames, computing ground tracks, and predicting satellite passes. These form the core satellite tracking pipeline in pg_orbit.
|
Functions for propagating TLEs, converting coordinate frames, computing ground tracks, and predicting satellite passes. These form the core satellite tracking pipeline in pg_orrery.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit defines two operators on the `tle` type and a GiST operator class that enables indexed conjunction screening over large satellite catalogs. The operators work on the orbital altitude band and inclination range extracted from TLE elements, providing a fast necessary-condition filter for proximity analysis.
|
pg_orrery defines two operators on the `tle` type and a GiST operator class that enables indexed conjunction screening over large satellite catalogs. The operators work on the orbital altitude band and inclination range extracted from TLE elements, providing a fast necessary-condition filter for proximity analysis.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ sidebar:
|
|||||||
|
|
||||||
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
import { Aside, Tabs, TabItem } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
pg_orbit defines seven composite types that represent the core data structures of orbital mechanics. Each type has a fixed on-disk size, a text I/O format for readability, and accessor functions for extracting individual fields.
|
pg_orrery defines seven composite types that represent the core data structures of orbital mechanics. Each type has a fixed on-disk size, a text I/O format for readability, and accessor functions for extracting individual fields.
|
||||||
|
|
||||||
## tle
|
## tle
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,14 @@
|
|||||||
title: From GMAT to SQL
|
title: From GMAT to SQL
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 3
|
order: 3
|
||||||
description: Comparing GMAT's mission design workflow with pg_orbit's SQL approach for Lambert transfer analysis.
|
description: Comparing GMAT's mission design workflow with pg_orrery's SQL approach for Lambert transfer analysis.
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Tabs, TabItem, Aside, Steps } from "@astrojs/starlight/components";
|
import { Tabs, TabItem, Aside, Steps } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
GMAT (General Mission Analysis Tool) is NASA's open-source mission design software. It handles everything from preliminary trajectory design to full mission planning with low-thrust propulsion, gravity assists, and multi-body optimization. It's powerful, free, and built for professional mission designers.
|
GMAT (General Mission Analysis Tool) is NASA's open-source mission design software. It handles everything from preliminary trajectory design to full mission planning with low-thrust propulsion, gravity assists, and multi-body optimization. It's powerful, free, and built for professional mission designers.
|
||||||
|
|
||||||
pg_orbit is not a mission design tool. It solves one specific problem from GMAT's domain — Lambert transfers between planets — and makes it available as a SQL function. This page is about that narrow overlap, and why doing Lambert analysis in SQL is sometimes a better fit than launching a full mission design environment.
|
pg_orrery is not a mission design tool. It solves one specific problem from GMAT's domain — Lambert transfers between planets — and makes it available as a SQL function. This page is about that narrow overlap, and why doing Lambert analysis in SQL is sometimes a better fit than launching a full mission design environment.
|
||||||
|
|
||||||
## The Lambert problem
|
## The Lambert problem
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ The solution gives you departure C3 (the energy needed to escape Earth's gravity
|
|||||||
the targeting sequence, handle convergence failures, and manage output
|
the targeting sequence, handle convergence failures, and manage output
|
||||||
parsing.
|
parsing.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
-- Single Earth-Mars transfer
|
-- Single Earth-Mars transfer
|
||||||
SELECT round(c3_departure::numeric, 2) AS c3_depart_km2s2,
|
SELECT round(c3_departure::numeric, 2) AS c3_depart_km2s2,
|
||||||
@ -136,7 +136,7 @@ This is where the SQL approach really differentiates itself. A pork chop plot su
|
|||||||
(not GMAT) for pork chop plots, and only bring GMAT in for detailed
|
(not GMAT) for pork chop plots, and only bring GMAT in for detailed
|
||||||
trajectory refinement once the launch window is identified.
|
trajectory refinement once the launch window is identified.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
-- Full pork chop plot: Earth to Mars, 2028-2029 window
|
-- Full pork chop plot: Earth to Mars, 2028-2029 window
|
||||||
-- 150 departure dates x 150 arrival dates = 22,500 transfer solutions
|
-- 150 departure dates x 150 arrival dates = 22,500 transfer solutions
|
||||||
@ -230,22 +230,22 @@ This scans a wide grid and finds the single best (lowest C3) departure/arrival c
|
|||||||
## Where GMAT wins
|
## Where GMAT wins
|
||||||
|
|
||||||
<Aside type="note" title="GMAT is a different class of tool">
|
<Aside type="note" title="GMAT is a different class of tool">
|
||||||
pg_orbit's Lambert solver is a screening tool. GMAT is a mission design environment. The comparison is between a calculator and a workshop.
|
pg_orrery's Lambert solver is a screening tool. GMAT is a mission design environment. The comparison is between a calculator and a workshop.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
**Multi-body dynamics.** GMAT propagates trajectories with full N-body gravitational perturbations — Sun, planets, moons, solar radiation pressure, atmospheric drag. pg_orbit's Lambert solver is strictly two-body (Sun + spacecraft).
|
**Multi-body dynamics.** GMAT propagates trajectories with full N-body gravitational perturbations — Sun, planets, moons, solar radiation pressure, atmospheric drag. pg_orrery's Lambert solver is strictly two-body (Sun + spacecraft).
|
||||||
|
|
||||||
**Low-thrust trajectories.** Electric propulsion missions (ion engines, Hall thrusters) require continuous thrust modeling. GMAT handles this; pg_orbit does not.
|
**Low-thrust trajectories.** Electric propulsion missions (ion engines, Hall thrusters) require continuous thrust modeling. GMAT handles this; pg_orrery does not.
|
||||||
|
|
||||||
**Gravity assists.** Flyby trajectories — using a planet's gravity to change direction and speed — are central to many interplanetary missions. GMAT models these with full patched-conic or N-body dynamics. pg_orbit solves point-to-point transfers only.
|
**Gravity assists.** Flyby trajectories — using a planet's gravity to change direction and speed — are central to many interplanetary missions. GMAT models these with full patched-conic or N-body dynamics. pg_orrery solves point-to-point transfers only.
|
||||||
|
|
||||||
**Mission sequence optimization.** GMAT's differential corrector and optimizer can target complex mission constraints: orbital insertion parameters, flyby altitudes, fuel budgets. pg_orbit returns raw transfer parameters without optimization.
|
**Mission sequence optimization.** GMAT's differential corrector and optimizer can target complex mission constraints: orbital insertion parameters, flyby altitudes, fuel budgets. pg_orrery returns raw transfer parameters without optimization.
|
||||||
|
|
||||||
**Attitude modeling.** GMAT tracks spacecraft orientation — important for solar panel pointing, antenna alignment, and sensor geometry. pg_orbit has no concept of spacecraft attitude.
|
**Attitude modeling.** GMAT tracks spacecraft orientation — important for solar panel pointing, antenna alignment, and sensor geometry. pg_orrery has no concept of spacecraft attitude.
|
||||||
|
|
||||||
**Visualization.** GMAT includes 3D trajectory visualization. pg_orbit returns numbers.
|
**Visualization.** GMAT includes 3D trajectory visualization. pg_orrery returns numbers.
|
||||||
|
|
||||||
## Where pg_orbit wins
|
## Where pg_orrery wins
|
||||||
|
|
||||||
**Speed of iteration.** Changing a date range or adding a constraint is editing a SQL query. No restarting a GUI, no waiting for script compilation, no managing convergence parameters.
|
**Speed of iteration.** Changing a date range or adding a constraint is editing a SQL query. No restarting a GUI, no waiting for script compilation, no managing convergence parameters.
|
||||||
|
|
||||||
@ -253,18 +253,18 @@ pg_orbit's Lambert solver is a screening tool. GMAT is a mission design environm
|
|||||||
|
|
||||||
**Integration with data.** If your satellite catalog, contact schedules, or mission database lives in PostgreSQL, Lambert results join directly with those tables. No file export/import cycle.
|
**Integration with data.** If your satellite catalog, contact schedules, or mission database lives in PostgreSQL, Lambert results join directly with those tables. No file export/import cycle.
|
||||||
|
|
||||||
**Accessibility.** GMAT has a steep learning curve — the tutorial for a basic interplanetary transfer is a 50-page document. pg_orbit's Lambert solver requires knowing one SQL function and its six output columns.
|
**Accessibility.** GMAT has a steep learning curve — the tutorial for a basic interplanetary transfer is a 50-page document. pg_orrery's Lambert solver requires knowing one SQL function and its six output columns.
|
||||||
|
|
||||||
## The intended workflow
|
## The intended workflow
|
||||||
|
|
||||||
pg_orbit's Lambert solver fits into a specific phase of mission planning:
|
pg_orrery's Lambert solver fits into a specific phase of mission planning:
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
1. **Screen with pg_orbit.** Generate a pork chop plot across a broad date range. Identify the departure/arrival windows with favorable C3 values. This takes minutes, not hours.
|
1. **Screen with pg_orrery.** Generate a pork chop plot across a broad date range. Identify the departure/arrival windows with favorable C3 values. This takes minutes, not hours.
|
||||||
|
|
||||||
2. **Refine with GMAT.** Take the promising date ranges from step 1 and set up detailed trajectory design in GMAT. Add perturbations, model the departure and arrival spirals, check flyby opportunities.
|
2. **Refine with GMAT.** Take the promising date ranges from step 1 and set up detailed trajectory design in GMAT. Add perturbations, model the departure and arrival spirals, check flyby opportunities.
|
||||||
|
|
||||||
3. **Iterate.** If the refined trajectory shifts the window, go back to pg_orbit to explore the neighborhood in SQL. Faster iteration between broad surveys and detailed analysis.
|
3. **Iterate.** If the refined trajectory shifts the window, go back to pg_orrery to explore the neighborhood in SQL. Faster iteration between broad surveys and detailed analysis.
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
<Aside type="tip" title="Quick C3 comparison">
|
<Aside type="tip" title="Quick C3 comparison">
|
||||||
|
|||||||
@ -2,14 +2,14 @@
|
|||||||
title: From JPL Horizons to SQL
|
title: From JPL Horizons to SQL
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 2
|
order: 2
|
||||||
description: Side-by-side comparison of JPL Horizons API workflows and equivalent pg_orbit SQL queries.
|
description: Side-by-side comparison of JPL Horizons API workflows and equivalent pg_orrery SQL queries.
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Tabs, TabItem, Aside, Steps } from "@astrojs/starlight/components";
|
import { Tabs, TabItem, Aside, Steps } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
JPL Horizons is the gold standard for solar system ephemeris data. Run by the Solar System Dynamics group at the Jet Propulsion Laboratory, it serves precise positions for every known body — planets, moons, asteroids, comets, spacecraft. You can access it through a web interface, telnet, email, or REST API.
|
JPL Horizons is the gold standard for solar system ephemeris data. Run by the Solar System Dynamics group at the Jet Propulsion Laboratory, it serves precise positions for every known body — planets, moons, asteroids, comets, spacecraft. You can access it through a web interface, telnet, email, or REST API.
|
||||||
|
|
||||||
pg_orbit does not replace Horizons. What it does is move the 95% of queries that don't need sub-milliarcsecond precision from a remote API into your local database — with no rate limits, no network latency, and results that join directly with your other tables.
|
pg_orrery does not replace Horizons. What it does is move the 95% of queries that don't need sub-milliarcsecond precision from a remote API into your local database — with no rate limits, no network latency, and results that join directly with your other tables.
|
||||||
|
|
||||||
## Planet ephemeris query
|
## Planet ephemeris query
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ The most common Horizons request: "Where is Mars from my location at this time?"
|
|||||||
single IP. Automated batch jobs that generate thousands of queries risk being
|
single IP. Automated batch jobs that generate thousands of queries risk being
|
||||||
throttled or blocked.
|
throttled or blocked.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
SELECT topo_azimuth(t) AS az,
|
SELECT topo_azimuth(t) AS az,
|
||||||
topo_elevation(t) AS el,
|
topo_elevation(t) AS el,
|
||||||
@ -111,7 +111,7 @@ This is where the workflow difference becomes dramatic. Generating a 24-hour ele
|
|||||||
You're managing thousands of requests, rate limiting, error handling,
|
You're managing thousands of requests, rate limiting, error handling,
|
||||||
and stitching results together.
|
and stitching results together.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
-- Jupiter elevation over 24 hours, 10-minute steps
|
-- Jupiter elevation over 24 hours, 10-minute steps
|
||||||
SELECT t,
|
SELECT t,
|
||||||
@ -152,7 +152,7 @@ This is where the workflow difference becomes dramatic. Generating a 24-hour ele
|
|||||||
|
|
||||||
## Moon positions
|
## Moon positions
|
||||||
|
|
||||||
Horizons excels at moons — it has ephemerides for every known natural satellite. pg_orbit covers the 19 most-observed moons.
|
Horizons excels at moons — it has ephemerides for every known natural satellite. pg_orrery covers the 19 most-observed moons.
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="Horizons (API)">
|
<TabItem label="Horizons (API)">
|
||||||
@ -187,7 +187,7 @@ Horizons excels at moons — it has ephemerides for every known natural satellit
|
|||||||
at 5-minute intervals (say, 8 hours = 96 steps), that's 4 requests or
|
at 5-minute intervals (say, 8 hours = 96 steps), that's 4 requests or
|
||||||
careful batching.
|
careful batching.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
-- All four Galilean moons, right now
|
-- All four Galilean moons, right now
|
||||||
SELECT moon_id,
|
SELECT moon_id,
|
||||||
@ -222,7 +222,7 @@ Horizons excels at moons — it has ephemerides for every known natural satellit
|
|||||||
|
|
||||||
## Lambert transfer survey
|
## Lambert transfer survey
|
||||||
|
|
||||||
This is where the difference is most striking. Horizons doesn't compute transfer orbits directly — you'd use its ephemeris data as input to your own Lambert solver. pg_orbit does both in one step.
|
This is where the difference is most striking. Horizons doesn't compute transfer orbits directly — you'd use its ephemeris data as input to your own Lambert solver. pg_orrery does both in one step.
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="Horizons + Python solver">
|
<TabItem label="Horizons + Python solver">
|
||||||
@ -267,7 +267,7 @@ This is where the difference is most striking. Horizons doesn't compute transfer
|
|||||||
The Horizons queries alone — even with careful batching — take minutes
|
The Horizons queries alone — even with careful batching — take minutes
|
||||||
and risk rate limiting. The Lambert solve is the easy part.
|
and risk rate limiting. The Lambert solve is the easy part.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
-- Full pork chop plot: 25 departure dates x 31 arrival dates = 775 transfers
|
-- Full pork chop plot: 25 departure dates x 31 arrival dates = 775 transfers
|
||||||
SELECT dep::date AS departure,
|
SELECT dep::date AS departure,
|
||||||
@ -288,7 +288,7 @@ This is where the difference is most striking. Horizons doesn't compute transfer
|
|||||||
WHERE tof_days > 90; -- Filter unrealistic short transfers
|
WHERE tof_days > 90; -- Filter unrealistic short transfers
|
||||||
```
|
```
|
||||||
|
|
||||||
pg_orbit computes the planet positions AND solves Lambert internally.
|
pg_orrery computes the planet positions AND solves Lambert internally.
|
||||||
No external API calls. The 775 transfer solutions run in under a second.
|
No external API calls. The 775 transfer solutions run in under a second.
|
||||||
|
|
||||||
Scale it up to a 150x150 grid (22,500 solutions) and it finishes in
|
Scale it up to a 150x150 grid (22,500 solutions) and it finishes in
|
||||||
@ -302,27 +302,27 @@ This is where the difference is most striking. Horizons doesn't compute transfer
|
|||||||
JPL Horizons uses DE441 and applies the full suite of apparent-position corrections. For certain applications, it's the right source — but the accuracy gap has narrowed significantly.
|
JPL Horizons uses DE441 and applies the full suite of apparent-position corrections. For certain applications, it's the right source — but the accuracy gap has narrowed significantly.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
**Apparent-position corrections.** Horizons applies light-time iteration, stellar aberration, and gravitational deflection of light. pg_orbit computes geometric positions. The difference is up to ~20 arcseconds for planets — irrelevant for observation planning, but significant for precision astrometry of apparent coordinates.
|
**Apparent-position corrections.** Horizons applies light-time iteration, stellar aberration, and gravitational deflection of light. pg_orrery computes geometric positions. The difference is up to ~20 arcseconds for planets — irrelevant for observation planning, but significant for precision astrometry of apparent coordinates.
|
||||||
|
|
||||||
**Positional accuracy.** With the built-in VSOP87 pipeline, pg_orbit is accurate to ~1 arcsecond. With [optional DE440/441 support](/guides/de-ephemeris/) (v0.3.0+), pg_orbit reads the same JPL ephemeris data that powers Horizons — matching it at ~0.1 milliarcsecond for geometric positions. The remaining gap is in the apparent-position corrections above, not in the underlying ephemeris.
|
**Positional accuracy.** With the built-in VSOP87 pipeline, pg_orrery is accurate to ~1 arcsecond. With [optional DE440/441 support](/guides/de-ephemeris/) (v0.3.0+), pg_orrery reads the same JPL ephemeris data that powers Horizons — matching it at ~0.1 milliarcsecond for geometric positions. The remaining gap is in the apparent-position corrections above, not in the underlying ephemeris.
|
||||||
|
|
||||||
**Physical properties.** Horizons can return visual magnitude, angular diameter, phase angle, illuminated fraction, and surface brightness. pg_orbit returns geometric position and range.
|
**Physical properties.** Horizons can return visual magnitude, angular diameter, phase angle, illuminated fraction, and surface brightness. pg_orrery returns geometric position and range.
|
||||||
|
|
||||||
**Topographic corrections.** Horizons accounts for Earth's oblateness and topographic features at the observer's location using precise geodetic models. pg_orbit uses a WGS-84 ellipsoid.
|
**Topographic corrections.** Horizons accounts for Earth's oblateness and topographic features at the observer's location using precise geodetic models. pg_orrery uses a WGS-84 ellipsoid.
|
||||||
|
|
||||||
**Body catalog.** Horizons knows about every numbered asteroid, every known comet, and spacecraft past and present. pg_orbit covers the 8 planets, the Sun, the Moon, 19 planetary moons, and whatever comets/asteroids you define with Keplerian elements.
|
**Body catalog.** Horizons knows about every numbered asteroid, every known comet, and spacecraft past and present. pg_orrery covers the 8 planets, the Sun, the Moon, 19 planetary moons, and whatever comets/asteroids you define with Keplerian elements.
|
||||||
|
|
||||||
## Where pg_orbit wins
|
## Where pg_orrery wins
|
||||||
|
|
||||||
**No network dependency.** pg_orbit runs locally, in your database process. No DNS resolution, no TLS handshake, no API parsing. Useful in air-gapped environments, on aircraft, or when Horizons is down for maintenance.
|
**No network dependency.** pg_orrery runs locally, in your database process. No DNS resolution, no TLS handshake, no API parsing. Useful in air-gapped environments, on aircraft, or when Horizons is down for maintenance.
|
||||||
|
|
||||||
**No rate limits.** Horizons is generous but not unlimited. Automated pipelines that generate thousands of queries — pork chop plot surveys, Monte Carlo trajectory analysis, multi-body scheduling — can hit throttling. pg_orbit has no external limits; you're bounded only by your own hardware.
|
**No rate limits.** Horizons is generous but not unlimited. Automated pipelines that generate thousands of queries — pork chop plot surveys, Monte Carlo trajectory analysis, multi-body scheduling — can hit throttling. pg_orrery has no external limits; you're bounded only by your own hardware.
|
||||||
|
|
||||||
**Batch everything locally.** The Lambert transfer example above illustrates this best. What takes hundreds of API calls and minutes of wall-clock time in the Horizons workflow is a single query that runs in seconds.
|
**Batch everything locally.** The Lambert transfer example above illustrates this best. What takes hundreds of API calls and minutes of wall-clock time in the Horizons workflow is a single query that runs in seconds.
|
||||||
|
|
||||||
**Results in your database.** Horizons returns text that you parse and then insert. pg_orbit results are already rows in PostgreSQL — ready to JOIN, index, aggregate, or export.
|
**Results in your database.** Horizons returns text that you parse and then insert. pg_orrery results are already rows in PostgreSQL — ready to JOIN, index, aggregate, or export.
|
||||||
|
|
||||||
**Reproducibility.** A pg_orbit query is deterministic. Given the same inputs, it produces the same output on any PostgreSQL instance with the extension installed. No dependency on the current state of a remote API or the version of its ephemeris files.
|
**Reproducibility.** A pg_orrery query is deterministic. Given the same inputs, it produces the same output on any PostgreSQL instance with the extension installed. No dependency on the current state of a remote API or the version of its ephemeris files.
|
||||||
|
|
||||||
## A practical workflow
|
## A practical workflow
|
||||||
|
|
||||||
@ -333,9 +333,9 @@ For many projects, the right approach uses both.
|
|||||||
|
|
||||||
2. **Enable DE for precision work.** If you need sub-arcsecond planet positions — dish pointing, occultation timing, precision Lambert transfers — [configure a DE file](/guides/de-ephemeris/) and use the `_de()` function variants. You get Horizons-quality positions locally.
|
2. **Enable DE for precision work.** If you need sub-arcsecond planet positions — dish pointing, occultation timing, precision Lambert transfers — [configure a DE file](/guides/de-ephemeris/) and use the `_de()` function variants. You get Horizons-quality positions locally.
|
||||||
|
|
||||||
3. **Use pg_orbit for surveys.** Any time you need positions for many bodies, many timestamps, or many observers — parameter sweeps, scheduling optimization, catalog screening — run it locally. No rate limits, no network dependency.
|
3. **Use pg_orrery for surveys.** Any time you need positions for many bodies, many timestamps, or many observers — parameter sweeps, scheduling optimization, catalog screening — run it locally. No rate limits, no network dependency.
|
||||||
|
|
||||||
4. **Use pg_orbit for integration.** When orbital data needs to join with other database tables — observation logs, equipment schedules, frequency allocations — computing inside PostgreSQL eliminates the ETL step.
|
4. **Use pg_orrery for integration.** When orbital data needs to join with other database tables — observation logs, equipment schedules, frequency allocations — computing inside PostgreSQL eliminates the ETL step.
|
||||||
|
|
||||||
5. **Use Horizons for exotic bodies.** If you need positions for Pluto, numbered asteroids with precise osculating elements, or decommissioned spacecraft, Horizons is the only option.
|
5. **Use Horizons for exotic bodies.** If you need positions for Pluto, numbered asteroids with precise osculating elements, or decommissioned spacecraft, Horizons is the only option.
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
title: From Radio Jupiter Pro to SQL
|
title: From Radio Jupiter Pro to SQL
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 4
|
order: 4
|
||||||
description: Replacing the Windows-only Radio Jupiter Pro desktop app with pg_orbit SQL queries for Jupiter decametric burst prediction.
|
description: Replacing the Windows-only Radio Jupiter Pro desktop app with pg_orrery SQL queries for Jupiter decametric burst prediction.
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Tabs, TabItem, Aside, Steps } from "@astrojs/starlight/components";
|
import { Tabs, TabItem, Aside, Steps } from "@astrojs/starlight/components";
|
||||||
@ -15,7 +15,7 @@ The application works. It has served the community well for years. But it has li
|
|||||||
|
|
||||||
Jupiter emits powerful radio bursts at frequencies between roughly 15 and 38 MHz. The strongest emissions correlate with the orbital position of Io relative to Jupiter and with Jupiter's rotation (the CML). The Carr et al. (1983) model maps source regions — labeled A, B, C, and D — onto an Io phase vs. CML diagram. When the current Io phase and CML fall within a source region, the probability of detecting a burst is elevated.
|
Jupiter emits powerful radio bursts at frequencies between roughly 15 and 38 MHz. The strongest emissions correlate with the orbital position of Io relative to Jupiter and with Jupiter's rotation (the CML). The Carr et al. (1983) model maps source regions — labeled A, B, C, and D — onto an Io phase vs. CML diagram. When the current Io phase and CML fall within a source region, the probability of detecting a burst is elevated.
|
||||||
|
|
||||||
Both Radio Jupiter Pro and pg_orbit use this same underlying model.
|
Both Radio Jupiter Pro and pg_orrery use this same underlying model.
|
||||||
|
|
||||||
## Checking burst probability right now
|
## Checking burst probability right now
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ Both Radio Jupiter Pro and pg_orbit use this same underlying model.
|
|||||||
There is no programmatic access. The result is on screen — you read it
|
There is no programmatic access. The result is on screen — you read it
|
||||||
and decide whether to turn on your receiver.
|
and decide whether to turn on your receiver.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
SELECT round(io_phase_angle(now())::numeric, 1) AS io_phase,
|
SELECT round(io_phase_angle(now())::numeric, 1) AS io_phase,
|
||||||
round(jupiter_cml('40.0N 105.3W 1655m'::observer, now())::numeric, 1) AS cml,
|
round(jupiter_cml('40.0N 105.3W 1655m'::observer, now())::numeric, 1) AS cml,
|
||||||
@ -62,7 +62,7 @@ This is where manual tools hit their limit. "When should I turn on my receiver t
|
|||||||
For a single evening, this takes 5 to 10 minutes of clicking. For
|
For a single evening, this takes 5 to 10 minutes of clicking. For
|
||||||
planning a week of observations, multiply accordingly.
|
planning a week of observations, multiply accordingly.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
-- Best burst windows tonight (6 PM to 6 AM local, 10-minute steps)
|
-- Best burst windows tonight (6 PM to 6 AM local, 10-minute steps)
|
||||||
-- Only show times when Jupiter is above the horizon AND burst probability > 0.2
|
-- Only show times when Jupiter is above the horizon AND burst probability > 0.2
|
||||||
@ -131,7 +131,7 @@ One query scans 30 nights and returns the peak burst probability for each, along
|
|||||||
|
|
||||||
## Correlate with observation logs
|
## Correlate with observation logs
|
||||||
|
|
||||||
If you maintain a database of past observations, pg_orbit lets you answer questions like "did I actually detect bursts when the model predicted them?"
|
If you maintain a database of past observations, pg_orrery lets you answer questions like "did I actually detect bursts when the model predicted them?"
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Compare burst predictions with actual observation results
|
-- Compare burst predictions with actual observation results
|
||||||
@ -186,7 +186,7 @@ This is the kind of analysis that's impossible with Radio Jupiter Pro — it has
|
|||||||
|
|
||||||
## Automated scheduling
|
## Automated scheduling
|
||||||
|
|
||||||
For operators who want to automate their receivers, pg_orbit can drive a scheduling system.
|
For operators who want to automate their receivers, pg_orrery can drive a scheduling system.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Generate tonight's observation schedule
|
-- Generate tonight's observation schedule
|
||||||
@ -254,22 +254,22 @@ Feed the CSV into gnuplot, matplotlib, or any plotting tool to generate the CML/
|
|||||||
For casual, visual use, a dedicated GUI application has advantages.
|
For casual, visual use, a dedicated GUI application has advantages.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
**Visual CML/Io-phase chart.** Radio Jupiter Pro displays the source regions graphically with the current position highlighted. You can see at a glance which source is active and how close the geometry is to a boundary. pg_orbit returns numbers — you build your own visualization.
|
**Visual CML/Io-phase chart.** Radio Jupiter Pro displays the source regions graphically with the current position highlighted. You can see at a glance which source is active and how close the geometry is to a boundary. pg_orrery returns numbers — you build your own visualization.
|
||||||
|
|
||||||
**Audio prediction.** Radio Jupiter Pro includes models for the expected spectral characteristics of different source regions. pg_orbit provides geometry and probability only.
|
**Audio prediction.** Radio Jupiter Pro includes models for the expected spectral characteristics of different source regions. pg_orrery provides geometry and probability only.
|
||||||
|
|
||||||
**Integrated display.** Radio Jupiter Pro shows Jupiter's position, the current CML, Io phase, predicted source, and receiver recommendations all in one window. With pg_orbit, you compose the information yourself from separate function calls.
|
**Integrated display.** Radio Jupiter Pro shows Jupiter's position, the current CML, Io phase, predicted source, and receiver recommendations all in one window. With pg_orrery, you compose the information yourself from separate function calls.
|
||||||
|
|
||||||
**Zero setup.** Install the application, enter your coordinates, and it works. pg_orbit requires PostgreSQL, the extension, and SQL knowledge.
|
**Zero setup.** Install the application, enter your coordinates, and it works. pg_orrery requires PostgreSQL, the extension, and SQL knowledge.
|
||||||
|
|
||||||
## Where pg_orbit wins
|
## Where pg_orrery wins
|
||||||
|
|
||||||
**Platform independence.** Radio Jupiter Pro is Windows-only. pg_orbit runs on any platform that supports PostgreSQL — Linux, macOS, Windows, containers, cloud instances.
|
**Platform independence.** Radio Jupiter Pro is Windows-only. pg_orrery runs on any platform that supports PostgreSQL — Linux, macOS, Windows, containers, cloud instances.
|
||||||
|
|
||||||
**Batch analysis.** Scanning 30 days, 90 days, or a full Jovian apparition at arbitrary resolution is a single `generate_series` query. No manual date advancement.
|
**Batch analysis.** Scanning 30 days, 90 days, or a full Jovian apparition at arbitrary resolution is a single `generate_series` query. No manual date advancement.
|
||||||
|
|
||||||
**Data integration.** Correlating predictions with observation logs, equipment status, weather data, or any other database table is a JOIN. Radio Jupiter Pro has no data export or import capability.
|
**Data integration.** Correlating predictions with observation logs, equipment status, weather data, or any other database table is a JOIN. Radio Jupiter Pro has no data export or import capability.
|
||||||
|
|
||||||
**Automated scheduling.** pg_orbit results feed directly into scripts, cron jobs, or any scheduling system through standard SQL exports. Radio Jupiter Pro requires a human to read the screen.
|
**Automated scheduling.** pg_orrery results feed directly into scripts, cron jobs, or any scheduling system through standard SQL exports. Radio Jupiter Pro requires a human to read the screen.
|
||||||
|
|
||||||
**Reproducibility.** A SQL query is a complete specification. Share it with another JOVE operator and they get the same results for their location by changing the observer coordinates.
|
**Reproducibility.** A SQL query is a complete specification. Share it with another JOVE operator and they get the same results for their location by changing the observer coordinates.
|
||||||
|
|||||||
@ -2,12 +2,12 @@
|
|||||||
title: From Skyfield to SQL
|
title: From Skyfield to SQL
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 1
|
order: 1
|
||||||
description: Side-by-side comparison of Skyfield Python workflows and equivalent pg_orbit SQL queries.
|
description: Side-by-side comparison of Skyfield Python workflows and equivalent pg_orrery SQL queries.
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Tabs, TabItem, Aside, Steps } from "@astrojs/starlight/components";
|
import { Tabs, TabItem, Aside, Steps } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
Skyfield is an excellent Python library for positional astronomy — well-documented, well-tested, and built on the same JPL ephemeris data used by spacecraft navigation teams. If you already use Skyfield, you'll recognize the computations pg_orbit performs. The difference is where they happen.
|
Skyfield is an excellent Python library for positional astronomy — well-documented, well-tested, and built on the same JPL ephemeris data used by spacecraft navigation teams. If you already use Skyfield, you'll recognize the computations pg_orrery performs. The difference is where they happen.
|
||||||
|
|
||||||
This page shows the same tasks done both ways. Not to argue one is better than the other — they make different trade-offs — but to help you decide which fits your workflow.
|
This page shows the same tasks done both ways. Not to argue one is better than the other — they make different trade-offs — but to help you decide which fits your workflow.
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ The most common starting point: where is Jupiter from my location, right now?
|
|||||||
itself is stable, but managing file paths across environments (local dev, CI, production)
|
itself is stable, but managing file paths across environments (local dev, CI, production)
|
||||||
adds friction.
|
adds friction.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
SELECT topo_azimuth(t) AS az,
|
SELECT topo_azimuth(t) AS az,
|
||||||
topo_elevation(t) AS el,
|
topo_elevation(t) AS el,
|
||||||
@ -49,7 +49,7 @@ The most common starting point: where is Jupiter from my location, right now?
|
|||||||
```
|
```
|
||||||
|
|
||||||
No files to download. No timescale object. The VSOP87 coefficients are compiled
|
No files to download. No timescale object. The VSOP87 coefficients are compiled
|
||||||
into the extension — they ship with `CREATE EXTENSION pg_orbit` and never expire.
|
into the extension — they ship with `CREATE EXTENSION pg_orrery` and never expire.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ Observe many satellites at the same timestamp. This is where the architectural d
|
|||||||
schedules, frequency assignments, or historical observation logs that live in
|
schedules, frequency assignments, or historical observation logs that live in
|
||||||
PostgreSQL, you have to bridge two systems.
|
PostgreSQL, you have to bridge two systems.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
-- TLEs already in your database? Just JOIN.
|
-- TLEs already in your database? Just JOIN.
|
||||||
SELECT s.norad_id,
|
SELECT s.norad_id,
|
||||||
@ -165,7 +165,7 @@ Generate positions over a time range — for plotting an elevation profile, buil
|
|||||||
parallelize automatically. For larger sweeps (thousands of satellites, days
|
parallelize automatically. For larger sweeps (thousands of satellites, days
|
||||||
of 1-minute resolution), you manage the iteration yourself.
|
of 1-minute resolution), you manage the iteration yourself.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
SELECT t AS time,
|
SELECT t AS time,
|
||||||
topo_azimuth(obs) AS az,
|
topo_azimuth(obs) AS az,
|
||||||
@ -193,13 +193,13 @@ Generate positions over a time range — for plotting an elevation profile, buil
|
|||||||
```
|
```
|
||||||
|
|
||||||
Then plot with whatever tool you prefer — gnuplot, matplotlib, Observable,
|
Then plot with whatever tool you prefer — gnuplot, matplotlib, Observable,
|
||||||
a spreadsheet. pg_orbit produces data; visualization is a separate concern.
|
a spreadsheet. pg_orrery produces data; visualization is a separate concern.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
## Pass prediction
|
## Pass prediction
|
||||||
|
|
||||||
Predict when a satellite will be visible from a location. This is where Skyfield and pg_orbit take genuinely different approaches.
|
Predict when a satellite will be visible from a location. This is where Skyfield and pg_orrery take genuinely different approaches.
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="Skyfield (Python)">
|
<TabItem label="Skyfield (Python)">
|
||||||
@ -229,7 +229,7 @@ Predict when a satellite will be visible from a location. This is where Skyfield
|
|||||||
elevation crosses the threshold. This gives sub-second precision for AOS and
|
elevation crosses the threshold. This gives sub-second precision for AOS and
|
||||||
LOS times.
|
LOS times.
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pg_orbit (SQL)">
|
<TabItem label="pg_orrery (SQL)">
|
||||||
```sql
|
```sql
|
||||||
WITH iss AS (
|
WITH iss AS (
|
||||||
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
||||||
@ -270,20 +270,20 @@ Predict when a satellite will be visible from a location. This is where Skyfield
|
|||||||
## Where Skyfield wins
|
## Where Skyfield wins
|
||||||
|
|
||||||
<Aside type="note" title="Skyfield has real advantages">
|
<Aside type="note" title="Skyfield has real advantages">
|
||||||
pg_orbit does not replace Skyfield for all use cases. Be clear about where the trade-offs fall.
|
pg_orrery does not replace Skyfield for all use cases. Be clear about where the trade-offs fall.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
**Apparent-position corrections.** Skyfield uses the full IAU 2000A nutation model, polar motion corrections, delta-T from IERS data, and iterates for light-time and stellar aberration. pg_orbit v0.3.0 can [optionally use DE441](/guides/de-ephemeris/) for the same underlying geometric accuracy (~0.1 milliarcsecond), but Skyfield still applies corrections that pg_orbit does not — corrections that matter for precision apparent-coordinate work like occultation timing or sub-arcsecond astrometry.
|
**Apparent-position corrections.** Skyfield uses the full IAU 2000A nutation model, polar motion corrections, delta-T from IERS data, and iterates for light-time and stellar aberration. pg_orrery v0.3.0 can [optionally use DE441](/guides/de-ephemeris/) for the same underlying geometric accuracy (~0.1 milliarcsecond), but Skyfield still applies corrections that pg_orrery does not — corrections that matter for precision apparent-coordinate work like occultation timing or sub-arcsecond astrometry.
|
||||||
|
|
||||||
**Rise/set finding.** `find_events()` uses numerical root-finding to pinpoint the exact moment a body crosses an elevation threshold. pg_orbit's `predict_passes` uses a step-and-refine approach that's fast for batches but less precise for individual events.
|
**Rise/set finding.** `find_events()` uses numerical root-finding to pinpoint the exact moment a body crosses an elevation threshold. pg_orrery's `predict_passes` uses a step-and-refine approach that's fast for batches but less precise for individual events.
|
||||||
|
|
||||||
**Aberration and light-time.** Skyfield iterates to correct for light travel time and applies stellar aberration. pg_orbit uses geometric positions without light-time iteration — the difference is under 20 arcseconds for planets and irrelevant for satellite tracking, but it matters for some applications.
|
**Aberration and light-time.** Skyfield iterates to correct for light travel time and applies stellar aberration. pg_orrery uses geometric positions without light-time iteration — the difference is under 20 arcseconds for planets and irrelevant for satellite tracking, but it matters for some applications.
|
||||||
|
|
||||||
**Visualization integration.** Skyfield works directly with matplotlib, numpy, and the rest of the Python scientific stack. pg_orbit produces rows and columns — you export to CSV or JSON and then plot separately.
|
**Visualization integration.** Skyfield works directly with matplotlib, numpy, and the rest of the Python scientific stack. pg_orrery produces rows and columns — you export to CSV or JSON and then plot separately.
|
||||||
|
|
||||||
**Extensibility.** Skyfield handles arbitrary BSP kernels — Pluto, spacecraft, asteroids with precise ephemerides. pg_orbit's body catalog is fixed at compile time.
|
**Extensibility.** Skyfield handles arbitrary BSP kernels — Pluto, spacecraft, asteroids with precise ephemerides. pg_orrery's body catalog is fixed at compile time.
|
||||||
|
|
||||||
## Where pg_orbit wins
|
## Where pg_orrery wins
|
||||||
|
|
||||||
**No file management.** No BSP kernels, no timescale data files, no expiring Earth orientation parameters. The computation ships with the extension.
|
**No file management.** No BSP kernels, no timescale data files, no expiring Earth orientation parameters. The computation ships with the extension.
|
||||||
|
|
||||||
@ -293,16 +293,16 @@ pg_orbit does not replace Skyfield for all use cases. Be clear about where the t
|
|||||||
|
|
||||||
**Automatic parallelism.** PostgreSQL's query planner distributes PARALLEL SAFE functions across available cores. You don't manage threads or multiprocessing pools.
|
**Automatic parallelism.** PostgreSQL's query planner distributes PARALLEL SAFE functions across available cores. You don't manage threads or multiprocessing pools.
|
||||||
|
|
||||||
**Reproducibility.** A SQL query is a complete, self-contained specification of a computation. No virtual environment, no package versions, no file paths. The same query produces the same result on any PostgreSQL instance with pg_orbit installed.
|
**Reproducibility.** A SQL query is a complete, self-contained specification of a computation. No virtual environment, no package versions, no file paths. The same query produces the same result on any PostgreSQL instance with pg_orrery installed.
|
||||||
|
|
||||||
## Migrating gradually
|
## Migrating gradually
|
||||||
|
|
||||||
You don't have to choose one or the other. A practical migration path:
|
You don't have to choose one or the other. A practical migration path:
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
1. **Keep Skyfield for apparent-position work.** Anything requiring aberration corrections, polar motion, nutation at IAU 2000A level, or custom BSP kernels stays in Python. For raw geometric position accuracy, pg_orbit with [DE enabled](/guides/de-ephemeris/) matches Skyfield.
|
1. **Keep Skyfield for apparent-position work.** Anything requiring aberration corrections, polar motion, nutation at IAU 2000A level, or custom BSP kernels stays in Python. For raw geometric position accuracy, pg_orrery with [DE enabled](/guides/de-ephemeris/) matches Skyfield.
|
||||||
|
|
||||||
2. **Move batch observation to SQL.** If you're computing positions for hundreds of objects to filter or correlate with database records, pg_orbit eliminates the Python-to-PostgreSQL round trip.
|
2. **Move batch observation to SQL.** If you're computing positions for hundreds of objects to filter or correlate with database records, pg_orrery eliminates the Python-to-PostgreSQL round trip.
|
||||||
|
|
||||||
3. **Move scheduling to SQL.** Pass prediction and visibility windows over time ranges are natural `generate_series` + `predict_passes` queries.
|
3. **Move scheduling to SQL.** Pass prediction and visibility windows over time ranges are natural `generate_series` + `predict_passes` queries.
|
||||||
|
|
||||||
|
|||||||
@ -2,16 +2,16 @@
|
|||||||
title: The SQL Advantage
|
title: The SQL Advantage
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 5
|
order: 5
|
||||||
description: SQL patterns that make pg_orbit uniquely powerful — generate_series, CROSS JOIN, PARALLEL SAFE, materialized views, GiST indexes, and more.
|
description: SQL patterns that make pg_orrery uniquely powerful — generate_series, CROSS JOIN, PARALLEL SAFE, materialized views, GiST indexes, and more.
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Tabs, TabItem, Aside } from "@astrojs/starlight/components";
|
import { Tabs, TabItem, Aside } from "@astrojs/starlight/components";
|
||||||
|
|
||||||
The previous pages compared pg_orbit with specific tools — Skyfield, JPL Horizons, GMAT, Radio Jupiter Pro. This page steps back and looks at the thing they all have in common: none of them are SQL.
|
The previous pages compared pg_orrery with specific tools — Skyfield, JPL Horizons, GMAT, Radio Jupiter Pro. This page steps back and looks at the thing they all have in common: none of them are SQL.
|
||||||
|
|
||||||
That sounds obvious, but the implications are deeper than "you can write queries instead of scripts." SQL brings a set of patterns — compositional, declarative, and optimized by decades of database engine development — that change what's practical to compute.
|
That sounds obvious, but the implications are deeper than "you can write queries instead of scripts." SQL brings a set of patterns — compositional, declarative, and optimized by decades of database engine development — that change what's practical to compute.
|
||||||
|
|
||||||
Each pattern below includes a concrete pg_orbit example.
|
Each pattern below includes a concrete pg_orrery example.
|
||||||
|
|
||||||
## generate_series: time series without loops
|
## generate_series: time series without loops
|
||||||
|
|
||||||
@ -179,10 +179,10 @@ Radio observation is affected by weather differently than optical — rain matte
|
|||||||
|
|
||||||
## PARALLEL SAFE: automatic multi-core
|
## PARALLEL SAFE: automatic multi-core
|
||||||
|
|
||||||
All pg_orbit computation functions are declared `PARALLEL SAFE`. This means PostgreSQL's query planner can distribute work across multiple CPU cores without any explicit threading or multiprocessing code.
|
All pg_orrery computation functions are declared `PARALLEL SAFE`. This means PostgreSQL's query planner can distribute work across multiple CPU cores without any explicit threading or multiprocessing code.
|
||||||
|
|
||||||
<Aside type="tip" title="When parallelism kicks in">
|
<Aside type="tip" title="When parallelism kicks in">
|
||||||
PostgreSQL enables parallel query execution when the estimated cost exceeds `parallel_tuple_cost * min_parallel_table_scan_size`. For pg_orbit, this typically happens when scanning tables with hundreds or thousands of rows, or when `generate_series` produces large result sets. You can check the execution plan with `EXPLAIN ANALYZE`.
|
PostgreSQL enables parallel query execution when the estimated cost exceeds `parallel_tuple_cost * min_parallel_table_scan_size`. For pg_orrery, this typically happens when scanning tables with hundreds or thousands of rows, or when `generate_series` produces large result sets. You can check the execution plan with `EXPLAIN ANALYZE`.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@ -259,7 +259,7 @@ The `CONCURRENTLY` option requires a unique index but allows queries to continue
|
|||||||
|
|
||||||
## COPY TO: export to anything
|
## COPY TO: export to anything
|
||||||
|
|
||||||
pg_orbit produces structured data. Getting it out of PostgreSQL and into other tools is a `COPY` statement.
|
pg_orrery produces structured data. Getting it out of PostgreSQL and into other tools is a `COPY` statement.
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="CSV">
|
<TabItem label="CSV">
|
||||||
@ -303,7 +303,7 @@ CSV feeds into gnuplot, matplotlib, Excel, R, Observable, or any visualization t
|
|||||||
|
|
||||||
## GiST INDEX: spatial queries on orbital elements
|
## GiST INDEX: spatial queries on orbital elements
|
||||||
|
|
||||||
pg_orbit's TLE type supports GiST indexing. This enables spatial-style queries over orbital elements — finding satellites that share similar orbits or screening for conjunction risks.
|
pg_orrery's TLE type supports GiST indexing. This enables spatial-style queries over orbital elements — finding satellites that share similar orbits or screening for conjunction risks.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Create a GiST index on the satellite catalog
|
-- Create a GiST index on the satellite catalog
|
||||||
@ -344,7 +344,7 @@ This is a screening filter, not a precision conjunction analysis. It identifies
|
|||||||
|
|
||||||
## Provider switching: accuracy when you need it
|
## Provider switching: accuracy when you need it
|
||||||
|
|
||||||
pg_orbit v0.3.0 has two ephemeris providers — the built-in VSOP87 pipeline (~1 arcsecond) and optional [JPL DE440/441](/guides/de-ephemeris/) (~0.1 milliarcsecond). The SQL interface makes switching between them a one-character change.
|
pg_orrery v0.3.0 has two ephemeris providers — the built-in VSOP87 pipeline (~1 arcsecond) and optional [JPL DE440/441](/guides/de-ephemeris/) (~0.1 milliarcsecond). The SQL interface makes switching between them a one-character change.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- VSOP87 (built-in, IMMUTABLE, no setup)
|
-- VSOP87 (built-in, IMMUTABLE, no setup)
|
||||||
@ -499,4 +499,4 @@ This query:
|
|||||||
|
|
||||||
In a traditional workflow, each of these steps would be a separate script, a separate data file, and a separate tool. In SQL, they compose into a single declarative statement that the database engine optimizes and parallelizes.
|
In a traditional workflow, each of these steps would be a separate script, a separate data file, and a separate tool. In SQL, they compose into a single declarative statement that the database engine optimizes and parallelizes.
|
||||||
|
|
||||||
That's the advantage. Not that SQL is a better programming language — it isn't. But for the specific pattern of "evaluate a function over structured parameter spaces and correlate the results with existing data," SQL is exactly the right tool. pg_orbit puts 68 functions inside that tool — from 17ms satellite batch propagation to sub-milliarcsecond DE441 planet positions — and every one of them composes with every SQL pattern on this page.
|
That's the advantage. Not that SQL is a better programming language — it isn't. But for the specific pattern of "evaluate a function over structured parameter spaces and correlate the results with existing data," SQL is exactly the right tool. pg_orrery puts 68 functions inside that tool — from 17ms satellite batch propagation to sub-milliarcsecond DE441 planet positions — and every one of them composes with every SQL pattern on this page.
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RenderFunctionInput } from "astro-opengraph-images";
|
import type { RenderFunctionInput } from "astro-opengraph-images";
|
||||||
|
|
||||||
export async function pgOrbitOgImage({
|
export async function pgOrreryOgImage({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
}: RenderFunctionInput): Promise<React.ReactNode> {
|
}: RenderFunctionInput): Promise<React.ReactNode> {
|
||||||
@ -147,7 +147,7 @@ export async function pgOrbitOgImage({
|
|||||||
color: "#e2e8f0",
|
color: "#e2e8f0",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
pg_orbit
|
pg_orrery
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -165,7 +165,7 @@ export async function pgOrbitOgImage({
|
|||||||
color: "#556677",
|
color: "#556677",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
pg-orbit.warehack.ing
|
pg-orrery.warehack.ing
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
@import "@fontsource/jetbrains-mono/400.css";
|
@import "@fontsource/jetbrains-mono/400.css";
|
||||||
@import "@fontsource/jetbrains-mono/500.css";
|
@import "@fontsource/jetbrains-mono/500.css";
|
||||||
|
|
||||||
/* pg_orbit palette — deep space observation theme */
|
/* pg_orrery palette — deep space observation theme */
|
||||||
:root {
|
:root {
|
||||||
--sl-font: "Inter", system-ui, -apple-system, sans-serif;
|
--sl-font: "Inter", system-ui, -apple-system, sans-serif;
|
||||||
--sl-font-mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
|
--sl-font-mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
comment = 'Orbital mechanics types and functions for PostgreSQL'
|
|
||||||
default_version = '0.3.0'
|
|
||||||
module_pathname = '$libdir/pg_orbit'
|
|
||||||
relocatable = true
|
|
||||||
4
pg_orrery.control
Normal file
4
pg_orrery.control
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
comment = 'A database orrery — celestial mechanics types and functions for PostgreSQL'
|
||||||
|
default_version = '0.3.0'
|
||||||
|
module_pathname = '$libdir/pg_orrery'
|
||||||
|
relocatable = true
|
||||||
@ -1,4 +1,4 @@
|
|||||||
-- pg_orbit 0.1.0 -> 0.2.0 migration
|
-- pg_orrery 0.1.0 -> 0.2.0 migration
|
||||||
--
|
--
|
||||||
-- Phase 1: Stars, comets, and Keplerian propagation.
|
-- Phase 1: Stars, comets, and Keplerian propagation.
|
||||||
-- Adds heliocentric type, star observation, and two-body propagation.
|
-- Adds heliocentric type, star observation, and two-body propagation.
|
||||||
@ -1,4 +1,4 @@
|
|||||||
-- pg_orbit -- Orbital mechanics types and functions for PostgreSQL
|
-- pg_orrery -- Orbital mechanics types and functions for PostgreSQL
|
||||||
--
|
--
|
||||||
-- Types: tle, eci_position, geodetic, topocentric, observer, pass_event
|
-- Types: tle, eci_position, geodetic, topocentric, observer, pass_event
|
||||||
-- Provides SGP4/SDP4 propagation, coordinate transforms, pass prediction,
|
-- Provides SGP4/SDP4 propagation, coordinate transforms, pass prediction,
|
||||||
@ -1,4 +1,4 @@
|
|||||||
-- pg_orbit 0.2.0 -> 0.3.0 migration
|
-- pg_orrery 0.2.0 -> 0.3.0 migration
|
||||||
--
|
--
|
||||||
-- Adds optional JPL DE440/441 ephemeris functions.
|
-- Adds optional JPL DE440/441 ephemeris functions.
|
||||||
-- Existing VSOP87/ELP2000-82B functions are unchanged (still IMMUTABLE).
|
-- Existing VSOP87/ELP2000-82B functions are unchanged (still IMMUTABLE).
|
||||||
@ -76,11 +76,11 @@ COMMENT ON FUNCTION mars_moon_observe_de(int4, observer, timestamptz) IS
|
|||||||
|
|
||||||
-- Diagnostic function
|
-- Diagnostic function
|
||||||
|
|
||||||
CREATE FUNCTION pg_orbit_ephemeris_info(
|
CREATE FUNCTION pg_orrery_ephemeris_info(
|
||||||
OUT provider text, OUT file_path text,
|
OUT provider text, OUT file_path text,
|
||||||
OUT start_jd float8, OUT end_jd float8,
|
OUT start_jd float8, OUT end_jd float8,
|
||||||
OUT version int4, OUT au_km float8
|
OUT version int4, OUT au_km float8
|
||||||
) RETURNS RECORD
|
) RETURNS RECORD
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C STABLE PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C STABLE PARALLEL SAFE;
|
||||||
COMMENT ON FUNCTION pg_orbit_ephemeris_info() IS
|
COMMENT ON FUNCTION pg_orrery_ephemeris_info() IS
|
||||||
'Returns current ephemeris provider status: VSOP87 or JPL_DE with file path, JD range, version, and AU value.';
|
'Returns current ephemeris provider status: VSOP87 or JPL_DE with file path, JD range, version, and AU value.';
|
||||||
@ -1,4 +1,4 @@
|
|||||||
-- pg_orbit -- Orbital mechanics types and functions for PostgreSQL
|
-- pg_orrery -- Orbital mechanics types and functions for PostgreSQL
|
||||||
--
|
--
|
||||||
-- Types: tle, eci_position, geodetic, topocentric, observer, pass_event
|
-- Types: tle, eci_position, geodetic, topocentric, observer, pass_event
|
||||||
-- Provides SGP4/SDP4 propagation, coordinate transforms, pass prediction,
|
-- Provides SGP4/SDP4 propagation, coordinate transforms, pass prediction,
|
||||||
@ -514,7 +514,7 @@ CREATE OPERATOR CLASS tle_ops
|
|||||||
FUNCTION 6 gist_tle_picksplit(internal, internal),
|
FUNCTION 6 gist_tle_picksplit(internal, internal),
|
||||||
FUNCTION 7 gist_tle_same(internal, internal, internal),
|
FUNCTION 7 gist_tle_same(internal, internal, internal),
|
||||||
FUNCTION 8 gist_tle_distance(internal, tle, smallint, oid, internal);
|
FUNCTION 8 gist_tle_distance(internal, tle, smallint, oid, internal);
|
||||||
-- pg_orbit 0.1.0 -> 0.2.0 migration
|
-- pg_orrery 0.1.0 -> 0.2.0 migration
|
||||||
--
|
--
|
||||||
-- Phase 1: Stars, comets, and Keplerian propagation.
|
-- Phase 1: Stars, comets, and Keplerian propagation.
|
||||||
-- Adds heliocentric type, star observation, and two-body propagation.
|
-- Adds heliocentric type, star observation, and two-body propagation.
|
||||||
@ -1,4 +1,4 @@
|
|||||||
-- pg_orbit -- Orbital mechanics types and functions for PostgreSQL
|
-- pg_orrery -- Orbital mechanics types and functions for PostgreSQL
|
||||||
--
|
--
|
||||||
-- Types: tle, eci_position, geodetic, topocentric, observer, pass_event
|
-- Types: tle, eci_position, geodetic, topocentric, observer, pass_event
|
||||||
-- Provides SGP4/SDP4 propagation, coordinate transforms, pass prediction,
|
-- Provides SGP4/SDP4 propagation, coordinate transforms, pass prediction,
|
||||||
@ -514,7 +514,7 @@ CREATE OPERATOR CLASS tle_ops
|
|||||||
FUNCTION 6 gist_tle_picksplit(internal, internal),
|
FUNCTION 6 gist_tle_picksplit(internal, internal),
|
||||||
FUNCTION 7 gist_tle_same(internal, internal, internal),
|
FUNCTION 7 gist_tle_same(internal, internal, internal),
|
||||||
FUNCTION 8 gist_tle_distance(internal, tle, smallint, oid, internal);
|
FUNCTION 8 gist_tle_distance(internal, tle, smallint, oid, internal);
|
||||||
-- pg_orbit 0.1.0 -> 0.2.0 migration
|
-- pg_orrery 0.1.0 -> 0.2.0 migration
|
||||||
--
|
--
|
||||||
-- Phase 1: Stars, comets, and Keplerian propagation.
|
-- Phase 1: Stars, comets, and Keplerian propagation.
|
||||||
-- Adds heliocentric type, star observation, and two-body propagation.
|
-- Adds heliocentric type, star observation, and two-body propagation.
|
||||||
@ -702,7 +702,7 @@ CREATE FUNCTION lambert_c3(int4, int4, timestamptz, timestamptz) RETURNS float8
|
|||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
COMMENT ON FUNCTION lambert_c3(int4, int4, timestamptz, timestamptz) IS
|
COMMENT ON FUNCTION lambert_c3(int4, int4, timestamptz, timestamptz) IS
|
||||||
'Departure C3 (km^2/s^2) for a Lambert transfer. Returns NULL if solver fails. For pork chop plots.';
|
'Departure C3 (km^2/s^2) for a Lambert transfer. Returns NULL if solver fails. For pork chop plots.';
|
||||||
-- pg_orbit 0.2.0 -> 0.3.0 migration
|
-- pg_orrery 0.2.0 -> 0.3.0 migration
|
||||||
--
|
--
|
||||||
-- Adds optional JPL DE440/441 ephemeris functions.
|
-- Adds optional JPL DE440/441 ephemeris functions.
|
||||||
-- Existing VSOP87/ELP2000-82B functions are unchanged (still IMMUTABLE).
|
-- Existing VSOP87/ELP2000-82B functions are unchanged (still IMMUTABLE).
|
||||||
@ -780,11 +780,11 @@ COMMENT ON FUNCTION mars_moon_observe_de(int4, observer, timestamptz) IS
|
|||||||
|
|
||||||
-- Diagnostic function
|
-- Diagnostic function
|
||||||
|
|
||||||
CREATE FUNCTION pg_orbit_ephemeris_info(
|
CREATE FUNCTION pg_orrery_ephemeris_info(
|
||||||
OUT provider text, OUT file_path text,
|
OUT provider text, OUT file_path text,
|
||||||
OUT start_jd float8, OUT end_jd float8,
|
OUT start_jd float8, OUT end_jd float8,
|
||||||
OUT version int4, OUT au_km float8
|
OUT version int4, OUT au_km float8
|
||||||
) RETURNS RECORD
|
) RETURNS RECORD
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C STABLE PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C STABLE PARALLEL SAFE;
|
||||||
COMMENT ON FUNCTION pg_orbit_ephemeris_info() IS
|
COMMENT ON FUNCTION pg_orrery_ephemeris_info() IS
|
||||||
'Returns current ephemeris provider status: VSOP87 or JPL_DE with file path, JD range, version, and AU value.';
|
'Returns current ephemeris provider status: VSOP87 or JPL_DE with file path, JD range, version, and AU value.';
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* astro_math.h -- Shared astronomical math for pg_orbit
|
* astro_math.h -- Shared astronomical math for pg_orrery
|
||||||
*
|
*
|
||||||
* Static inline functions used by star_funcs.c, kepler_funcs.c,
|
* Static inline functions used by star_funcs.c, kepler_funcs.c,
|
||||||
* and future planet/moon observation code.
|
* and future planet/moon observation code.
|
||||||
@ -8,8 +8,8 @@
|
|||||||
* cross-translation-unit symbol coupling.
|
* cross-translation-unit symbol coupling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_ASTRO_MATH_H
|
#ifndef PG_ORRERY_ASTRO_MATH_H
|
||||||
#define PG_ORBIT_ASTRO_MATH_H
|
#define PG_ORRERY_ASTRO_MATH_H
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
@ -217,4 +217,4 @@ observe_from_geocentric(const double geo_ecl_au[3], double jd,
|
|||||||
result->range_rate = 0.0; /* no velocity computation yet */
|
result->range_rate = 0.0; /* no velocity computation yet */
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* PG_ORBIT_ASTRO_MATH_H */
|
#endif /* PG_ORRERY_ASTRO_MATH_H */
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* coord_funcs.c -- Coordinate transform functions for pg_orbit
|
* coord_funcs.c -- Coordinate transform functions for pg_orrery
|
||||||
*
|
*
|
||||||
* TEME -> WGS-84 geodetic and TEME -> topocentric transforms.
|
* TEME -> WGS-84 geodetic and TEME -> topocentric transforms.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -47,7 +47,7 @@ PG_FUNCTION_INFO_V1(galilean_observe_de);
|
|||||||
PG_FUNCTION_INFO_V1(saturn_moon_observe_de);
|
PG_FUNCTION_INFO_V1(saturn_moon_observe_de);
|
||||||
PG_FUNCTION_INFO_V1(uranus_moon_observe_de);
|
PG_FUNCTION_INFO_V1(uranus_moon_observe_de);
|
||||||
PG_FUNCTION_INFO_V1(mars_moon_observe_de);
|
PG_FUNCTION_INFO_V1(mars_moon_observe_de);
|
||||||
PG_FUNCTION_INFO_V1(pg_orbit_ephemeris_info);
|
PG_FUNCTION_INFO_V1(pg_orrery_ephemeris_info);
|
||||||
|
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
@ -469,7 +469,7 @@ lambert_c3_de(PG_FUNCTION_ARGS)
|
|||||||
* and Earth positions. Falls back to VSOP87 if DE is unavailable.
|
* and Earth positions. Falls back to VSOP87 if DE is unavailable.
|
||||||
*
|
*
|
||||||
* moon_rel[3]: moon position relative to parent (ecliptic J2000, AU)
|
* moon_rel[3]: moon position relative to parent (ecliptic J2000, AU)
|
||||||
* parent_body_id: pg_orbit body ID of parent (5=Jupiter, 6=Saturn, etc.)
|
* parent_body_id: pg_orrery body ID of parent (5=Jupiter, 6=Saturn, etc.)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
observe_moon_de(const double moon_rel[3], int parent_body_id,
|
observe_moon_de(const double moon_rel[3], int parent_body_id,
|
||||||
@ -611,14 +611,14 @@ mars_moon_observe_de(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* pg_orbit_ephemeris_info() -> RECORD
|
* pg_orrery_ephemeris_info() -> RECORD
|
||||||
*
|
*
|
||||||
* Diagnostic function: returns current ephemeris provider status.
|
* Diagnostic function: returns current ephemeris provider status.
|
||||||
* STABLE (not STRICT — no args), PARALLEL SAFE.
|
* STABLE (not STRICT — no args), PARALLEL SAFE.
|
||||||
* ================================================================
|
* ================================================================
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
pg_orbit_ephemeris_info(PG_FUNCTION_ARGS)
|
pg_orrery_ephemeris_info(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
Datum values[6];
|
Datum values[6];
|
||||||
|
|||||||
@ -12,8 +12,8 @@
|
|||||||
* DE405/LE405" (Standish 1998) — format description.
|
* DE405/LE405" (Standish 1998) — format description.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_DE_READER_H
|
#ifndef PG_ORRERY_DE_READER_H
|
||||||
#define PG_ORBIT_DE_READER_H
|
#define PG_ORRERY_DE_READER_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@ -137,4 +137,4 @@ void de_reader_close(de_handle *h);
|
|||||||
*/
|
*/
|
||||||
double de_reader_get_const(de_handle *h, const char *name);
|
double de_reader_get_const(de_handle *h, const char *name);
|
||||||
|
|
||||||
#endif /* PG_ORBIT_DE_READER_H */
|
#endif /* PG_ORRERY_DE_READER_H */
|
||||||
|
|||||||
@ -42,7 +42,7 @@ the original Fortran code:
|
|||||||
2) units are: julian day, AU
|
2) units are: julian day, AU
|
||||||
|
|
||||||
This file is derived from the Stellarium project
|
This file is derived from the Stellarium project
|
||||||
(https://github.com/Stellarium/stellarium). Modified for pg_orbit:
|
(https://github.com/Stellarium/stellarium). Modified for pg_orrery:
|
||||||
removed static mutable state and caching/interpolation for thread safety
|
removed static mutable state and caching/interpolation for thread safety
|
||||||
(PostgreSQL PARALLEL SAFE). All computation is done fresh on each call.
|
(PostgreSQL PARALLEL SAFE). All computation is done fresh on each call.
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ the original Fortran code:
|
|||||||
2) units are: julian day, AU
|
2) units are: julian day, AU
|
||||||
|
|
||||||
This file is derived from the Stellarium project
|
This file is derived from the Stellarium project
|
||||||
(https://github.com/Stellarium/stellarium). Modified for pg_orbit:
|
(https://github.com/Stellarium/stellarium). Modified for pg_orrery:
|
||||||
removed static mutable state for thread safety (PostgreSQL PARALLEL SAFE).
|
removed static mutable state for thread safety (PostgreSQL PARALLEL SAFE).
|
||||||
|
|
||||||
****************************************************************/
|
****************************************************************/
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* eph_provider.c -- Ephemeris provider management for pg_orbit
|
* eph_provider.c -- Ephemeris provider management for pg_orrery
|
||||||
*
|
*
|
||||||
* Manages the lifecycle of the optional JPL DE ephemeris reader
|
* Manages the lifecycle of the optional JPL DE ephemeris reader
|
||||||
* within PostgreSQL's multi-process architecture.
|
* within PostgreSQL's multi-process architecture.
|
||||||
@ -47,7 +47,7 @@ void
|
|||||||
eph_register_gucs(void)
|
eph_register_gucs(void)
|
||||||
{
|
{
|
||||||
DefineCustomStringVariable(
|
DefineCustomStringVariable(
|
||||||
"pg_orbit.ephemeris_path",
|
"pg_orrery.ephemeris_path",
|
||||||
"Path to JPL DE binary ephemeris file (DE430/DE440/DE441).",
|
"Path to JPL DE binary ephemeris file (DE430/DE440/DE441).",
|
||||||
"When set, enables high-precision _de() function variants. "
|
"When set, enables high-precision _de() function variants. "
|
||||||
"Default empty = no DE support (VSOP87 only). "
|
"Default empty = no DE support (VSOP87 only). "
|
||||||
@ -89,17 +89,17 @@ ensure_de_init(void)
|
|||||||
if (de_handle_ptr == NULL)
|
if (de_handle_ptr == NULL)
|
||||||
{
|
{
|
||||||
ereport(NOTICE,
|
ereport(NOTICE,
|
||||||
(errmsg("pg_orbit: cannot open DE ephemeris at \"%s\" (error %d), "
|
(errmsg("pg_orrery: cannot open DE ephemeris at \"%s\" (error %d), "
|
||||||
"DE functions will fall back to VSOP87",
|
"DE functions will fall back to VSOP87",
|
||||||
eph_path_guc, errcode)));
|
eph_path_guc, errcode)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verify AU consistency with pg_orbit's compiled-in value */
|
/* Verify AU consistency with pg_orrery's compiled-in value */
|
||||||
if (fabs(de_handle_ptr->au_km - AU_KM) > 0.01)
|
if (fabs(de_handle_ptr->au_km - AU_KM) > 0.01)
|
||||||
{
|
{
|
||||||
ereport(NOTICE,
|
ereport(NOTICE,
|
||||||
(errmsg("pg_orbit: DE ephemeris AU = %.3f km, expected %.3f km; "
|
(errmsg("pg_orrery: DE ephemeris AU = %.3f km, expected %.3f km; "
|
||||||
"DE functions will fall back to VSOP87",
|
"DE functions will fall back to VSOP87",
|
||||||
de_handle_ptr->au_km, (double)AU_KM)));
|
de_handle_ptr->au_km, (double)AU_KM)));
|
||||||
de_reader_close(de_handle_ptr);
|
de_reader_close(de_handle_ptr);
|
||||||
@ -108,7 +108,7 @@ ensure_de_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ereport(DEBUG1,
|
ereport(DEBUG1,
|
||||||
(errmsg("pg_orbit: DE%d ephemeris loaded, JD %.1f to %.1f",
|
(errmsg("pg_orrery: DE%d ephemeris loaded, JD %.1f to %.1f",
|
||||||
de_handle_ptr->de_version,
|
de_handle_ptr->de_version,
|
||||||
de_handle_ptr->start_jd,
|
de_handle_ptr->start_jd,
|
||||||
de_handle_ptr->end_jd)));
|
de_handle_ptr->end_jd)));
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* eph_provider.h -- Ephemeris provider dispatch for pg_orbit
|
* eph_provider.h -- Ephemeris provider dispatch for pg_orrery
|
||||||
*
|
*
|
||||||
* Manages the optional JPL DE ephemeris alongside the compiled-in
|
* Manages the optional JPL DE ephemeris alongside the compiled-in
|
||||||
* VSOP87 and ELP2000-82B theories.
|
* VSOP87 and ELP2000-82B theories.
|
||||||
@ -11,8 +11,8 @@
|
|||||||
* - No shared state between PostgreSQL backends
|
* - No shared state between PostgreSQL backends
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_EPH_PROVIDER_H
|
#ifndef PG_ORRERY_EPH_PROVIDER_H
|
||||||
#define PG_ORBIT_EPH_PROVIDER_H
|
#define PG_ORRERY_EPH_PROVIDER_H
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
@ -28,7 +28,7 @@ typedef enum
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the GUC variable (pg_orbit.ephemeris_path).
|
* Initialize the GUC variable (pg_orrery.ephemeris_path).
|
||||||
* Called from _PG_init(). Does NOT open the DE file.
|
* Called from _PG_init(). Does NOT open the DE file.
|
||||||
*/
|
*/
|
||||||
void eph_register_gucs(void);
|
void eph_register_gucs(void);
|
||||||
@ -63,7 +63,7 @@ const char *eph_get_path(void);
|
|||||||
/*
|
/*
|
||||||
* Get planet heliocentric ecliptic J2000 position via DE.
|
* Get planet heliocentric ecliptic J2000 position via DE.
|
||||||
*
|
*
|
||||||
* body_id: pg_orbit body ID (1=Mercury..8=Neptune)
|
* body_id: pg_orrery body ID (1=Mercury..8=Neptune)
|
||||||
* jd: Julian date (TDB)
|
* jd: Julian date (TDB)
|
||||||
* xyz[6]: output position (AU) in ecliptic J2000 frame
|
* xyz[6]: output position (AU) in ecliptic J2000 frame
|
||||||
* (xyz[3..5] are zero — velocity not yet implemented)
|
* (xyz[3..5] are zero — velocity not yet implemented)
|
||||||
@ -99,7 +99,7 @@ bool eph_de_moon(double jd, double xyz[3]);
|
|||||||
bool eph_de_sun(double jd, double xyz[6]);
|
bool eph_de_sun(double jd, double xyz[6]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DE metadata accessors (for pg_orbit_ephemeris_info).
|
* DE metadata accessors (for pg_orrery_ephemeris_info).
|
||||||
* Return 0/0.0 if DE is not loaded.
|
* Return 0/0.0 if DE is not loaded.
|
||||||
*/
|
*/
|
||||||
double eph_de_start_jd(void);
|
double eph_de_start_jd(void);
|
||||||
@ -107,4 +107,4 @@ double eph_de_end_jd(void);
|
|||||||
int eph_de_version(void);
|
int eph_de_version(void);
|
||||||
double eph_de_au_km(void);
|
double eph_de_au_km(void);
|
||||||
|
|
||||||
#endif /* PG_ORBIT_EPH_PROVIDER_H */
|
#endif /* PG_ORRERY_EPH_PROVIDER_H */
|
||||||
|
|||||||
@ -34,7 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
Derived from Stellarium's GUST86 implementation.
|
Derived from Stellarium's GUST86 implementation.
|
||||||
Modified for pg_orbit: removed all static mutable state for thread safety
|
Modified for pg_orrery: removed all static mutable state for thread safety
|
||||||
(PostgreSQL PARALLEL SAFE). The original used static caching arrays and
|
(PostgreSQL PARALLEL SAFE). The original used static caching arrays and
|
||||||
CalcInterpolatedElements for performance; this version computes fresh on
|
CalcInterpolatedElements for performance; this version computes fresh on
|
||||||
each call which is acceptable for SQL query workloads.
|
each call which is acceptable for SQL query workloads.
|
||||||
|
|||||||
@ -34,7 +34,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
Derived from Stellarium's GUST86 implementation.
|
Derived from Stellarium's GUST86 implementation.
|
||||||
Modified for pg_orbit: removed static mutable state and
|
Modified for pg_orrery: removed static mutable state and
|
||||||
CalcInterpolatedElements caching for thread safety
|
CalcInterpolatedElements caching for thread safety
|
||||||
(PostgreSQL PARALLEL SAFE). Elements are computed fresh on each call.
|
(PostgreSQL PARALLEL SAFE). Elements are computed fresh on each call.
|
||||||
|
|
||||||
@ -45,8 +45,8 @@ CalcInterpolatedElements caching for thread safety
|
|||||||
****************************************************************/
|
****************************************************************/
|
||||||
|
|
||||||
|
|
||||||
#ifndef PG_ORBIT_GUST86_H
|
#ifndef PG_ORRERY_GUST86_H
|
||||||
#define PG_ORBIT_GUST86_H
|
#define PG_ORRERY_GUST86_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
L1.2 Galilean satellite theory -- Lainey, Duriez & Vienne
|
L1.2 Galilean satellite theory -- Lainey, Duriez & Vienne
|
||||||
|
|
||||||
Clean-room implementation for pg_orbit.
|
Clean-room implementation for pg_orrery.
|
||||||
Positions and velocities of Io, Europa, Ganymede, and Callisto
|
Positions and velocities of Io, Europa, Ganymede, and Callisto
|
||||||
relative to Jupiter's center, in VSOP87 ecliptic J2000 coordinates.
|
relative to Jupiter's center, in VSOP87 ecliptic J2000 coordinates.
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
L1.2 Galilean satellite theory -- Lainey, Duriez & Vienne
|
L1.2 Galilean satellite theory -- Lainey, Duriez & Vienne
|
||||||
|
|
||||||
Clean-room implementation for pg_orbit.
|
Clean-room implementation for pg_orrery.
|
||||||
The L1.2 theory provides positions and velocities of Jupiter's four
|
The L1.2 theory provides positions and velocities of Jupiter's four
|
||||||
Galilean moons (Io, Europa, Ganymede, Callisto) relative to Jupiter's
|
Galilean moons (Io, Europa, Ganymede, Callisto) relative to Jupiter's
|
||||||
center, in the VSOP87 ecliptic J2000 reference frame.
|
center, in the VSOP87 ecliptic J2000 reference frame.
|
||||||
|
|||||||
@ -18,8 +18,8 @@
|
|||||||
* Thread-safe: no static mutable state.
|
* Thread-safe: no static mutable state.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_LAMBERT_H
|
#ifndef PG_ORRERY_LAMBERT_H
|
||||||
#define PG_ORBIT_LAMBERT_H
|
#define PG_ORRERY_LAMBERT_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -57,4 +57,4 @@ int lambert_solve_uv(const double r1[3], const double r2[3],
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* PG_ORBIT_LAMBERT_H */
|
#endif /* PG_ORRERY_LAMBERT_H */
|
||||||
|
|||||||
@ -42,7 +42,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
Derived from Stellarium's MARSSAT implementation.
|
Derived from Stellarium's MARSSAT implementation.
|
||||||
Modified for pg_orbit: removed all static mutable state for thread safety
|
Modified for pg_orrery: removed all static mutable state for thread safety
|
||||||
(PostgreSQL PARALLEL SAFE). The original used static caching arrays and
|
(PostgreSQL PARALLEL SAFE). The original used static caching arrays and
|
||||||
CalcInterpolatedElements for performance; this version computes fresh on
|
CalcInterpolatedElements for performance; this version computes fresh on
|
||||||
each call which is acceptable for SQL query workloads.
|
each call which is acceptable for SQL query workloads.
|
||||||
|
|||||||
@ -42,7 +42,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
Derived from Stellarium's MARSSAT implementation.
|
Derived from Stellarium's MARSSAT implementation.
|
||||||
Modified for pg_orbit: removed static mutable state and
|
Modified for pg_orrery: removed static mutable state and
|
||||||
CalcInterpolatedElements caching for thread safety
|
CalcInterpolatedElements caching for thread safety
|
||||||
(PostgreSQL PARALLEL SAFE). Elements are computed fresh on each call.
|
(PostgreSQL PARALLEL SAFE). Elements are computed fresh on each call.
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ CalcInterpolatedElements caching for thread safety
|
|||||||
|
|
||||||
****************************************************************/
|
****************************************************************/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_MARSSAT_H
|
#ifndef PG_ORRERY_MARSSAT_H
|
||||||
#define PG_ORBIT_MARSSAT_H
|
#define PG_ORRERY_MARSSAT_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* pass_funcs.c -- Satellite pass prediction for pg_orbit
|
* pass_funcs.c -- Satellite pass prediction for pg_orrery
|
||||||
*
|
*
|
||||||
* Finds visibility windows (AOS/LOS) for a satellite relative to a
|
* Finds visibility windows (AOS/LOS) for a satellite relative to a
|
||||||
* ground observer. Uses bisection on the elevation function to pin
|
* ground observer. Uses bisection on the elevation function to pin
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* pg_orbit.c -- Extension entry point
|
* pg_orrery.c -- Extension entry point
|
||||||
*
|
*
|
||||||
* PostgreSQL extension for orbital mechanics.
|
* PostgreSQL extension for orbital mechanics.
|
||||||
* Provides TLE, ECI, geodetic, topocentric, observer, and pass_event types
|
* Provides TLE, ECI, geodetic, topocentric, observer, and pass_event types
|
||||||
* with SGP4/SDP4 propagation, coordinate transforms, and pass prediction.
|
* with SGP4/SDP4 propagation, coordinate transforms, and pass prediction.
|
||||||
*
|
*
|
||||||
* v0.3.0 adds _PG_init for GUC registration (pg_orbit.ephemeris_path)
|
* v0.3.0 adds _PG_init for GUC registration (pg_orrery.ephemeris_path)
|
||||||
* and on_proc_exit cleanup for the optional DE ephemeris handle.
|
* and on_proc_exit cleanup for the optional DE ephemeris handle.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ planet_heliocentric(PG_FUNCTION_ARGS)
|
|||||||
body_id)));
|
body_id)));
|
||||||
|
|
||||||
jd = timestamptz_to_jd(ts);
|
jd = timestamptz_to_jd(ts);
|
||||||
vsop_body = body_id - 1; /* pg_orbit 1-based -> VSOP87 0-based */
|
vsop_body = body_id - 1; /* pg_orrery 1-based -> VSOP87 0-based */
|
||||||
GetVsop87Coor(jd, vsop_body, xyz);
|
GetVsop87Coor(jd, vsop_body, xyz);
|
||||||
|
|
||||||
result = (pg_heliocentric *) palloc(sizeof(pg_heliocentric));
|
result = (pg_heliocentric *) palloc(sizeof(pg_heliocentric));
|
||||||
|
|||||||
@ -10,8 +10,8 @@
|
|||||||
* No PostgreSQL dependencies -- pure math, suitable for standalone use.
|
* No PostgreSQL dependencies -- pure math, suitable for standalone use.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_PRECESSION_H
|
#ifndef PG_ORRERY_PRECESSION_H
|
||||||
#define PG_ORBIT_PRECESSION_H
|
#define PG_ORRERY_PRECESSION_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_precession_angles_vondrak -- IAU 2006 precession angles
|
* get_precession_angles_vondrak -- IAU 2006 precession angles
|
||||||
@ -52,4 +52,4 @@ void get_nutation_angles_iau2000b(double jde,
|
|||||||
double *delta_psi,
|
double *delta_psi,
|
||||||
double *delta_epsilon);
|
double *delta_epsilon);
|
||||||
|
|
||||||
#endif /* PG_ORBIT_PRECESSION_H */
|
#endif /* PG_ORRERY_PRECESSION_H */
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Bill Gray's **sat_code** library, MIT license.
|
|||||||
|
|
||||||
## What was vendored
|
## What was vendored
|
||||||
|
|
||||||
7 source files and 2 headers, the minimal set required by pg_orbit:
|
7 source files and 2 headers, the minimal set required by pg_orrery:
|
||||||
|
|
||||||
| Original file | Vendored as | Purpose |
|
| Original file | Vendored as | Purpose |
|
||||||
|---------------|-------------|---------|
|
|---------------|-------------|---------|
|
||||||
@ -42,7 +42,7 @@ phantom dependency on `g++` and `-lstdc++`.
|
|||||||
|
|
||||||
4. **Removed unused model prototypes** from `norad.h` — `SGP_init()`, `SGP()`,
|
4. **Removed unused model prototypes** from `norad.h` — `SGP_init()`, `SGP()`,
|
||||||
`SGP8_init()`, `SGP8()`, `SDP8_init()`, `SDP8()`, and Win32 dynamic loading
|
`SGP8_init()`, `SGP8()`, `SDP8_init()`, `SDP8()`, and Win32 dynamic loading
|
||||||
functions. pg_orbit uses only SGP4/SDP4.
|
functions. pg_orrery uses only SGP4/SDP4.
|
||||||
|
|
||||||
5. **Added forward declarations** to suppress `-Wmissing-prototypes`:
|
5. **Added forward declarations** to suppress `-Wmissing-prototypes`:
|
||||||
- `common.c`: `sxpall_common_init()` declared before definition
|
- `common.c`: `sxpall_common_init()` declared before definition
|
||||||
|
|||||||
@ -23,9 +23,9 @@
|
|||||||
* singularity in node/perigee. STR#1 Chapter 6, STR#3 Eqs. 8-63..8-80.
|
* singularity in node/perigee. STR#1 Chapter 6, STR#3 Eqs. 8-63..8-80.
|
||||||
*
|
*
|
||||||
* Global statics (safe in PostgreSQL fork model):
|
* Global statics (safe in PostgreSQL fork model):
|
||||||
* dpsec_integration_step = 720.0 (minutes, never modified by pg_orbit)
|
* dpsec_integration_step = 720.0 (minutes, never modified by pg_orrery)
|
||||||
* dpsec_integration_order = 2 (never modified by pg_orbit)
|
* dpsec_integration_order = 2 (never modified by pg_orrery)
|
||||||
* is_dundee_compliant = 0 (never modified by pg_orbit)
|
* is_dundee_compliant = 0 (never modified by pg_orrery)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
* - Vallado, Crawford, Hujsak, Kelso, AIAA 2006-6753-Rev1
|
* - Vallado, Crawford, Hujsak, Kelso, AIAA 2006-6753-Rev1
|
||||||
*
|
*
|
||||||
* Vendored from Bill Gray's sat_code (commit ff7b989, MIT license).
|
* Vendored from Bill Gray's sat_code (commit ff7b989, MIT license).
|
||||||
* Stripped: SGP, SGP8, SDP8 model prototypes (pg_orbit uses only SGP4/SDP4).
|
* Stripped: SGP, SGP8, SDP8 model prototypes (pg_orrery uses only SGP4/SDP4).
|
||||||
* Stripped: Win32 DLL_FUNC/__stdcall and dynamic loading support.
|
* Stripped: Win32 DLL_FUNC/__stdcall and dynamic loading support.
|
||||||
* Stripped: extern "C" wrapper (now compiled as C, not C++).
|
* Stripped: extern "C" wrapper (now compiled as C, not C++).
|
||||||
* N_SAT_PARAMS kept at its original value for binary compatibility.
|
* N_SAT_PARAMS kept at its original value for binary compatibility.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* sgp4_funcs.c -- SGP4/SDP4 propagation functions for pg_orbit
|
* sgp4_funcs.c -- SGP4/SDP4 propagation functions for pg_orrery
|
||||||
*
|
*
|
||||||
* Wraps Bill Gray's sat_code implementation. Near-earth objects
|
* Wraps Bill Gray's sat_code implementation. Near-earth objects
|
||||||
* use SGP4, deep-space objects use SDP4. The choice is automatic
|
* use SGP4, deep-space objects use SDP4. The choice is automatic
|
||||||
|
|||||||
@ -10,8 +10,8 @@
|
|||||||
* No PostgreSQL dependencies -- pure math, suitable for standalone use.
|
* No PostgreSQL dependencies -- pure math, suitable for standalone use.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_SIDEREAL_TIME_H
|
#ifndef PG_ORRERY_SIDEREAL_TIME_H
|
||||||
#define PG_ORBIT_SIDEREAL_TIME_H
|
#define PG_ORRERY_SIDEREAL_TIME_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_mean_sidereal_time -- Greenwich Mean Sidereal Time
|
* get_mean_sidereal_time -- Greenwich Mean Sidereal Time
|
||||||
@ -22,7 +22,7 @@
|
|||||||
* JD = Julian Date (UT1 timescale)
|
* JD = Julian Date (UT1 timescale)
|
||||||
* JDE = Julian Ephemeris Date (TDB/TT timescale)
|
* JDE = Julian Ephemeris Date (TDB/TT timescale)
|
||||||
*
|
*
|
||||||
* For pg_orbit Phase 2, JD and JDE are treated as identical
|
* For pg_orrery Phase 2, JD and JDE are treated as identical
|
||||||
* (delta-T correction not yet applied; the difference is <1s
|
* (delta-T correction not yet applied; the difference is <1s
|
||||||
* for recent epochs).
|
* for recent epochs).
|
||||||
*
|
*
|
||||||
@ -44,4 +44,4 @@ double get_mean_sidereal_time(double JD, double JDE);
|
|||||||
*/
|
*/
|
||||||
double get_apparent_sidereal_time(double JD, double JDE);
|
double get_apparent_sidereal_time(double JD, double JDE);
|
||||||
|
|
||||||
#endif /* PG_ORBIT_SIDEREAL_TIME_H */
|
#endif /* PG_ORRERY_SIDEREAL_TIME_H */
|
||||||
|
|||||||
@ -33,13 +33,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
Adapted for pg_orbit: removed static mutable state for thread safety
|
Adapted for pg_orrery: removed static mutable state for thread safety
|
||||||
(PostgreSQL PARALLEL SAFE).
|
(PostgreSQL PARALLEL SAFE).
|
||||||
|
|
||||||
****************************************************************/
|
****************************************************************/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_TASS17_H
|
#ifndef PG_ORRERY_TASS17_H
|
||||||
#define PG_ORBIT_TASS17_H
|
#define PG_ORRERY_TASS17_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|||||||
12
src/types.h
12
src/types.h
@ -1,13 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* types.h -- Shared type definitions for pg_orbit
|
* types.h -- Shared type definitions for pg_orrery
|
||||||
*
|
*
|
||||||
* All orbital mechanics types stored in PostgreSQL tuples.
|
* All orbital mechanics types stored in PostgreSQL tuples.
|
||||||
* Positions in TEME frame, propagated with WGS-72.
|
* Positions in TEME frame, propagated with WGS-72.
|
||||||
* Coordinate output converted to WGS-84.
|
* Coordinate output converted to WGS-84.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PG_ORBIT_TYPES_H
|
#ifndef PG_ORRERY_TYPES_H
|
||||||
#define PG_ORBIT_TYPES_H
|
#define PG_ORRERY_TYPES_H
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
@ -224,9 +224,9 @@ typedef struct pg_heliocentric
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* JPL DE body target IDs (for de_reader.h)
|
* JPL DE body target IDs (for de_reader.h)
|
||||||
* Used by eph_provider.c to translate pg_orbit body IDs to DE targets.
|
* Used by eph_provider.c to translate pg_orrery body IDs to DE targets.
|
||||||
*
|
*
|
||||||
* pg_orbit: 0=Sun, 1=Mercury, ..., 8=Neptune, 10=Moon
|
* pg_orrery: 0=Sun, 1=Mercury, ..., 8=Neptune, 10=Moon
|
||||||
* DE file: 0=Mercury, ..., 7=Neptune, 8=Pluto, 9=Moon, 10=Sun, 2=EMB
|
* DE file: 0=Mercury, ..., 7=Neptune, 8=Pluto, 9=Moon, 10=Sun, 2=EMB
|
||||||
*/
|
*/
|
||||||
#define DE_CENTER_EARTH 99 /* virtual: Earth derived from EMB-Moon */
|
#define DE_CENTER_EARTH 99 /* virtual: Earth derived from EMB-Moon */
|
||||||
@ -250,4 +250,4 @@ pgbody_to_de_target(int body_id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* PG_ORBIT_TYPES_H */
|
#endif /* PG_ORRERY_TYPES_H */
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/************************************************************************
|
/************************************************************************
|
||||||
|
|
||||||
Derived from Stellarium's VSOP87 implementation.
|
Derived from Stellarium's VSOP87 implementation.
|
||||||
Modified for pg_orbit: removed all static mutable state for thread safety
|
Modified for pg_orrery: removed all static mutable state for thread safety
|
||||||
(PostgreSQL PARALLEL SAFE). The original used static caching arrays and
|
(PostgreSQL PARALLEL SAFE). The original used static caching arrays and
|
||||||
CalcInterpolatedElements for performance; this version computes fresh on
|
CalcInterpolatedElements for performance; this version computes fresh on
|
||||||
each call (~0.5ms) which is acceptable for SQL query workloads.
|
each call (~0.5ms) which is acceptable for SQL query workloads.
|
||||||
@ -137321,7 +137321,7 @@ void CalcVsop87Elem(const double t,double elem[8*6], void *user) {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pg_orbit: all static caching removed for thread safety (PARALLEL SAFE).
|
/* pg_orrery: all static caching removed for thread safety (PARALLEL SAFE).
|
||||||
Each call computes elements fresh via CalcVsop87Elem and converts to
|
Each call computes elements fresh via CalcVsop87Elem and converts to
|
||||||
rectangular coordinates. No mutable static state remains. */
|
rectangular coordinates. No mutable static state remains. */
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/************************************************************************
|
/************************************************************************
|
||||||
|
|
||||||
Derived from Stellarium's VSOP87 implementation.
|
Derived from Stellarium's VSOP87 implementation.
|
||||||
Modified for pg_orbit: removed static caching for thread safety
|
Modified for pg_orrery: removed static caching for thread safety
|
||||||
(PostgreSQL PARALLEL SAFE).
|
(PostgreSQL PARALLEL SAFE).
|
||||||
|
|
||||||
The PLANETARY SOLUTION VSOP87 by Bretagnon P. and Francou G. can be found at
|
The PLANETARY SOLUTION VSOP87 by Bretagnon P. and Francou G. can be found at
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
-- convenience functions requested by Craft (Astrolock) integration
|
-- convenience functions requested by Craft (Astrolock) integration
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
NOTICE: extension "pg_orbit" already exists, skipping
|
NOTICE: extension "pg_orrery" already exists, skipping
|
||||||
-- tle_from_lines: two-argument constructor
|
-- tle_from_lines: two-argument constructor
|
||||||
SELECT tle_norad_id(tle_from_lines(
|
SELECT tle_norad_id(tle_from_lines(
|
||||||
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002',
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
-- Test coordinate transform functions
|
-- Test coordinate transform functions
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
NOTICE: extension "pg_orbit" already exists, skipping
|
NOTICE: extension "pg_orrery" already exists, skipping
|
||||||
-- Subsatellite point at epoch
|
-- Subsatellite point at epoch
|
||||||
WITH iss AS (
|
WITH iss AS (
|
||||||
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
||||||
|
|||||||
@ -6,11 +6,11 @@
|
|||||||
-- results to the VSOP87 variants.
|
-- results to the VSOP87 variants.
|
||||||
\set boulder '''40.015N 105.270W 1655m'''::observer
|
\set boulder '''40.015N 105.270W 1655m'''::observer
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- Test 1: pg_orbit_ephemeris_info() returns VSOP87 when no DE file
|
-- Test 1: pg_orrery_ephemeris_info() returns VSOP87 when no DE file
|
||||||
-- The provider should be 'VSOP87' since no ephemeris_path is set.
|
-- The provider should be 'VSOP87' since no ephemeris_path is set.
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
SELECT 'eph_info_vsop87' AS test,
|
SELECT 'eph_info_vsop87' AS test,
|
||||||
(pg_orbit_ephemeris_info()).provider AS provider;
|
(pg_orrery_ephemeris_info()).provider AS provider;
|
||||||
test | provider
|
test | provider
|
||||||
-----------------+----------
|
-----------------+----------
|
||||||
eph_info_vsop87 | VSOP87
|
eph_info_vsop87 | VSOP87
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
-- Test GiST index and operators (2-D: altitude + inclination)
|
-- Test GiST index and operators (2-D: altitude + inclination)
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
NOTICE: extension "pg_orbit" already exists, skipping
|
NOTICE: extension "pg_orrery" already exists, skipping
|
||||||
-- Create test table with mixed orbit types
|
-- Create test table with mixed orbit types
|
||||||
CREATE TABLE test_orbits (
|
CREATE TABLE test_orbits (
|
||||||
id serial,
|
id serial,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
-- Test pass prediction functions
|
-- Test pass prediction functions
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
NOTICE: extension "pg_orbit" already exists, skipping
|
NOTICE: extension "pg_orrery" already exists, skipping
|
||||||
-- Predict ISS passes over Boulder in 24-hour window
|
-- Predict ISS passes over Boulder in 24-hour window
|
||||||
WITH iss AS (
|
WITH iss AS (
|
||||||
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
-- Test SGP4/SDP4 propagation functions
|
-- Test SGP4/SDP4 propagation functions
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
NOTICE: extension "pg_orbit" already exists, skipping
|
NOTICE: extension "pg_orrery" already exists, skipping
|
||||||
-- ISS TLE (LEO, near-earth -> SGP4)
|
-- ISS TLE (LEO, near-earth -> SGP4)
|
||||||
WITH iss AS (
|
WITH iss AS (
|
||||||
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
-- Test TLE type: parsing, round-trip, accessors
|
-- Test TLE type: parsing, round-trip, accessors
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
-- Parse a valid ISS TLE
|
-- Parse a valid ISS TLE
|
||||||
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
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 IS NOT NULL AS parse_ok;
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle IS NOT NULL AS parse_ok;
|
||||||
|
|||||||
@ -22,8 +22,8 @@
|
|||||||
-- Tolerances for the summary check:
|
-- Tolerances for the summary check:
|
||||||
-- Position: 1e-4 km (0.1 m) — catches marginal velocity-only failures
|
-- Position: 1e-4 km (0.1 m) — catches marginal velocity-only failures
|
||||||
-- Velocity: 1e-7 km/s (0.1 mm/s) — tight enough for regression detection
|
-- Velocity: 1e-7 km/s (0.1 mm/s) — tight enough for regression detection
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
NOTICE: extension "pg_orbit" already exists, skipping
|
NOTICE: extension "pg_orrery" already exists, skipping
|
||||||
-- Helper: convert Julian date + minutes-since-epoch to timestamptz.
|
-- Helper: convert Julian date + minutes-since-epoch to timestamptz.
|
||||||
-- JD 2440587.5 = 1970-01-01 00:00:00 UTC (Unix epoch).
|
-- JD 2440587.5 = 1970-01-01 00:00:00 UTC (Unix epoch).
|
||||||
CREATE OR REPLACE FUNCTION _vallado_jd_plus_min(jd float8, tsince_min float8)
|
CREATE OR REPLACE FUNCTION _vallado_jd_plus_min(jd float8, tsince_min float8)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
-- convenience functions requested by Craft (Astrolock) integration
|
-- convenience functions requested by Craft (Astrolock) integration
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
|
|
||||||
-- tle_from_lines: two-argument constructor
|
-- tle_from_lines: two-argument constructor
|
||||||
SELECT tle_norad_id(tle_from_lines(
|
SELECT tle_norad_id(tle_from_lines(
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
-- Test coordinate transform functions
|
-- Test coordinate transform functions
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
|
|
||||||
-- Subsatellite point at epoch
|
-- Subsatellite point at epoch
|
||||||
WITH iss AS (
|
WITH iss AS (
|
||||||
|
|||||||
@ -8,11 +8,11 @@
|
|||||||
\set boulder '''40.015N 105.270W 1655m'''::observer
|
\set boulder '''40.015N 105.270W 1655m'''::observer
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- Test 1: pg_orbit_ephemeris_info() returns VSOP87 when no DE file
|
-- Test 1: pg_orrery_ephemeris_info() returns VSOP87 when no DE file
|
||||||
-- The provider should be 'VSOP87' since no ephemeris_path is set.
|
-- The provider should be 'VSOP87' since no ephemeris_path is set.
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
SELECT 'eph_info_vsop87' AS test,
|
SELECT 'eph_info_vsop87' AS test,
|
||||||
(pg_orbit_ephemeris_info()).provider AS provider;
|
(pg_orrery_ephemeris_info()).provider AS provider;
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- Test 2: planet_heliocentric_de falls back to VSOP87
|
-- Test 2: planet_heliocentric_de falls back to VSOP87
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
-- Test GiST index and operators (2-D: altitude + inclination)
|
-- Test GiST index and operators (2-D: altitude + inclination)
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
|
|
||||||
-- Create test table with mixed orbit types
|
-- Create test table with mixed orbit types
|
||||||
CREATE TABLE test_orbits (
|
CREATE TABLE test_orbits (
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
-- Test pass prediction functions
|
-- Test pass prediction functions
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
|
|
||||||
-- Predict ISS passes over Boulder in 24-hour window
|
-- Predict ISS passes over Boulder in 24-hour window
|
||||||
WITH iss AS (
|
WITH iss AS (
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
-- Test SGP4/SDP4 propagation functions
|
-- Test SGP4/SDP4 propagation functions
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
||||||
|
|
||||||
-- ISS TLE (LEO, near-earth -> SGP4)
|
-- ISS TLE (LEO, near-earth -> SGP4)
|
||||||
WITH iss AS (
|
WITH iss AS (
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user