Merge v0.10.0 doc updates: benchmarks, Skyfield parity, LLM refs
This commit is contained in:
commit
91d183e5c4
15
.gitignore
vendored
15
.gitignore
vendored
@ -25,6 +25,21 @@ test/test_od_math
|
||||
test/test_od_iod
|
||||
test/test_od_gauss
|
||||
|
||||
# Bench — downloaded TLE catalogs (large, ephemeral)
|
||||
# Already-tracked files (active.tle, spacetrack_full*.tle) are unaffected.
|
||||
bench/alpha5.tle
|
||||
bench/celestrak_*.tle
|
||||
bench/mega_catalog.tle
|
||||
bench/merged_catalog.tle
|
||||
bench/satnogs*.tle
|
||||
bench/spacetrack_all_onorbit.tle
|
||||
bench/spacetrack_everything.tle
|
||||
bench/supgp_*.tle
|
||||
bench/tle_api_catalog.tle
|
||||
bench/cookies*.txt
|
||||
bench/load_mega_catalog.sql
|
||||
bench/load_merged_catalog.sql
|
||||
|
||||
# Docs site
|
||||
docs/node_modules/
|
||||
docs/dist/
|
||||
|
||||
22
CLAUDE.md
22
CLAUDE.md
@ -1,9 +1,9 @@
|
||||
# pg_orrery — A Database Orrery for PostgreSQL
|
||||
|
||||
## What This Is
|
||||
A database orrery — celestial mechanics types and functions for PostgreSQL. Native C extension using PGXS, 106 SQL functions, 9 custom types, covering satellites (SGP4/SDP4), planets (VSOP87 + optional JPL DE441), Moon (ELP2000-82B), 19 planetary moons (L1.2/TASS17/GUST86/MarsSat), stars (with proper motion), comets, asteroids (MPC catalog), Jupiter radio bursts, interplanetary Lambert transfers, equatorial RA/Dec coordinates, atmospheric refraction, and light-time correction.
|
||||
A database orrery — celestial mechanics types and functions for PostgreSQL. Native C extension using PGXS, 114 SQL functions, 9 custom types, covering satellites (SGP4/SDP4), planets (VSOP87 + optional JPL DE441), Moon (ELP2000-82B), 19 planetary moons (L1.2/TASS17/GUST86/MarsSat), stars (with proper motion and annual parallax), comets, asteroids (MPC catalog), Jupiter radio bursts, interplanetary Lambert transfers, equatorial RA/Dec coordinates with angular separation, atmospheric refraction, annual stellar aberration, and light-time correction.
|
||||
|
||||
**Current version:** 0.9.0 on branch `phase/spgist-orbital-trie`
|
||||
**Current version:** 0.10.0
|
||||
**Repository:** https://git.supported.systems/warehack.ing/pg_orrery
|
||||
**Documentation:** https://pg-orrery.warehack.ing
|
||||
|
||||
@ -11,7 +11,7 @@ A database orrery — celestial mechanics types and functions for PostgreSQL. Na
|
||||
```bash
|
||||
make PG_CONFIG=/usr/bin/pg_config # Compile with PGXS
|
||||
sudo make install PG_CONFIG=/usr/bin/pg_config # Install extension
|
||||
make installcheck PG_CONFIG=/usr/bin/pg_config # Run 18 regression test suites
|
||||
make installcheck PG_CONFIG=/usr/bin/pg_config # Run 19 regression test suites
|
||||
```
|
||||
|
||||
Requires: PostgreSQL 17 development headers, GCC, Make.
|
||||
@ -123,7 +123,7 @@ All types are fixed-size, `STORAGE = plain`, `ALIGNMENT = double`. No TOAST over
|
||||
| `orbital_elements` | 72 | Classical Keplerian elements for comets/asteroids (epoch, q, e, inc, omega, Omega, tp, H, G) |
|
||||
| `equatorial` | 24 | Apparent RA (hours), Dec (degrees), distance (km) — of date |
|
||||
|
||||
## Function Domains (106 total)
|
||||
## Function Domains (114 total)
|
||||
|
||||
| Domain | Theory | Key Functions | Count |
|
||||
|--------|--------|---------------|-------|
|
||||
@ -134,9 +134,10 @@ All types are fixed-size, `STORAGE = plain`, `ALIGNMENT = double`. No TOAST over
|
||||
| Stars | J2000 + IAU 1976 precession | `star_observe()`, `star_equatorial()`, `star_observe_pm()` | 5 |
|
||||
| Comets/asteroids | Two-body Keplerian + MPC | `small_body_observe()`, `small_body_equatorial()`, `oe_from_mpc()` | 19 |
|
||||
| Refraction | Bennett (1982) | `atmospheric_refraction()`, `predict_passes_refracted()` | 4 |
|
||||
| Equatorial spatial | Vincenty formula | `eq_angular_distance()`, `eq_within_cone()`, `<->` | 2 |
|
||||
| Jupiter radio | Carr et al. (1983) | `jupiter_burst_probability()` | 3 |
|
||||
| Transfers | Lambert (Izzo 2015) | `lambert_transfer()`, `lambert_c3()` | 2 |
|
||||
| DE ephemeris | JPL DE440/441 (optional) | `planet_observe_de()`, `planet_equatorial_de()` | 13 |
|
||||
| DE ephemeris | JPL DE440/441 (optional) | `planet_observe_de()`, `planet_equatorial_de()`, `*_apparent_de()` | 19 |
|
||||
| GiST index | Altitude-band approximation | `&&` (overlap), `<->` (distance) | 8 |
|
||||
| Diagnostics | -- | `pg_orrery_ephemeris_info()` | 1 |
|
||||
|
||||
@ -240,6 +241,12 @@ Every `_de()` function mirrors an existing VSOP87 function:
|
||||
| `mars_moon_observe_de()` | `mars_moon_observe()` | STABLE |
|
||||
| `planet_equatorial_de()` | `planet_equatorial()` | STABLE |
|
||||
| `moon_equatorial_de()` | `moon_equatorial()` | STABLE |
|
||||
| `planet_observe_apparent_de()` | `planet_observe_apparent()` | STABLE |
|
||||
| `sun_observe_apparent_de()` | `sun_observe_apparent()` | STABLE |
|
||||
| `moon_observe_apparent_de()` | `moon_observe_apparent()` | STABLE |
|
||||
| `planet_equatorial_apparent_de()` | `planet_equatorial_apparent()` | STABLE |
|
||||
| `moon_equatorial_apparent_de()` | `moon_equatorial_apparent()` | STABLE |
|
||||
| `small_body_observe_apparent_de()` | `small_body_observe_apparent()` | STABLE |
|
||||
| `pg_orrery_ephemeris_info()` | — | STABLE |
|
||||
|
||||
## Vendored SGP4/SDP4
|
||||
@ -261,7 +268,7 @@ All numerical logic is byte-identical to upstream. Verified against 518 Vallado
|
||||
|
||||
## Testing
|
||||
|
||||
18 regression test suites via `make installcheck`:
|
||||
19 regression test suites via `make installcheck`:
|
||||
|
||||
| Suite | What it tests |
|
||||
|-------|--------------|
|
||||
@ -282,11 +289,12 @@ All numerical logic is byte-identical to upstream. Verified against 518 Vallado
|
||||
| orbital_elements | orbital_elements type I/O, MPC parser, small_body_observe/heliocentric |
|
||||
| equatorial | equatorial type I/O, RA/Dec for planets/stars/satellites, proper motion, light-time |
|
||||
| refraction | Bennett refraction, P/T correction, apparent elevation, refracted pass prediction |
|
||||
| aberration | Annual aberration magnitude, DE apparent fallback, angular distance, cone search, stellar parallax |
|
||||
| vallado_518 | 518 Vallado test vectors (AIAA 2006-6753-Rev1), per-satellite breakdown |
|
||||
|
||||
### PG Version Matrix
|
||||
|
||||
Test all 18 regression suites + DE reader unit test across PostgreSQL 14-18 using Docker:
|
||||
Test all 19 regression suites + DE reader unit test across PostgreSQL 14-18 using Docker:
|
||||
|
||||
```bash
|
||||
make test-matrix # Full matrix (PG 14-18)
|
||||
|
||||
173
bench/build_catalog.py
Executable file
173
bench/build_catalog.py
Executable file
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Build a merged TLE catalog from multiple sources for pg_orrery benchmarks.
|
||||
|
||||
Usage:
|
||||
# Merge existing TLE files into SQL
|
||||
./build_catalog.py bench/spacetrack_everything.tle bench/celestrak_active.tle ...
|
||||
|
||||
# Pipe to psql
|
||||
./build_catalog.py bench/*.tle | PGPORT=5499 psql -d contrib_regression
|
||||
|
||||
# Or generate SQL file
|
||||
./build_catalog.py bench/*.tle > bench/load_catalog.sql
|
||||
|
||||
Deduplication: when the same NORAD ID appears in multiple files, the entry
|
||||
with the newest epoch wins. This means CelesTrak SupGP data (fresher epochs)
|
||||
automatically overrides stale Space-Track entries.
|
||||
|
||||
Alpha-5 NORAD IDs (T0002 etc.) are handled transparently — they parse into
|
||||
integers >100,000 via the same logic as Bill Gray's get_el.c.
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
|
||||
# Alpha-5 NORAD decoding — mirrors get_norad_number() in src/sgp4/get_el.c
|
||||
_ALPHA5_SKIP = {'I', 'O'} # skipped in Alpha-5 encoding
|
||||
|
||||
def decode_norad(s):
|
||||
"""Decode a 5-character NORAD field to integer. Handles Alpha-5."""
|
||||
s = s.strip()
|
||||
if not s:
|
||||
return None
|
||||
first = s[0]
|
||||
if first.isdigit():
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
return None
|
||||
elif first.isalpha() and first.isupper():
|
||||
# Alpha-5: letter + 4 digits
|
||||
val = ord(first) - ord('A')
|
||||
if first > 'I':
|
||||
val -= 1
|
||||
if first > 'O':
|
||||
val -= 1
|
||||
try:
|
||||
return val * 10000 + int(s[1:]) + 100000
|
||||
except ValueError:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def parse_3le_file(filepath):
|
||||
"""Parse a 3LE (or 2LE) file into a dict of norad_str -> (line1, line2, name, epoch)."""
|
||||
objects = {}
|
||||
try:
|
||||
lines = open(filepath, errors='replace').readlines()
|
||||
except FileNotFoundError:
|
||||
print(f"# SKIP {filepath}: not found", file=sys.stderr)
|
||||
return objects
|
||||
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i].rstrip('\r\n')
|
||||
|
||||
if line.startswith('1 ') and i + 1 < len(lines) and lines[i + 1].rstrip('\r\n').startswith('2 '):
|
||||
line1 = line.rstrip('\r\n')
|
||||
line2 = lines[i + 1].rstrip('\r\n')
|
||||
|
||||
# Look back for name line (3LE format)
|
||||
name = ''
|
||||
if i > 0:
|
||||
prev = lines[i - 1].rstrip('\r\n')
|
||||
if prev and not prev.startswith(('1 ', '2 ')):
|
||||
name = prev.strip()
|
||||
|
||||
# Extract NORAD ID (works for both standard and Alpha-5)
|
||||
norad_field = line1[2:7]
|
||||
norad_int = decode_norad(norad_field)
|
||||
if norad_int is None:
|
||||
i += 2
|
||||
continue
|
||||
|
||||
norad_str = str(norad_int)
|
||||
|
||||
# Extract epoch (column 18-32 of line 1)
|
||||
try:
|
||||
epoch = float(line1[18:32].strip())
|
||||
except (ValueError, IndexError):
|
||||
epoch = 0.0
|
||||
|
||||
# Keep the entry with the newest epoch
|
||||
if norad_str not in objects or epoch > objects[norad_str][3]:
|
||||
objects[norad_str] = (line1, line2, name, epoch)
|
||||
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Parse --table-name option
|
||||
table_name = 'bench_catalog'
|
||||
files = []
|
||||
i = 1
|
||||
while i < len(sys.argv):
|
||||
if sys.argv[i] == '--table' and i + 1 < len(sys.argv):
|
||||
table_name = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif sys.argv[i].startswith('--table='):
|
||||
table_name = sys.argv[i].split('=', 1)[1]
|
||||
i += 1
|
||||
else:
|
||||
files.append(sys.argv[i])
|
||||
i += 1
|
||||
|
||||
# Merge all sources (later files override earlier for same NORAD ID if newer epoch)
|
||||
mega = {}
|
||||
for filepath in files:
|
||||
objs = parse_3le_file(filepath)
|
||||
new = updated = 0
|
||||
for k, v in objs.items():
|
||||
if k not in mega:
|
||||
new += 1
|
||||
mega[k] = v
|
||||
elif v[3] > mega[k][3]:
|
||||
updated += 1
|
||||
mega[k] = v
|
||||
basename = os.path.basename(filepath)
|
||||
print(f"-- {basename}: {len(objs)} objects ({new} new, {updated} updated)", file=sys.stderr)
|
||||
|
||||
print(f"-- Total: {len(mega)} unique objects", file=sys.stderr)
|
||||
|
||||
# Emit SQL
|
||||
print(f"-- pg_orrery benchmark catalog ({len(mega)} objects)")
|
||||
print(f"-- Generated from {len(files)} TLE source files")
|
||||
print(f"-- Sources: {', '.join(os.path.basename(f) for f in files)}")
|
||||
print()
|
||||
print(f"DROP TABLE IF EXISTS {table_name};")
|
||||
print(f"CREATE TABLE {table_name} (")
|
||||
print(f" id serial,")
|
||||
print(f" name text,")
|
||||
print(f" tle tle")
|
||||
print(f");")
|
||||
print()
|
||||
|
||||
count = 0
|
||||
for norad_str in sorted(mega.keys(), key=lambda x: int(x)):
|
||||
line1, line2, name, epoch = mega[norad_str]
|
||||
|
||||
if not name:
|
||||
name = f'NORAD {norad_str}'
|
||||
|
||||
name_sql = name.replace("'", "''").replace('\\', '\\\\')
|
||||
tle_str = f"{line1}\\n{line2}"
|
||||
|
||||
print(f"INSERT INTO {table_name} (name, tle) VALUES ('{name_sql}', E'{tle_str}');")
|
||||
count += 1
|
||||
|
||||
print()
|
||||
print(f"-- Loaded {count} objects")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
138
docs/TODO-v0.10.0.md
Normal file
138
docs/TODO-v0.10.0.md
Normal file
@ -0,0 +1,138 @@
|
||||
# pg_orrery — Post-v0.10.0 Roadmap
|
||||
|
||||
## Current State
|
||||
|
||||
**Version:** v0.10.0 (tagged 2026-02-22)
|
||||
**Branch:** `phase/spgist-orbital-trie` merged to `main`
|
||||
**Functions:** 114 SQL functions, 9 custom types, 19 test suites, 1 new operator
|
||||
**Docs:** https://pg-orrery.warehack.ing
|
||||
|
||||
### What v0.10.0 shipped
|
||||
- Annual stellar aberration in all `_apparent()` functions (~20 arcsec)
|
||||
- 6 new `_apparent_de()` variants with VSOP87 fallback
|
||||
- `eq_angular_distance()` + `eq_within_cone()` + `<->` operator on equatorial
|
||||
- Stellar annual parallax in `star_observe_pm()` / `star_equatorial_pm()`
|
||||
|
||||
### Astrolock integration status
|
||||
Thread: `docs/agent-threads/v090-astrolock-upgrade/`
|
||||
- v0.9.0 fully deployed to both local and production servers
|
||||
- v0.10.0 upgrade path communicated (message 003)
|
||||
- Pending their upgrade — aberration improvement is automatic
|
||||
|
||||
## Remaining Housekeeping
|
||||
|
||||
- [x] Merge `phase/spgist-orbital-trie` to `main`
|
||||
- [ ] Clean up `bench/` — gitignore the untracked TLE catalog files (alpha5, celestrak, satnogs, spacetrack, supgp, tle_api, merged, mega)
|
||||
- [ ] Update "From Skyfield" workflow page for v0.9.0/v0.10.0 RA/Dec + aberration parity
|
||||
- [ ] Add timing numbers for equatorial, refraction, aberration functions to benchmarks page
|
||||
- [ ] Update CLAUDE.md function count: 106 -> 114, test suites: 18 -> 19
|
||||
- [ ] Update docs llms.txt and llms-full.txt for v0.10.0 functions
|
||||
|
||||
## Feature Candidates — Next Version
|
||||
|
||||
### Tier 1 — High value, low effort
|
||||
|
||||
#### A. `make_orbital_elements()` constructor
|
||||
**Requested by:** astrolock-api (message 002, question 1)
|
||||
|
||||
SQL constructor from 9 floats. Lets users compose orbital_elements from individual table columns without `format()`/cast workaround.
|
||||
|
||||
```sql
|
||||
make_orbital_elements(epoch_jd, q_au, e, inc_rad, omega_rad, node_rad, tp_jd, h_mag, g_slope)
|
||||
-> orbital_elements
|
||||
```
|
||||
|
||||
Complexity: ~30 lines in `orbital_elements_type.c`. One new function.
|
||||
|
||||
#### B. `galilean_equatorial()` and moon family equatorial functions
|
||||
**Requested by:** astrolock-api (message 002, question 2)
|
||||
|
||||
Geocentric RA/Dec for planetary moons. Follows `planet_equatorial()` pattern — convert geocentric ecliptic position to equatorial J2000, precess to date.
|
||||
|
||||
New functions (~4):
|
||||
- `galilean_equatorial(int4, timestamptz) -> equatorial`
|
||||
- `saturn_moon_equatorial(int4, timestamptz) -> equatorial`
|
||||
- `uranus_moon_equatorial(int4, timestamptz) -> equatorial`
|
||||
- `mars_moon_equatorial(int4, timestamptz) -> equatorial`
|
||||
|
||||
Plus DE variants (~4 more).
|
||||
|
||||
Complexity: ~100 lines. Follows established pattern.
|
||||
|
||||
#### C. GiST/SP-GiST index on equatorial type
|
||||
The `<->` operator and `eq_within_cone()` exist but have no index support. For cone-search queries over large catalogs, an index would enable:
|
||||
|
||||
```sql
|
||||
-- Indexed: "what's within 10 deg of Jupiter?"
|
||||
SELECT * FROM star_catalog
|
||||
WHERE position <-> planet_equatorial(5, NOW()) < 10.0;
|
||||
```
|
||||
|
||||
Approach: GiST with bounding-box approximation in RA/Dec space, or SP-GiST with HEALPix-style recursive decomposition.
|
||||
|
||||
Complexity: Medium (~300-500 lines). The SP-GiST infrastructure from TLE index is reusable.
|
||||
|
||||
### Tier 2 — Medium value, medium risk
|
||||
|
||||
#### D. Nutation correction (~9 arcsec)
|
||||
IAU 1980 nutation (106 terms) or simplified IAU 2000B.
|
||||
|
||||
Currently: TEME uses 4 of 106 terms. Equatorial output uses IAU 1976 precession only (no nutation).
|
||||
|
||||
Value: ~9 arcsec correction in equatorial coordinates. Matters for sub-arcminute accuracy — telescope GoTo mounts and catalog cross-matching.
|
||||
|
||||
Scope: New `nutation.c` + modify `precess_j2000_to_date()` to include nutation matrix.
|
||||
Risk: Touches the precession pipeline used by every equatorial function.
|
||||
|
||||
#### E. Delta T (TDB - UTC)
|
||||
The "affects everything" change. Currently all time is treated as UTC with no TT/TDB distinction.
|
||||
|
||||
Requires IERS lookup table or polynomial approximation (Espenak & Meeus 2006).
|
||||
|
||||
Scope: Touch `sidereal_time.h`, propagation pipelines, all observation functions.
|
||||
Risk: High — affects every time conversion. Needs careful regression testing.
|
||||
Value: Improves accuracy for historical epochs (pre-2000) and future predictions (post-2030).
|
||||
|
||||
Already noted as deferred at `sidereal_time.h:22-26`.
|
||||
|
||||
#### F. Rise/set prediction for solar system objects
|
||||
Like `predict_passes()` but for planets, Sun, and Moon. Binary search for horizon crossings.
|
||||
|
||||
Use cases: sunrise/sunset, moonrise/moonset, planet visibility windows.
|
||||
Complexity: Medium. The pass prediction binary search machinery exists but needs adaptation for much slower angular rates.
|
||||
|
||||
### Tier 3 — Future / deferred
|
||||
|
||||
- **Perturbed asteroid propagation** — secular perturbation terms for orbital_elements (currently two-body Keplerian)
|
||||
- **Eclipse prediction** — Moon shadow cone intersection with observer
|
||||
- **Satellite sunlit visibility** — extend `pass_visible()` with Earth shadow geometry
|
||||
- **Constellation identification** — equatorial position to IAU constellation boundary lookup
|
||||
- **Coordinate frame transforms** — ICRS/FK5/galactic/ecliptic conversion functions
|
||||
|
||||
## Suggested Next Phase
|
||||
|
||||
```
|
||||
Housekeeping (bench cleanup, docs, CLAUDE.md)
|
||||
|
|
||||
v
|
||||
Feature A: make_orbital_elements() — 30 lines, unblocks Craft comets
|
||||
Feature B: moon family equatorial — 100 lines, unblocks Craft Galilean moons
|
||||
|
|
||||
v
|
||||
Feature C: equatorial GiST index — enables indexed cone search
|
||||
Feature D: nutation — closes largest remaining accuracy gap (~9 arcsec)
|
||||
|
|
||||
v
|
||||
Feature E: Delta T — high risk, needs its own careful phase
|
||||
Feature F: rise/set — new domain, independent
|
||||
```
|
||||
|
||||
A + B could ship as v0.11.0. C + D as v0.12.0.
|
||||
|
||||
## Verification
|
||||
|
||||
- All 19 existing regression suites must continue to pass
|
||||
- New test suites for each feature
|
||||
- PG 14-18 matrix (`make test-matrix`)
|
||||
- Cross-check against JPL Horizons for nutation accuracy
|
||||
- Astrolock integration smoke test after each db upgrade
|
||||
@ -0,0 +1,196 @@
|
||||
# Message 001
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| From | pg-orrery |
|
||||
| To | astrolock-api |
|
||||
| Date | 2026-02-21T18:30:00-07:00 |
|
||||
| Re | pg_orrery v0.9.0 released — 24 new functions for Craft |
|
||||
|
||||
---
|
||||
|
||||
## What shipped
|
||||
|
||||
pg_orrery v0.9.0 is tagged and pushed. Docker image at `git.supported.systems/warehack.ing/pg_orrery:pg17`. Tested across PG 14-18. Docs live at https://pg-orrery.warehack.ing.
|
||||
|
||||
**24 new SQL functions** in 4 feature areas:
|
||||
|
||||
### 1. Equatorial RA/Dec output (12 functions)
|
||||
|
||||
New `equatorial` type (24 bytes: RA in hours, Dec in degrees, distance in km). Apparent coordinates of date — what telescope GoTo mounts and sky apps expect.
|
||||
|
||||
```sql
|
||||
-- Planets, Sun, Moon — geocentric RA/Dec
|
||||
SELECT eq_ra(planet_equatorial(5, NOW())) AS jupiter_ra_hours,
|
||||
eq_dec(planet_equatorial(5, NOW())) AS jupiter_dec_deg;
|
||||
|
||||
SELECT eq_ra(sun_equatorial(NOW())), eq_dec(sun_equatorial(NOW()));
|
||||
SELECT eq_ra(moon_equatorial(NOW())), eq_dec(moon_equatorial(NOW()));
|
||||
|
||||
-- Satellites — topocentric (observer parallax-corrected) and geocentric
|
||||
SELECT eq_ra(eci_to_equatorial(
|
||||
sgp4_propagate(tle_from_lines(l1, l2), NOW()),
|
||||
observer_from_geodetic(lat, lon, alt_m),
|
||||
NOW()
|
||||
)) AS sat_ra_hours;
|
||||
|
||||
SELECT eq_ra(eci_to_equatorial_geo(
|
||||
sgp4_propagate(tle_from_lines(l1, l2), NOW()),
|
||||
NOW()
|
||||
)) AS sat_ra_geo;
|
||||
|
||||
-- Comets/asteroids from orbital_elements
|
||||
SELECT eq_ra(small_body_equatorial(oe, NOW())) AS ra_hours
|
||||
FROM asteroids;
|
||||
|
||||
-- Stars (precesses J2000 catalog coords to date)
|
||||
SELECT eq_ra(star_equatorial(ra_hours, dec_deg, NOW()));
|
||||
|
||||
-- Accessors
|
||||
eq_ra(equatorial) -> float8 -- hours [0, 24)
|
||||
eq_dec(equatorial) -> float8 -- degrees [-90, 90]
|
||||
eq_distance(equatorial) -> float8 -- km
|
||||
```
|
||||
|
||||
**Why this matters for Craft:** The sky engine currently returns only alt/az from `topo_elevation()`/`topo_azimuth()`. RA/Dec enables:
|
||||
- CesiumJS sky layer with equatorial grid overlay
|
||||
- Telescope GoTo integration (mounts speak RA/Dec)
|
||||
- Cross-matching objects against star catalogs
|
||||
- Proper sky chart rendering in the web UI
|
||||
|
||||
### 2. Atmospheric refraction (4 functions)
|
||||
|
||||
Bennett (1982) formula. Objects near the horizon appear ~0.57 deg higher than their geometric position.
|
||||
|
||||
```sql
|
||||
-- Basic: standard atmosphere
|
||||
SELECT atmospheric_refraction(0.0); -- 0.57 deg at horizon
|
||||
|
||||
-- Extended: with pressure (mbar) and temperature (C)
|
||||
SELECT atmospheric_refraction_ext(0.0, 700.0, -20.0); -- high altitude, cold
|
||||
|
||||
-- Apparent elevation (geometric + refraction correction)
|
||||
SELECT topo_elevation_apparent(planet_observe(5, obs, NOW()));
|
||||
|
||||
-- Refracted pass prediction (horizon at -0.569 deg geometric)
|
||||
SELECT * FROM predict_passes_refracted(
|
||||
tle, obs, start_ts, end_ts
|
||||
);
|
||||
```
|
||||
|
||||
**Why this matters for Craft:**
|
||||
- `predict_passes_refracted()` finds passes ~35 seconds earlier/later than geometric — more accurate AOS/LOS times for rotor pre-positioning
|
||||
- `topo_elevation_apparent()` gives what the observer actually *sees*, not the geometric truth
|
||||
- The pass finder currently uses `predict_passes()` — drop-in replacement with `predict_passes_refracted()` for better accuracy
|
||||
|
||||
### 3. Light-time corrected apparent positions (6 functions)
|
||||
|
||||
Single-iteration light-time correction. Shows where an object *was* when its light left, not where it *is now*. Jupiter: ~35-52 minutes of light travel time.
|
||||
|
||||
```sql
|
||||
-- Planet apparent position (light-time corrected)
|
||||
SELECT topo_elevation(planet_observe_apparent(5, obs, NOW())) AS jupiter_apparent;
|
||||
SELECT topo_elevation(sun_observe_apparent(obs, NOW())) AS sun_apparent;
|
||||
|
||||
-- Equatorial apparent (light-time corrected RA/Dec)
|
||||
SELECT eq_ra(planet_equatorial_apparent(5, NOW()));
|
||||
SELECT eq_ra(moon_equatorial_apparent(NOW()));
|
||||
|
||||
-- Comets/asteroids
|
||||
SELECT * FROM small_body_observe_apparent(oe, obs, NOW());
|
||||
SELECT eq_ra(small_body_equatorial_apparent(oe, NOW()));
|
||||
```
|
||||
|
||||
**Why this matters for Craft:** The sky engine's `planet_observe()` returns geometric position. For telescope pointing accuracy, `planet_observe_apparent()` gives the correction. Matters most for outer planets.
|
||||
|
||||
### 4. Stellar proper motion (2 functions)
|
||||
|
||||
Stars move. Barnard's Star drifts ~10 arcseconds/year. For high-proper-motion stars, catalog J2000 coords drift noticeably over decades.
|
||||
|
||||
```sql
|
||||
-- Observe with proper motion (Hipparcos/Gaia convention)
|
||||
SELECT topo_elevation(star_observe_pm(
|
||||
ra_hours, dec_deg,
|
||||
pm_ra_masyr, -- mu_alpha * cos(delta), mas/yr
|
||||
pm_dec_masyr, -- mu_delta, mas/yr
|
||||
parallax_mas, -- 0 to skip parallax
|
||||
rv_kms, -- 0 to skip radial velocity
|
||||
obs, NOW()
|
||||
));
|
||||
|
||||
-- RA/Dec with proper motion
|
||||
SELECT eq_ra(star_equatorial_pm(
|
||||
ra_hours, dec_deg, pm_ra, pm_dec, plx, rv, NOW()
|
||||
));
|
||||
```
|
||||
|
||||
**Why this matters for Craft:** If Craft's star catalog has Hipparcos/Gaia proper motion columns, these functions give positions corrected for stellar drift. The existing `star_observe()` assumes static J2000 — fine for most stars, but Barnard's Star is off by ~2.6 arcmin over 25 years.
|
||||
|
||||
## Upgrade path
|
||||
|
||||
### 1. Rebuild the database image
|
||||
|
||||
Craft's `packages/db/Dockerfile` pulls pg_orrery source via `additional_contexts`. Point it at the v0.9.0 tag or the latest `phase/spgist-orbital-trie`:
|
||||
|
||||
```bash
|
||||
cd ~/claude/ham/satellite/astrolock
|
||||
docker compose build db
|
||||
```
|
||||
|
||||
### 2. Install/upgrade the extension
|
||||
|
||||
```sql
|
||||
-- If already on 0.8.0:
|
||||
ALTER EXTENSION pg_orrery UPDATE TO '0.9.0';
|
||||
|
||||
-- Or fresh install:
|
||||
CREATE EXTENSION pg_orrery VERSION '0.9.0';
|
||||
```
|
||||
|
||||
### 3. Quick smoke test
|
||||
|
||||
```sql
|
||||
-- RA/Dec works?
|
||||
SELECT eq_ra(planet_equatorial(5, NOW())) AS jupiter_ra,
|
||||
eq_dec(planet_equatorial(5, NOW())) AS jupiter_dec;
|
||||
|
||||
-- Refraction works?
|
||||
SELECT atmospheric_refraction(0.0); -- should be ~0.57
|
||||
|
||||
-- Refracted passes?
|
||||
SELECT count(*) FROM predict_passes_refracted(
|
||||
tle_from_lines(l1, l2),
|
||||
observer_from_geodetic(36.0, -86.0, 200.0),
|
||||
NOW(), NOW() + interval '7 days'
|
||||
);
|
||||
```
|
||||
|
||||
## Suggested integration points (for astrolock-api to evaluate)
|
||||
|
||||
These are suggestions, not requirements — Craft knows its own priorities:
|
||||
|
||||
1. **RA/Dec in `whats_up` response** — Add `ra` and `dec` fields alongside `altitude_deg`/`azimuth_deg`. The SQL change is small: add `eq_ra(planet_equatorial(...))` to the planet CTE, `eq_ra(eci_to_equatorial(...))` to the satellite CTE, etc.
|
||||
|
||||
2. **Replace `predict_passes()` with `predict_passes_refracted()`** in `pass_finder.py` — Drop-in replacement, same return signature, better AOS/LOS accuracy.
|
||||
|
||||
3. **Use `planet_observe_apparent()` for telescope pointing** — When the rotor is tracking a planet, the apparent position is what the mount should point at.
|
||||
|
||||
4. **Proper motion for bright star catalog** — If `star` table has Gaia/Hipparcos proper motion columns, swap `star_observe_safe()` for `star_observe_pm()` in the star CTE.
|
||||
|
||||
5. **DE ephemeris RA/Dec** — If DE441 is loaded, `planet_equatorial_de()` gives sub-arcsecond RA/Dec. Two new functions: `planet_equatorial_de()`, `moon_equatorial_de()`.
|
||||
|
||||
## Reference
|
||||
|
||||
- Full docs: https://pg-orrery.warehack.ing
|
||||
- New refraction page: https://pg-orrery.warehack.ing/reference/functions-refraction/
|
||||
- Updated types page: https://pg-orrery.warehack.ing/reference/types/ (equatorial type)
|
||||
- LLM reference: https://pg-orrery.warehack.ing/llms-full.txt (all 106 function signatures)
|
||||
|
||||
---
|
||||
|
||||
**Next steps for recipient:**
|
||||
- [ ] Review which v0.9.0 features are worth integrating
|
||||
- [ ] Rebuild db image with pg_orrery v0.9.0
|
||||
- [ ] Run `ALTER EXTENSION pg_orrery UPDATE TO '0.9.0'`
|
||||
- [ ] Decide priority order for integration (RA/Dec, refracted passes, apparent positions, proper motion)
|
||||
- [ ] Reply with questions, concerns, or an integration plan
|
||||
@ -1,6 +1,6 @@
|
||||
# pg_orrery — Complete LLM Reference
|
||||
|
||||
> Celestial mechanics types and functions for PostgreSQL. Native C extension (v0.9.0) with 106 SQL functions, 9 custom types + 1 composite, GiST/SP-GiST indexing. All functions PARALLEL SAFE.
|
||||
> Celestial mechanics types and functions for PostgreSQL. Native C extension (v0.10.0) with 114 SQL functions, 9 custom types + 1 composite, GiST/SP-GiST indexing. All functions PARALLEL SAFE.
|
||||
|
||||
- Source: https://git.supported.systems/warehack.ing/pg_orrery
|
||||
- Docs: https://pg-orrery.warehack.ing
|
||||
@ -214,6 +214,8 @@ planet_observe_apparent(body_id int4, observer, timestamptz) → topocentric IM
|
||||
sun_observe_apparent(observer, timestamptz) → topocentric IMMUTABLE
|
||||
planet_equatorial_apparent(body_id int4, timestamptz) → equatorial IMMUTABLE
|
||||
moon_equatorial_apparent(timestamptz) → equatorial IMMUTABLE
|
||||
|
||||
-- All _apparent() functions include annual aberration correction (~20 arcsec) + light-time
|
||||
```
|
||||
|
||||
### Planetary Moons (4 functions)
|
||||
@ -235,6 +237,8 @@ star_equatorial(ra_hours, dec_degrees, timestamptz) → equatorial IMMUTAB
|
||||
-- Proper motion (Hipparcos/Gaia convention: pm_ra = mu_alpha * cos(delta) in mas/yr)
|
||||
star_observe_pm(ra_h, dec_deg, pm_ra_masyr, pm_dec_masyr, parallax_mas, rv_kms, observer, timestamptz) → topocentric IMMUTABLE
|
||||
star_equatorial_pm(ra_h, dec_deg, pm_ra_masyr, pm_dec_masyr, parallax_mas, rv_kms, timestamptz) → equatorial IMMUTABLE
|
||||
|
||||
-- When parallax_mas > 0, annual stellar parallax is applied using Earth's heliocentric position (Green 1985)
|
||||
```
|
||||
|
||||
RA in hours [0,24), Dec in degrees [-90,90]. Range returned as 0 (infinite distance) unless parallax > 0 in _pm variants.
|
||||
@ -283,7 +287,16 @@ predict_passes_refracted(tle, observer, start, end, min_el DEFAULT 0.0) → SETO
|
||||
|
||||
Bennett formula: `R = 1/tan(h + 7.31/(h + 4.4))` arcminutes. Domain guard: clamps at -1°, returns 0.0 below. At horizon (0°) refraction is ~0.57°, meaning satellites become visible ~35 seconds earlier.
|
||||
|
||||
### DE Ephemeris — Optional High-Precision (13 functions)
|
||||
### Equatorial Spatial — Angular Separation (2 functions)
|
||||
|
||||
```
|
||||
eq_angular_distance(equatorial, equatorial) → float8 IMMUTABLE -- degrees, Vincenty formula (stable at 0° and 180°)
|
||||
eq_within_cone(equatorial, equatorial, float8) → bool IMMUTABLE -- true if within radius_deg, cosine shortcut
|
||||
```
|
||||
|
||||
Operator: `equatorial <-> equatorial → float8` (angular separation in degrees, commutative).
|
||||
|
||||
### DE Ephemeris — Optional High-Precision (19 functions)
|
||||
|
||||
All _de() functions fall back to VSOP87/ELP2000-82B when DE is unavailable. All STABLE (external file dependency).
|
||||
|
||||
@ -301,6 +314,14 @@ mars_moon_observe_de(moon_id, observer, timestamptz) → topocentric STABLE
|
||||
planet_equatorial_de(body_id int4, timestamptz) → equatorial STABLE -- geocentric RA/Dec via DE
|
||||
moon_equatorial_de(timestamptz) → equatorial STABLE -- geocentric RA/Dec via DE
|
||||
pg_orrery_ephemeris_info() → (provider, file_path, start_jd, end_jd, version, au_km) STABLE
|
||||
|
||||
-- Apparent DE variants (light-time + aberration, falls back to VSOP87)
|
||||
planet_observe_apparent_de(body_id int4, observer, timestamptz) → topocentric STABLE
|
||||
sun_observe_apparent_de(observer, timestamptz) → topocentric STABLE
|
||||
moon_observe_apparent_de(observer, timestamptz) → topocentric STABLE
|
||||
planet_equatorial_apparent_de(body_id int4, timestamptz) → equatorial STABLE
|
||||
moon_equatorial_apparent_de(timestamptz) → equatorial STABLE
|
||||
small_body_observe_apparent_de(orbital_elements, observer, timestamptz) → topocentric STABLE
|
||||
```
|
||||
|
||||
Configure: `ALTER SYSTEM SET pg_orrery.ephemeris_path = '/path/to/de441.bin'; SELECT pg_reload_conf();`
|
||||
@ -358,6 +379,12 @@ CREATE INDEX ON satellites USING spgist (elements tle_spgist_ops);
|
||||
|
||||
SP-GiST is a 2-level orbital trie (SMA → inclination) with query-time RAAN filter. Returns a conservative superset — survivors need `predict_passes()` for ground truth.
|
||||
|
||||
### Equatorial distance
|
||||
|
||||
| Operator | Meaning | Usage |
|
||||
|----------|---------|-------|
|
||||
| `<->` (equatorial) | Angular separation in degrees (Vincenty formula) | `ORDER BY pos1 <-> pos2` or `WHERE pos1 <-> pos2 < 5.0` |
|
||||
|
||||
## Common Query Patterns
|
||||
|
||||
### Observe a satellite
|
||||
@ -513,6 +540,21 @@ FROM satellites,
|
||||
WHERE name = 'ISS';
|
||||
```
|
||||
|
||||
### Angular separation and cone search
|
||||
|
||||
```sql
|
||||
-- Find angular separation between two objects
|
||||
SELECT planet_equatorial(5, NOW()) <-> moon_equatorial(NOW()) AS jupiter_moon_sep_deg;
|
||||
|
||||
-- Objects within 10 degrees of Jupiter
|
||||
SELECT eq_within_cone(
|
||||
star_equatorial(ra_h, dec_deg, NOW()),
|
||||
planet_equatorial(5, NOW()),
|
||||
10.0
|
||||
) AS near_jupiter
|
||||
FROM star_catalog;
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### _safe() variants
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# pg_orrery
|
||||
|
||||
> Celestial mechanics types and functions for PostgreSQL. Native C extension with 106 SQL functions, 9 custom types, GiST/SP-GiST indexing. Covers satellites (SGP4/SDP4), planets (VSOP87), Moon (ELP2000-82B), 19 planetary moons, stars (with proper motion), comets, asteroids (MPC catalog), Jupiter radio bursts, orbit determination, interplanetary Lambert transfers, equatorial RA/Dec coordinates, atmospheric refraction, and light-time correction. Optional JPL DE440/441 ephemeris for sub-arcsecond accuracy.
|
||||
> Celestial mechanics types and functions for PostgreSQL. Native C extension with 114 SQL functions, 9 custom types, GiST/SP-GiST indexing. Covers satellites (SGP4/SDP4), planets (VSOP87), Moon (ELP2000-82B), 19 planetary moons, stars (with proper motion), comets, asteroids (MPC catalog), Jupiter radio bursts, orbit determination, interplanetary Lambert transfers, equatorial RA/Dec coordinates, atmospheric refraction, light-time correction, annual stellar aberration, and equatorial angular separation. Optional JPL DE440/441 ephemeris for sub-arcsecond accuracy.
|
||||
|
||||
- [Source code](https://git.supported.systems/warehack.ing/pg_orrery)
|
||||
- [Full LLM reference](https://pg-orrery.warehack.ing/llms-full.txt): All function signatures, types, body IDs, operators, and query patterns inline
|
||||
@ -47,7 +47,8 @@
|
||||
- [Functions: Radio](https://pg-orrery.warehack.ing/reference/functions-radio/): Jupiter decametric radio burst prediction — Io phase, CML, burst probability
|
||||
- [Functions: Transfers](https://pg-orrery.warehack.ing/reference/functions-transfers/): Lambert transfer solver for interplanetary trajectory design
|
||||
- [Functions: Refraction](https://pg-orrery.warehack.ing/reference/functions-refraction/): Bennett (1982) atmospheric refraction, P/T correction, apparent elevation, refracted pass prediction
|
||||
- [Functions: DE Ephemeris](https://pg-orrery.warehack.ing/reference/functions-de/): Optional JPL DE440/441 variants of observation and equatorial functions
|
||||
- [Functions: Equatorial Spatial](https://pg-orrery.warehack.ing/reference/functions-equatorial/): Angular separation (Vincenty formula), cone search, `<->` operator on equatorial type
|
||||
- [Functions: DE Ephemeris](https://pg-orrery.warehack.ing/reference/functions-de/): Optional JPL DE440/441 variants of observation, equatorial, and apparent functions
|
||||
- [Functions: Orbit Determination](https://pg-orrery.warehack.ing/reference/functions-od/): TLE fitting from ECI, topocentric, and angles-only observations
|
||||
- [Operators & Indexes](https://pg-orrery.warehack.ing/reference/operators-gist/): GiST (&&, <->) and SP-GiST (&?) operator classes for orbital indexing
|
||||
- [Body ID Reference](https://pg-orrery.warehack.ing/reference/body-ids/): Planet IDs 0–10, Galilean 0–3, Saturn 0–7, Uranus 0–4, Mars 0–1
|
||||
|
||||
@ -24,6 +24,10 @@ All benchmarks use PostgreSQL's `EXPLAIN (ANALYZE, BUFFERS)` for timing. The num
|
||||
| Galilean moon observation | 1,000 | 63 ms | 15.9K/sec | L1.2 + VSOP87 pipeline |
|
||||
| Saturn moon observation | 800 | 53 ms | 15.1K/sec | TASS17 + VSOP87 |
|
||||
| Star observation | 500 | 0.7 ms | 714K/sec | Precession + az/el only |
|
||||
| Star with proper motion + parallax | 8,761 | 343 ms | 25.5K/sec | Proper motion + annual parallax (VSOP87 Earth) |
|
||||
| Planet equatorial + apparent | 1,414 | 107 ms | 13.2K/sec | RA/Dec geometric + light-time + aberration |
|
||||
| Angular distance (`<->` equatorial) | 10,000 | 7 ms | 1.43M/sec | Vincenty formula on equatorial pairs |
|
||||
| Atmospheric refraction | 18,200 | 3.5 ms | 5.2M/sec | Bennett (1982) + P/T correction |
|
||||
| Lambert transfer solve | 100 | 0.1 ms | 800K/sec | Single-rev prograde |
|
||||
| Pork chop plot (150 x 150) | 22,500 | 8.3 s | 2.7K/sec | Full VSOP87 + Lambert pipeline |
|
||||
|
||||
@ -145,6 +149,87 @@ LIMIT 500;
|
||||
|
||||
This is nearly as fast as SGP4 propagation because the only computation is matrix multiplication (precession) and a trigonometric transform (az/el). No series evaluation, no iteration.
|
||||
|
||||
## Star observation with proper motion and parallax
|
||||
|
||||
The full stellar pipeline: proper motion correction (RA/Dec rates), annual parallax displacement from VSOP87 Earth position, IAU 1976 precession, sidereal time, and equatorial output.
|
||||
|
||||
```sql
|
||||
-- Benchmark: proper motion + parallax for stars across a year of epochs
|
||||
EXPLAIN (ANALYZE)
|
||||
SELECT star_equatorial_pm(
|
||||
mod(a * 1.5, 24.0), mod(a * 0.7, 180.0) - 90.0,
|
||||
a * 0.1, a * 0.05, a * 10.0, a * 0.01,
|
||||
'2024-01-01'::timestamptz + (a || ' hours')::interval
|
||||
)
|
||||
FROM generate_series(1, 8761) AS a;
|
||||
```
|
||||
|
||||
**8,761 evaluations in 343 ms --- 25,500 per second.**
|
||||
|
||||
When `parallax_mas > 0`, each call adds a `GetVsop87Coor()` evaluation for Earth's heliocentric position to compute the annual parallax displacement. This makes it ~28x slower than basic star observation (which skips VSOP87 entirely). The cost is dominated by the single VSOP87 Earth call per star --- the parallax displacement math itself is negligible.
|
||||
|
||||
For catalogs where parallax is zero or unknown (most survey catalogs), `star_equatorial()` without proper motion runs at the full 714K/sec rate.
|
||||
|
||||
## Planet equatorial and apparent positions
|
||||
|
||||
Equatorial output (RA/Dec) for planets, including the `_apparent()` pipeline that adds light-time correction and annual stellar aberration.
|
||||
|
||||
```sql
|
||||
-- Benchmark: equatorial + apparent for all non-Earth planets
|
||||
EXPLAIN (ANALYZE)
|
||||
SELECT planet_equatorial(body_id, t),
|
||||
planet_equatorial_apparent(body_id, t)
|
||||
FROM generate_series(1, 8) AS body_id,
|
||||
generate_series(
|
||||
'2024-01-01'::timestamptz,
|
||||
'2024-01-01'::timestamptz + interval '100 hours',
|
||||
interval '1 hour'
|
||||
) AS t
|
||||
WHERE body_id != 3;
|
||||
```
|
||||
|
||||
**1,414 evaluations (707 geometric + 707 apparent) in 107 ms --- 13,200 per second.**
|
||||
|
||||
The `_apparent()` variant adds two operations beyond the geometric pipeline: iterating on light-travel time (1--2 iterations to converge) and applying annual stellar aberration from Earth's VSOP87 velocity vector. The aberration calculation itself is cheap (~20 ns) but the extra VSOP87 evaluation for the retarded position roughly doubles the cost per call.
|
||||
|
||||
## Angular distance (`<->` on equatorial)
|
||||
|
||||
The `<->` operator on the `equatorial` type computes angular separation in degrees using the Vincenty formula, which is numerically stable at all separations including 0° and 180°.
|
||||
|
||||
```sql
|
||||
-- Benchmark: angular distance between 10,000 star pairs
|
||||
EXPLAIN (ANALYZE)
|
||||
SELECT eq_angular_distance(
|
||||
star_equatorial(mod(a * 1.3, 24.0), mod(a * 0.7, 180.0) - 90.0, now()),
|
||||
star_equatorial(mod(a * 2.1, 24.0), mod(a * 0.9, 180.0) - 90.0, now())
|
||||
)
|
||||
FROM generate_series(1, 10000) AS a;
|
||||
```
|
||||
|
||||
**10,000 angular distances in 7 ms --- 1.43 million per second.**
|
||||
|
||||
The Vincenty formula involves four trigonometric evaluations and an `atan2` --- comparable cost to a single coordinate transform. The `eq_within_cone()` predicate is even faster because it uses a cosine shortcut (`cos(dist) >= cos(radius)`) that avoids the `atan2`.
|
||||
|
||||
<Aside type="tip" title="Future: GiST index for cone search">
|
||||
Currently `eq_angular_distance()` and `eq_within_cone()` require sequential evaluation. A future GiST or SP-GiST index on the `equatorial` type would enable indexed cone search, turning "what's within 10° of Jupiter?" into an index scan rather than a full table scan over a star catalog.
|
||||
</Aside>
|
||||
|
||||
## Atmospheric refraction
|
||||
|
||||
Bennett's (1982) empirical formula for atmospheric refraction, with optional pressure/temperature correction.
|
||||
|
||||
```sql
|
||||
-- Benchmark: refraction + P/T-corrected refraction for elevation range
|
||||
EXPLAIN (ANALYZE)
|
||||
SELECT atmospheric_refraction(a * 0.01),
|
||||
atmospheric_refraction_ext(a * 0.01, 1013.25, 15.0)
|
||||
FROM generate_series(1, 9100) AS a;
|
||||
```
|
||||
|
||||
**18,200 evaluations (9,100 base + 9,100 extended) in 3.5 ms --- 5.2 million per second.**
|
||||
|
||||
Refraction is pure arithmetic --- a single `cot()` approximation with a polynomial correction term. No series evaluation, no iteration. The `_ext()` variant adds a pressure/temperature scaling factor (one division). This makes refraction essentially free when layered onto the observation pipeline.
|
||||
|
||||
## Lambert transfer
|
||||
|
||||
A single Lambert solve: given two planet positions and a time of flight, find the transfer orbit.
|
||||
@ -412,6 +497,8 @@ The CTE pattern works correctly but forces PostgreSQL to compute all distances a
|
||||
|
||||
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 v0.10.0 additions --- aberration, angular distance, refraction --- range from negligible overhead (refraction at 5.2M/sec) to moderate (apparent positions at 13.2K/sec, roughly half the geometric rate due to the extra VSOP87 call for light-time iteration). Angular distance at 1.43M/sec means cone-search predicates over star catalogs are fast even without index support.
|
||||
|
||||
The visibility cone filter (`&?`) is the fastest operation per evaluation --- three floating-point comparisons vs. the full SGP4 pipeline --- and its 84--90% pruning rate means the most expensive operation in a pass prediction pipeline (SGP4 propagation) only runs on the small fraction of the catalog that could actually produce a visible pass.
|
||||
|
||||
The GiST index provides the clearest speedup for conjunction screening: 5.8x faster than sequential scan for ISS `&&` queries, with 0 false positives or negatives verified against exhaustive sequential evaluation. KNN queries find the nearest orbits in 2 ms via index-ordered traversal using 2-D orbital distance (altitude + inclination), which would otherwise require computing and sorting all 66,440 distances.
|
||||
|
||||
@ -273,11 +273,11 @@ Predict when a satellite will be visible from a location. This is where Skyfield
|
||||
pg_orrery does not replace Skyfield for all use cases. Be clear about where the trade-offs fall.
|
||||
</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. Since v0.3.0, pg_orrery 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.
|
||||
**Apparent-position corrections.** Since v0.9.0, pg_orrery applies light-time correction, and since v0.10.0, annual stellar aberration (~20 arcsec) in all `_apparent()` functions — closing the two largest gaps with Skyfield. However, Skyfield still uses the full IAU 2000A nutation model (~9 arcsec more accurate than pg_orrery's IAU 1976 precession), polar motion corrections, and delta-T from IERS data. These 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_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_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.
|
||||
**Nutation and polar motion.** pg_orrery uses IAU 1976 precession, which introduces a ~9 arcsecond gap compared to Skyfield's IAU 2000A nutation model. Skyfield also applies polar motion corrections from IERS data. For work requiring sub-arcsecond accuracy (occultation timing, precise astrometric reductions), this gap still matters.
|
||||
|
||||
**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.
|
||||
|
||||
@ -336,7 +336,7 @@ FROM fit,
|
||||
You don't have to choose one or the other. A practical migration path:
|
||||
|
||||
<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_orrery with [DE enabled](/guides/de-ephemeris/) matches Skyfield.
|
||||
1. **Keep Skyfield for high-precision apparent positions.** Anything requiring polar motion, nutation at IAU 2000A level (~9 arcsec beyond pg_orrery's IAU 1976 precession), or custom BSP kernels stays in Python. For geometric accuracy and apparent-position work including light-time and aberration, pg_orrery v0.10.0 with [DE enabled](/guides/de-ephemeris/) is now competitive.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user