Update CLAUDE.md for v0.2.0
Reflects the full solar system expansion: 57 functions, 7 types, 11 test suites, 36 source files, body ID tables, error handling patterns, astronomical constants, and the Starlight docs site.
This commit is contained in:
parent
12292415ab
commit
76767c2909
249
CLAUDE.md
249
CLAUDE.md
@ -1,147 +1,218 @@
|
||||
# pg_orbit — PostgreSQL Extension for Orbital Mechanics
|
||||
# pg_orbit — Solar System Computation for PostgreSQL
|
||||
|
||||
## What This Is
|
||||
A PostgreSQL extension that makes TLE/orbital data first-class types — the way PostGIS does for geographic data. Native C extension using PGXS, wrapping Bill Gray's `sat_code` SGP4/SDP4 implementation.
|
||||
A PostgreSQL extension that moves orbital mechanics inside the database — the way PostGIS did for geography. Native C extension using PGXS, 57 SQL functions, 7 custom types, covering satellites (SGP4/SDP4), planets (VSOP87), Moon (ELP2000-82B), 19 planetary moons (L1.2/TASS17/GUST86/MarsSat), stars, comets, Jupiter radio bursts, and interplanetary Lambert transfers.
|
||||
|
||||
**Current version:** 0.2.0 on branch `phase/solar-system-expansion`
|
||||
|
||||
## Build System
|
||||
```bash
|
||||
make # Compile with PGXS
|
||||
make install # Install to PostgreSQL extensions dir
|
||||
make installcheck # Run regression tests
|
||||
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 11 regression test suites
|
||||
```
|
||||
|
||||
Requires: PostgreSQL 14+ development headers (`pg_config` in PATH), GCC, Make.
|
||||
Requires: PostgreSQL 17 development headers, GCC, G++ (for sat_code C++), Make.
|
||||
|
||||
### Docker
|
||||
```bash
|
||||
make docker-build # Build standalone image (pg17 + pg_orbit)
|
||||
make docker-test # Smoke test the image
|
||||
make docker-push # Push to git.supported.systems registry
|
||||
```
|
||||
|
||||
Image: `git.supported.systems/warehack.ing/pg_orbit:pg17`
|
||||
|
||||
## Project Layout
|
||||
```
|
||||
pg_orbit.control # Extension metadata
|
||||
Makefile # PGXS build
|
||||
pg_orbit.control # Extension metadata (version 0.2.0)
|
||||
Makefile # PGXS build + Docker targets
|
||||
sql/
|
||||
pg_orbit--0.1.0.sql # All type/function/operator definitions
|
||||
pg_orbit--0.1.0.sql # v0.1.0: satellite types/functions/operators
|
||||
pg_orbit--0.2.0.sql # v0.2.0: complete extension (all 57 functions)
|
||||
pg_orbit--0.1.0--0.2.0.sql # Migration script (adds solar system)
|
||||
src/
|
||||
pg_orbit.c # PG_MODULE_MAGIC entry point
|
||||
tle_type.c # TLE custom type (input/output/binary/accessors)
|
||||
eci_type.c # ECI position type + geodetic/topocentric types
|
||||
observer_type.c # Observer location type with flexible parsing
|
||||
sgp4_funcs.c # sgp4_propagate(), sgp4_propagate_series()
|
||||
coord_funcs.c # eci_to_geodetic(), eci_to_topocentric(), subsatellite_point()
|
||||
pass_funcs.c # next_pass(), predict_passes(), pass_visible()
|
||||
gist_tle.c # GiST operator class for altitude-band indexing
|
||||
types.h # Shared struct definitions
|
||||
pg_orbit.c # PG_MODULE_MAGIC entry point
|
||||
types.h # All struct definitions + constants
|
||||
astro_math.h # Shared astronomical helper functions
|
||||
# --- Satellite (v0.1.0) ---
|
||||
tle_type.c # TLE custom type (I/O, binary, 15 accessors)
|
||||
eci_type.c # ECI position type + geodetic/topocentric types
|
||||
observer_type.c # Observer type with flexible string parsing
|
||||
sgp4_funcs.c # sgp4_propagate(), _safe(), _series(), tle_distance()
|
||||
coord_funcs.c # eci_to_geodetic(), eci_to_topocentric(), ground_track()
|
||||
pass_funcs.c # next_pass(), predict_passes(), pass_visible()
|
||||
gist_tle.c # GiST operator class (&&, <->)
|
||||
# --- Solar System (v0.2.0) ---
|
||||
vsop87.c / vsop87.h # VSOP87 planetary ephemeris (Bretagnon 1988)
|
||||
elp82b.c / elp82b.h # ELP2000-82B lunar ephemeris (Chapront 1988)
|
||||
precession.c / precession.h # IAU 1976 precession (Lieske 1979)
|
||||
sidereal_time.c / .h # GMST calculation (Vallado Eq. 3-47)
|
||||
elliptic_to_rectangular.c/.h # Orbital element conversions
|
||||
planet_funcs.c # planet_observe(), planet_heliocentric(), sun/moon_observe()
|
||||
star_funcs.c # star_observe(), star_observe_safe()
|
||||
kepler_funcs.c # kepler_propagate(), comet_observe()
|
||||
l12.c / l12.h # L1.2 Galilean moon theory (Lieske 1998)
|
||||
tass17.c / tass17.h # TASS 1.7 Saturn moon theory (Vienne & Duriez 1995)
|
||||
gust86.c / gust86.h # GUST86 Uranus moon theory (Laskar & Jacobson 1987)
|
||||
marssat.c / marssat.h # MarsSat Mars moon theory (Jacobson 2014)
|
||||
moon_funcs.c # galilean/saturn/uranus/mars_moon_observe()
|
||||
radio_funcs.c # io_phase_angle(), jupiter_cml(), burst_probability()
|
||||
lambert.c / lambert.h # Lambert transfer solver (Izzo 2015)
|
||||
transfer_funcs.c # lambert_transfer(), lambert_c3()
|
||||
lib/
|
||||
sat_code/ # Bill Gray's SGP4 (MIT license, git submodule)
|
||||
sat_code/ # Bill Gray's SGP4/SDP4 (MIT, git submodule)
|
||||
test/
|
||||
sql/ # Regression test SQL
|
||||
expected/ # Expected output
|
||||
data/
|
||||
vallado_518.csv # 518 verification test vectors
|
||||
sql/ # 11 regression test suites
|
||||
expected/ # Expected output
|
||||
docs/
|
||||
DESIGN.md # Architecture decisions, theory-to-code mappings
|
||||
DESIGN.md # Architecture decisions, theory-to-code mappings
|
||||
Dockerfile # Starlight docs site (Astro + Caddy)
|
||||
package.json # Docs site dependencies
|
||||
astro.config.mjs # Starlight configuration
|
||||
src/content/docs/ # 34 MDX documentation pages
|
||||
```
|
||||
|
||||
## Type System
|
||||
|
||||
### Core Types (all varlena or fixed-size, stored in tuples)
|
||||
All types are fixed-size, `STORAGE = plain`, `ALIGNMENT = double`. No TOAST overhead.
|
||||
|
||||
| Type | Storage | Description |
|
||||
|------|---------|-------------|
|
||||
| `tle` | ~160 bytes fixed | Parsed mean elements (not raw text) |
|
||||
| `eci_position` | 48 bytes | x,y,z + vx,vy,vz (km, km/s) in TEME |
|
||||
| `geodetic` | 24 bytes | lat, lon (radians), alt (km) above WGS-84 |
|
||||
| `topocentric` | 32 bytes | azimuth, elevation, range, range_rate |
|
||||
| `observer` | 24 bytes | lat, lon (radians), alt_m (meters) |
|
||||
| `pass_event` | 56 bytes | AOS/MAX/LOS times + max_el + AOS/LOS az |
|
||||
| Type | Bytes | Description |
|
||||
|------|-------|-------------|
|
||||
| `tle` | 112 | Parsed mean orbital elements for SGP4/SDP4 |
|
||||
| `eci_position` | 48 | x,y,z + vx,vy,vz (km, km/s) in TEME frame |
|
||||
| `geodetic` | 24 | lat, lon (radians), alt (km) above WGS-84 |
|
||||
| `topocentric` | 32 | azimuth, elevation, range, range_rate |
|
||||
| `observer` | 24 | lat, lon (radians), alt_m (meters) |
|
||||
| `pass_event` | 48 | AOS/MAX/LOS times + max_el + AOS/LOS azimuth |
|
||||
| `heliocentric` | 24 | x, y, z in AU (ecliptic J2000 frame) |
|
||||
|
||||
### TLE Internal Struct
|
||||
Stores all parsed mean elements from the two-line format:
|
||||
- epoch (Julian date, float64)
|
||||
- inclination, eccentricity, RAAN, arg_perigee, mean_anomaly (radians, float64)
|
||||
- mean_motion (rev/day, float64), mean_motion_dot, mean_motion_ddot
|
||||
- bstar (drag coefficient, float64)
|
||||
- norad_id (int32), elset_num (int32), rev_num (int32)
|
||||
- classification (char), intl_designator (8 chars)
|
||||
- ephemeris_type (int8)
|
||||
## Function Domains (57 total)
|
||||
|
||||
| Domain | Theory | Key Functions | Count |
|
||||
|--------|--------|---------------|-------|
|
||||
| Satellite | SGP4/SDP4 (Brouwer 1959) | `observe()`, `predict_passes()`, `ground_track()` | 22 |
|
||||
| Planets | VSOP87 (Bretagnon 1988) | `planet_observe()`, `planet_heliocentric()` | 3 |
|
||||
| Sun/Moon | VSOP87 + ELP2000-82B | `sun_observe()`, `moon_observe()` | 2 |
|
||||
| Planetary moons | L1.2, TASS17, GUST86, MarsSat | `galilean_observe()`, `saturn_moon_observe()` | 4 |
|
||||
| Stars | J2000 + IAU 1976 precession | `star_observe()`, `star_observe_safe()` | 2 |
|
||||
| Comets/asteroids | Two-body Keplerian | `kepler_propagate()`, `comet_observe()` | 2 |
|
||||
| Jupiter radio | Carr et al. (1983) | `jupiter_burst_probability()` | 3 |
|
||||
| Transfers | Lambert (Izzo 2015) | `lambert_transfer()`, `lambert_c3()` | 2 |
|
||||
| GiST index | Altitude-band approximation | `&&` (overlap), `<->` (distance) | 8 |
|
||||
|
||||
All 57 functions are `PARALLEL SAFE`. No global mutable state.
|
||||
|
||||
## Body IDs
|
||||
|
||||
### Planets (VSOP87 convention)
|
||||
| ID | Body | ID | Body |
|
||||
|----|------|----|------|
|
||||
| 0 | Sun | 5 | Jupiter |
|
||||
| 1 | Mercury | 6 | Saturn |
|
||||
| 2 | Venus | 7 | Uranus |
|
||||
| 3 | Earth | 8 | Neptune |
|
||||
| 4 | Mars | 10 | Moon |
|
||||
|
||||
### Planetary Moons (per-family indexing)
|
||||
- **Galilean** (0-3): Io, Europa, Ganymede, Callisto
|
||||
- **Saturn** (0-7): Mimas, Enceladus, Tethys, Dione, Rhea, Titan, Iapetus, Hyperion
|
||||
- **Uranus** (0-4): Miranda, Ariel, Umbriel, Titania, Oberon
|
||||
- **Mars** (0-1): Phobos, Deimos
|
||||
|
||||
## Constant Chain of Custody
|
||||
|
||||
**This is the most critical design constraint.**
|
||||
|
||||
TLEs are mean elements fitted using WGS-72 constants. The elements absorb geodetic model biases — using WGS-84 constants for propagation silently corrupts position accuracy by kilometers.
|
||||
**The most critical design constraint.** TLEs absorb geodetic model biases — using wrong constants silently corrupts positions by kilometers.
|
||||
|
||||
### Rules
|
||||
1. **SGP4 propagation**: WGS-72 constants ONLY (mu, ae, J2, J3, J4, ke)
|
||||
2. **Coordinate output** (geodetic, topocentric): Convert to WGS-84 (a=6378.137km, f=1/298.257223563)
|
||||
3. **TEME frame**: Use only 4 of 106 IAU-80 nutation terms (matching SGP4's internal model)
|
||||
4. **Never mix**: WGS-72 propagation + WGS-84 output. No other combination.
|
||||
2. **Coordinate output** (geodetic, topocentric): WGS-84 (a=6378.137km, f=1/298.257223563)
|
||||
3. **TEME frame**: Only 4 of 106 IAU-80 nutation terms (matching SGP4's internal model)
|
||||
4. **Solar system pipeline**: IAU 1976 precession, J2000 obliquity, GMST from Vallado Eq. 3-47
|
||||
5. **Never mix**: WGS-72 propagation + WGS-84 output. No other combination.
|
||||
|
||||
### WGS-72 Constants (from Hoots & Roehrich STR#3)
|
||||
### WGS-72 Constants (from Hoots & Roehrich STR#3, propagation only)
|
||||
```c
|
||||
#define WGS72_MU 398600.8 /* km^3/s^2 */
|
||||
#define WGS72_AE 6378.135 /* km */
|
||||
#define WGS72_MU 398600.8 /* km^3/s^2 */
|
||||
#define WGS72_AE 6378.135 /* km */
|
||||
#define WGS72_J2 0.001082616
|
||||
#define WGS72_J3 -0.00000253881
|
||||
#define WGS72_J4 -0.00000165597
|
||||
#define WGS72_KE 0.0743669161 /* (min)^(-1), = sqrt(mu) * 60 / ae^(3/2) */
|
||||
#define WGS72_XPDOTP 1440.0 / (2.0 * M_PI) /* min/rev */
|
||||
#define WGS72_KE 0.0743669161331734132 /* (min)^(-1) */
|
||||
```
|
||||
|
||||
### WGS-84 Constants (for output only)
|
||||
### WGS-84 Constants (coordinate output only)
|
||||
```c
|
||||
#define WGS84_A 6378.137 /* km */
|
||||
#define WGS84_A 6378.137 /* km */
|
||||
#define WGS84_F (1.0 / 298.257223563)
|
||||
#define WGS84_E2 (WGS84_F * (2.0 - WGS84_F))
|
||||
```
|
||||
|
||||
### Astronomical Constants
|
||||
```c
|
||||
#define AU_KM 149597870.7 /* IAU 2012 */
|
||||
#define GAUSS_K 0.01720209895 /* AU^(3/2)/day */
|
||||
#define OBLIQUITY_J2000 0.40909280422232897 /* 23.4392911 deg in radians */
|
||||
#define J2000_JD 2451545.0 /* 2000 Jan 1.5 TT */
|
||||
```
|
||||
|
||||
## sat_code Submodule
|
||||
|
||||
Bill Gray's SGP4 implementation: https://github.com/Bill-Gray/sat_code
|
||||
Bill Gray's SGP4/SDP4: https://github.com/Bill-Gray/sat_code (MIT license)
|
||||
|
||||
Key files we use:
|
||||
- `sgp4.c` / `sgp4.h` — SGP4/SDP4 propagator
|
||||
- `norad.h` — TLE struct definitions and constants
|
||||
C++ sources compiled with `g++ -fPIC`, linked via `-lstdc++`. C linkage through `norad.h`.
|
||||
|
||||
The submodule lives at `lib/sat_code/`. To initialize:
|
||||
```bash
|
||||
git submodule update --init
|
||||
git submodule update --init # Initialize
|
||||
```
|
||||
|
||||
### Integration Pattern
|
||||
```c
|
||||
#include "lib/sat_code/norad.h"
|
||||
|
||||
// Parse TLE lines into sat_code's tle_t struct
|
||||
// Call SGP4_init() once per TLE
|
||||
// Call SGP4() with minutes-since-epoch for each propagation
|
||||
```
|
||||
Key files: `sgp4.cpp`, `sdp4.cpp`, `deep.cpp`, `common.cpp`, `basics.cpp`, `norad.h`, `norad_in.h`
|
||||
|
||||
## Testing
|
||||
|
||||
### Vallado 518 Test Vectors
|
||||
The definitive SGP4 verification dataset. Each row: NORAD ID, minutes since epoch, expected x,y,z,vx,vy,vz. All 518 must pass to machine epsilon before any other work proceeds.
|
||||
11 regression test suites via `make installcheck`:
|
||||
|
||||
### Regression Tests
|
||||
Standard PostgreSQL `make installcheck` framework:
|
||||
- `test/sql/*.sql` — test queries
|
||||
- `test/expected/*.out` — expected output
|
||||
- Tests run against a temporary database
|
||||
| Suite | What it tests |
|
||||
|-------|--------------|
|
||||
| tle_parse | TLE I/O round-trip, malformed input rejection, all 15 accessors |
|
||||
| sgp4_propagate | SGP4/SDP4, propagation series, tle_distance |
|
||||
| coord_transforms | TEME-to-geodetic, TEME-to-topocentric, ground_track |
|
||||
| pass_prediction | predict_passes, next_pass, pass_visible, min elevation filter |
|
||||
| gist_index | `&&` overlap, `<->` distance, GiST index scan, KNN ordering |
|
||||
| convenience | observe(), observe_safe(), tle_from_lines(), observer_from_geodetic() |
|
||||
| star_observe | Star observation, IAU 1976 precession, heliocentric type I/O |
|
||||
| kepler_comet | Keplerian propagation (elliptic/parabolic/hyperbolic), comet_observe |
|
||||
| planet_observe | VSOP87 planets, sun_observe, moon_observe (ELP2000-82B) |
|
||||
| moon_observe | Galilean/Saturn/Uranus/Mars moons, Io phase, Jupiter CML, burst probability |
|
||||
| lambert_transfer | Lambert solver, lambert_c3, pork chop grid, error handling |
|
||||
|
||||
### Test Categories
|
||||
1. **tle_parse** — TLE input/output round-trip, malformed input rejection
|
||||
2. **sgp4_propagate** — Vallado vectors, edge cases (deep space, high eccentricity)
|
||||
3. **coord_transforms** — TEME->geodetic, TEME->topocentric accuracy
|
||||
4. **pass_prediction** — Known ISS passes, edge cases (polar, retrograde)
|
||||
5. **gist_index** — Index scan vs sequential scan equivalence
|
||||
## Error Handling Patterns
|
||||
|
||||
- `_safe()` variants (`sgp4_propagate_safe`, `observe_safe`, `star_observe_safe`) return NULL on error instead of raising exceptions. Use these for batch queries over potentially invalid data.
|
||||
- SGP4 error codes: -1 (nearly parabolic), -2 (negative semi-major axis/decayed), -3/-4 (orbit within Earth, returns with NOTICE), -5 (negative mean motion), -6 (convergence failure)
|
||||
- Pass prediction: propagation failures return -pi elevation (below horizon), shedding the failed timestep without aborting the scan.
|
||||
- Input validation: same-body Lambert check, arrival-before-departure, invalid body_id, RA out of [0,24), negative perihelion distance.
|
||||
|
||||
## Documentation Site
|
||||
|
||||
Starlight docs at `docs/` — 34 MDX pages covering all domains:
|
||||
|
||||
```bash
|
||||
cd docs && npm run dev # Dev server on :3000
|
||||
cd docs && npm run build # Static build to dist/
|
||||
docker build --target production -t pg-orbit-docs docs/ # Caddy image
|
||||
```
|
||||
|
||||
Sections: Getting Started, Guides (8 domain walkthroughs), Workflow Translation (Skyfield/Horizons/GMAT/Radio Jupiter Pro comparisons), Reference (all 57 functions), Architecture (Hamilton's principles, constant custody, observation pipeline), Performance (benchmarks).
|
||||
|
||||
## Coding Style
|
||||
- Standard PostgreSQL extension C style
|
||||
- `ereport(ERROR, ...)` for user-facing errors, never `elog(ERROR, ...)`
|
||||
- All memory allocation through `palloc`/`pfree` (PostgreSQL memory contexts)
|
||||
- All memory via `palloc`/`pfree` (PostgreSQL memory contexts)
|
||||
- Comments explain "why", not "what"
|
||||
- No global mutable state — all computation from function arguments
|
||||
- Functions that call `SGP4()` must handle the error return code
|
||||
- Every function handling SGP4 must check the error return code
|
||||
- All functions marked `PARALLEL SAFE`
|
||||
|
||||
## Git Conventions
|
||||
- One commit per logical change
|
||||
- Branch per phase: `phase/1-tle-sgp4`, `phase/2-coordinates`, etc.
|
||||
- Tag releases: `v0.1.0`, `v0.2.0`, etc.
|
||||
- Branch per phase: `phase/solar-system-expansion`
|
||||
- Tag releases: `v0.1.0`, `v0.2.0`
|
||||
- Commit messages: imperative mood, no AI attribution
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user