pg_orrery/CLAUDE.md
Ryan Malloy 501872d45d v0.15.0: constellation full name lookup, rise/set status diagnostics
constellation_full_name(text) returns full IAU name from 3-letter
abbreviation (88-entry static table, IMMUTABLE). Returns NULL for
invalid input — composable with constellation() in queries.

Three rise_set_status functions classify body visibility as
'rises_and_sets', 'circumpolar', or 'never_rises' by sampling
elevation at 48 points across 24h. Separate diagnostic path —
called only when rise/set returns NULL, zero cost in normal case.

147 → 151 SQL objects. 25 → 26 regression suites. All pass.
2026-02-25 19:38:52 -07:00

24 KiB

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, 151 SQL objects (135 user-visible functions + 16 GiST support), 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 GiST-indexed angular separation, atmospheric refraction, annual stellar aberration, light-time correction, rise/set prediction (geometric + refracted) with status diagnostics, and IAU constellation identification with full name lookup (Roman 1987).

Current version: 0.15.0 Repository: https://git.supported.systems/warehack.ing/pg_orrery Documentation: https://pg-orrery.warehack.ing

Build System

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 26 regression test suites

Requires: PostgreSQL 17 development headers, GCC, Make.

Docker

make docker-build   # Build standalone image (pg17 + pg_orrery)
make docker-test    # Smoke test the image
make docker-push    # Push to git.supported.systems registry

Image: git.supported.systems/warehack.ing/pg_orrery:pg17

Project Layout

pg_orrery.control                # Extension metadata (version 0.15.0)
Makefile                        # PGXS build + Docker targets
sql/
  pg_orrery--0.1.0.sql           # v0.1.0: satellite types/functions/operators
  pg_orrery--0.2.0.sql           # v0.2.0: solar system (57 functions)
  pg_orrery--0.3.0.sql           # v0.3.0: DE ephemeris (68 functions)
  pg_orrery--0.4.0.sql           # v0.4.0: orbit determination
  pg_orrery--0.5.0.sql           # v0.5.0: SP-GiST orbital trie
  pg_orrery--0.6.0.sql           # v0.6.0: conjunction screening
  pg_orrery--0.7.0.sql           # v0.7.0: GiST improvements
  pg_orrery--0.8.0.sql           # v0.8.0: orbital_elements type + MPC parser (82 functions)
  pg_orrery--0.9.0.sql           # v0.9.0: equatorial type, refraction, proper motion, light-time (106 functions)
  pg_orrery--0.10.0.sql          # v0.10.0: angular separation, cone search, apparent functions (114 functions)
  pg_orrery--0.11.0.sql          # v0.11.0: orbital_elements constructors, moon equatorial (120 functions)
  pg_orrery--0.12.0.sql          # v0.12.0: equatorial GiST, DE moon equatorial (132 objects)
  pg_orrery--0.13.0.sql          # v0.13.0: nutation, make_equatorial, rise/set (141 objects)
  pg_orrery--0.14.0.sql          # v0.14.0: refracted rise/set, constellation ID (147 objects)
  pg_orrery--0.15.0.sql          # v0.15.0: constellation full name, rise/set status (151 objects)
  pg_orrery--0.1.0--0.2.0.sql    # Migration: v0.1.0 → v0.2.0 (adds solar system)
  pg_orrery--0.2.0--0.3.0.sql    # Migration: v0.2.0 → v0.3.0 (adds DE ephemeris)
  pg_orrery--0.3.0--0.4.0.sql    # Migration: v0.3.0 → v0.4.0
  pg_orrery--0.4.0--0.5.0.sql    # Migration: v0.4.0 → v0.5.0
  pg_orrery--0.5.0--0.6.0.sql    # Migration: v0.5.0 → v0.6.0
  pg_orrery--0.6.0--0.7.0.sql    # Migration: v0.6.0 → v0.7.0
  pg_orrery--0.7.0--0.8.0.sql    # Migration: v0.7.0 → v0.8.0 (orbital_elements type)
  pg_orrery--0.8.0--0.9.0.sql    # Migration: v0.8.0 → v0.9.0 (equatorial, refraction, proper motion, light-time)
  pg_orrery--0.9.0--0.10.0.sql   # Migration: v0.9.0 → v0.10.0 (angular separation, cone search)
  pg_orrery--0.10.0--0.11.0.sql  # Migration: v0.10.0 → v0.11.0 (constructors, moon equatorial)
  pg_orrery--0.11.0--0.12.0.sql  # Migration: v0.11.0 → v0.12.0 (equatorial GiST, DE moon equatorial)
  pg_orrery--0.12.0--0.13.0.sql  # Migration: v0.12.0 → v0.13.0 (nutation, make_equatorial, rise/set)
  pg_orrery--0.13.0--0.14.0.sql  # Migration: v0.13.0 → v0.14.0 (refracted rise/set, constellation ID)
  pg_orrery--0.14.0--0.15.0.sql  # Migration: v0.14.0 → v0.15.0 (constellation full name, rise/set status)
src/
  pg_orrery.c                    # PG_MODULE_MAGIC + _PG_init() (GUC registration)
  types.h                       # All struct definitions + constants + DE body ID mapping
  astro_math.h                  # Shared astronomical helpers + observe_from_geocentric()
  # --- 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(), predict_passes_refracted(), pass_visible()
  gist_tle.c                    # GiST operator class for TLE (&&, <->)
  gist_equatorial.c              # GiST operator class for equatorial (KNN <->)
  # --- 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(), _equatorial(), _apparent()
  star_funcs.c                  # star_observe(), star_observe_safe(), star_equatorial(), star_observe_pm(), star_equatorial_pm()
  kepler_funcs.c                # kepler_propagate(), comet_observe()
  kepler.h                      # Shared Kepler solver interface (kepler_position())
  orbital_elements_type.c       # orbital_elements type, MPC parser, small_body_observe/equatorial/apparent()
  equatorial_funcs.c            # equatorial type I/O, accessors, satellite/planet/sun/moon RA/Dec
  refraction_funcs.c            # atmospheric_refraction(), _ext(), topo_elevation_apparent()
  rise_set_funcs.c              # planet/sun/moon rise/set (geometric + refracted)
  constellation_data.h / .c     # Roman (1987) IAU boundary table (CDS VI/42, 357 segments)
  constellation_funcs.c         # constellation() from equatorial or RA/Dec
  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()
  # --- JPL DE Ephemeris (v0.3.0) ---
  de_reader.h / de_reader.c     # Clean-room JPL DE binary reader (Chebyshev/Clenshaw)
  eph_provider.h / eph_provider.c # Provider dispatch, GUC, lazy init, frame rotation
  de_funcs.c                    # All _de() SQL function implementations (incl. moon equatorial DE)
  sgp4/                         # Vendored SGP4/SDP4 (Bill Gray's sat_code, MIT license)
    sgp4.c                      # Near-earth propagator (period < 225 min)
    sdp4.c                      # Deep-space propagator (period >= 225 min)
    deep.c                      # Lunar/solar perturbations, resonance integration
    common.c                    # Shared initialization (Brouwer mean elements, Kepler solver)
    basics.c                    # select_ephemeris(), FMod2p()
    get_el.c                    # TLE parsing (parse_elements(), checksum)
    tle_out.c                   # TLE output (write_elements_in_tle_format())
    norad.h / norad_in.h        # Public + internal headers
    PROVENANCE.md               # Vendoring decision, modifications, verification
    LICENSE                     # MIT license (Bill Gray / Project Pluto)
test/
  sql/                          # 26 regression test suites
  expected/                     # Expected output
  data/vallado_518.json         # 518 Vallado test vectors (AIAA 2006-6753-Rev1)
docs/
  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/             # MDX documentation pages

Type System

All types are fixed-size, STORAGE = plain, ALIGNMENT = double. No TOAST overhead.

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)
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 (151 SQL objects)

Domain Theory Key Functions Count
Satellite SGP4/SDP4 (Brouwer 1959) observe(), predict_passes(), eci_to_equatorial() 25
Planets VSOP87 (Bretagnon 1988) planet_observe(), planet_equatorial(), planet_observe_apparent() 7
Sun/Moon VSOP87 + ELP2000-82B sun_observe(), moon_observe(), sun/moon_equatorial() 6
Planetary moons L1.2, TASS17, GUST86, MarsSat galilean_observe(), saturn_moon_observe(), *_equatorial() 12
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(), *_equatorial_de(), *_apparent_de() 23
GiST index (TLE) Altitude-band approximation && (overlap), <-> (distance) 8
GiST index (equatorial) Spherical bounding box <-> (KNN ordering) 8
Rise/set Bisection (60s scan) planet_next_rise(), sun_next_rise_refracted(), *_rise_set_status() 15
Constellation Roman (1987) CDS VI/42 constellation(), constellation_full_name() 3
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).

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

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): 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.
  6. DE frame rotation: DE positions (ICRS equatorial) pass through equatorial_to_ecliptic() at the provider boundary before entering the observation pipeline
  7. Same-provider rule: Both target and Earth must come from the same provider in any geocentric computation (never mix DE target with VSOP87 Earth)
  8. DE AU consistency: Verify DE header AU matches compiled-in AU_KM (149597870.7) at init time

WGS-72 Constants (from Hoots & Roehrich STR#3, propagation only)

#define WGS72_MU      398600.8            /* km^3/s^2 */
#define WGS72_AE      6378.135            /* km */
#define WGS72_J2      0.001082616
#define WGS72_KE      0.0743669161331734132  /* (min)^(-1) */

WGS-84 Constants (coordinate output only)

#define WGS84_A       6378.137            /* km */
#define WGS84_F       (1.0 / 298.257223563)

Astronomical Constants

#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 */
#define C_LIGHT_AU_DAY  173.1446327         /* speed of light in AU/day */

JPL DE Ephemeris (Optional)

v0.3.0 adds optional JPL DE440/441 ephemeris support (~0.1 milliarcsecond accuracy) alongside the existing VSOP87 pipeline (~1 arcsecond). DE functions are separate _de() variants — existing VSOP87 functions are completely unchanged.

Architecture

  • Clean-room DE reader (de_reader.c): ~250 lines of C. Parses the JPL binary format, evaluates Chebyshev polynomials via Clenshaw recurrence. No GPL dependency (avoids Bill Gray's jpl_eph).
  • Per-backend lazy init: Each PostgreSQL backend opens its own file descriptor on first _de() call. Never opens in _PG_init() (postmaster context). Safe for PARALLEL SAFE.
  • VSOP87 fallback: Every _de() function falls back to its VSOP87/ELP82B equivalent when DE is unavailable.
  • STABLE volatility: DE functions are STABLE (not IMMUTABLE) because output depends on an external file. Existing VSOP87 functions remain IMMUTABLE.

GUC Configuration

-- Set the path to a JPL DE binary file (requires superuser)
ALTER SYSTEM SET pg_orrery.ephemeris_path = '/var/lib/postgres/de441.bin';
SELECT pg_reload_conf();

-- Check which provider is active
SELECT * FROM pg_orrery_ephemeris_info();
GUC Type Default Context
pg_orrery.ephemeris_path string '' (empty = VSOP87 only) SIGHUP (superuser only)

DE Function Variants

Every _de() function mirrors an existing VSOP87 function:

DE Function VSOP87 Equivalent Volatility
planet_heliocentric_de() planet_heliocentric() STABLE
planet_observe_de() planet_observe() STABLE
sun_observe_de() sun_observe() STABLE
moon_observe_de() moon_observe() STABLE
lambert_transfer_de() lambert_transfer() STABLE
lambert_c3_de() lambert_c3() STABLE
galilean_observe_de() galilean_observe() STABLE
saturn_moon_observe_de() saturn_moon_observe() STABLE
uranus_moon_observe_de() uranus_moon_observe() STABLE
mars_moon_observe_de() mars_moon_observe() STABLE
planet_equatorial_de() planet_equatorial() STABLE
moon_equatorial_de() moon_equatorial() STABLE
galilean_equatorial_de() galilean_equatorial() STABLE
saturn_moon_equatorial_de() saturn_moon_equatorial() STABLE
uranus_moon_equatorial_de() uranus_moon_equatorial() STABLE
mars_moon_equatorial_de() mars_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

Bill Gray's sat_code (MIT license): https://github.com/Bill-Gray/sat_code

Vendored into src/sgp4/ from upstream commit ff7b98957dfa2979700a482bde9de9542807293e. The .cpp files were renamed to .c — the code is valid C99 with zero C++ features (no classes, templates, namespaces, exceptions, or STL). This eliminates the g++ and -lstdc++ dependencies.

Modifications from upstream are minimal and documented in src/sgp4/PROVENANCE.md:

  • Renamed .cpp.c (no code changes — already valid C99)
  • Stripped Win32 DLL_FUNC/__stdcall decorators
  • Removed extern "C" wrapper (now compiled as C)
  • Removed unused SGP/SGP8/SDP8 model prototypes
  • Added forward declarations (-Wmissing-prototypes)
  • Changed bare inline to static inline for C99 compliance
  • Added equation citations referencing STR#3 and Vallado Rev-1

All numerical logic is byte-identical to upstream. Verified against 518 Vallado test vectors (AIAA 2006-6753-Rev1).

Testing

26 regression test suites via make installcheck:

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 (TLE)
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
de_ephemeris DE function fallback to VSOP87, cross-provider consistency, error handling
od_fit Orbit determination from ECI/topocentric/angles-only observations
spgist_tle SP-GiST orbital trie index operations
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
v011_features make_orbital_elements constructors, moon equatorial functions
gist_equatorial Equatorial GiST KNN ordering, RA wrapping, cone search, EXPLAIN index scan
v012_features DE moon equatorial fallback to VSOP87, invalid body_id rejection
v013_features Nutation correction, make_equatorial constructor
rise_set Planet/Sun/Moon rise/set (geometric + refracted), circumpolar, polar night
constellation Roman (1987) boundary lookup, known stars, solar system objects, edge cases
v015_features constellation_full_name lookup, rise_set_status diagnostics (circumpolar/never_rises)

PG Version Matrix

Test all 26 regression suites + DE reader unit test across PostgreSQL 14-18 using Docker:

make test-matrix                              # Full matrix (PG 14-18)
make test-pg18                                # Single version
PG_TEST_VERSIONS="16 17" make test-matrix     # Subset
make test-matrix-clean                        # Remove logs + test images

Logs saved to test/matrix-logs/pg${ver}.log. The script reuses the Dockerfile builder stage as the test engine — no additional test infrastructure.

Adding a new PG version: Update PG_TEST_VERSIONS default in Makefile and PG_VERSIONS default in test/pg-version-matrix.sh.

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

Live: https://pg-orrery.warehack.ing

Starlight docs at docs/ — 44+ MDX pages covering all domains.

Sections: Getting Started, Guides (9 domain walkthroughs incl. DE ephemeris), Workflow Translation (Skyfield/Horizons/GMAT/Radio Jupiter Pro comparisons), Reference (all 151 SQL objects incl. DE variants, equatorial GiST, refraction, rise/set, constellation), Architecture (Hamilton's principles, constant custody, observation pipeline), Performance (benchmarks).

Local Development

cd docs && npm run dev      # Dev server on :4321
cd docs && npm run build    # Static build to dist/

Production Deployment

The docs site deploys to the warehack.ing VPS (149.28.126.25) which runs caddy-docker-proxy with wildcard DNS for *.warehack.ing.

Deploy (or redeploy after changes):

ssh -A warehack-ing@pg-orrery.warehack.ing
cd ~/pg_orrery
git pull origin phase/spgist-orbital-trie   # or the current branch
cd docs
make prod                                       # builds image + starts container

First-time setup on VPS:

ssh -A warehack-ing@pg-orrery.warehack.ing
git clone git@git.supported.systems:warehack.ing/pg_orrery.git
cd pg_orrery && git checkout phase/spgist-orbital-trie
cat > docs/.env << 'EOF'
COMPOSE_PROJECT_NAME=pg-orrery-docs
NODE_ENV=production
VITE_HMR_HOST=pg-orrery.warehack.ing
EOF
cd docs && make prod

Makefile targets:

  • make prod — build + start production (Caddy serves static files)
  • make dev — build + start dev mode (hot-reload, volume mounts)
  • make down — stop containers
  • make restart — stop + start production
  • make clean — stop + remove volumes
  • make logs — tail container logs

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.

Coding Style

  • Standard PostgreSQL extension C style
  • ereport(ERROR, ...) for user-facing errors, never elog(ERROR, ...)
  • All memory via palloc/pfree (PostgreSQL memory contexts)
  • 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_orrery)
  • Every function handling SGP4 must check the error return code
  • All functions marked PARALLEL SAFE
  • DE functions: always fall back to VSOP87/ELP82B on any error

Git Conventions

  • One commit per logical change
  • Branch per phase: phase/spgist-orbital-trie
  • Tag releases: v0.1.0, v0.2.0, v0.3.0
  • Commit messages: imperative mood, no AI attribution