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).
141 lines
4.4 KiB
C
141 lines
4.4 KiB
C
/*
|
|
* de_reader.h -- Clean-room JPL Development Ephemeris reader
|
|
*
|
|
* Reads JPL DE430/DE440/DE441 binary ephemeris files.
|
|
* Implements Chebyshev polynomial evaluation via Clenshaw recurrence.
|
|
*
|
|
* No GPL dependency: written from the public JPL binary format spec.
|
|
* No global state: each handle is independently opened/managed.
|
|
*
|
|
* Reference:
|
|
* JPL IOM 312.N-03-009 "The JPL Planetary and Lunar Ephemerides,
|
|
* DE405/LE405" (Standish 1998) — format description.
|
|
*/
|
|
|
|
#ifndef PG_ORRERY_DE_READER_H
|
|
#define PG_ORRERY_DE_READER_H
|
|
|
|
#include <stdint.h>
|
|
|
|
/*
|
|
* JPL DE body targets.
|
|
*
|
|
* The ephemeris stores 13 "body groups" in a specific order.
|
|
* Each group has its own coefficient layout (offset, ncoeff, nsub).
|
|
*/
|
|
#define DE_MERCURY 0
|
|
#define DE_VENUS 1
|
|
#define DE_EMB 2 /* Earth-Moon Barycenter */
|
|
#define DE_MARS 3
|
|
#define DE_JUPITER 4
|
|
#define DE_SATURN 5
|
|
#define DE_URANUS 6
|
|
#define DE_NEPTUNE 7
|
|
#define DE_PLUTO 8
|
|
#define DE_MOON 9 /* Moon (geocentric) */
|
|
#define DE_SUN 10
|
|
#define DE_NUTATION 11 /* nutations (dpsi, deps) */
|
|
#define DE_LIBRATION 12 /* lunar librations */
|
|
#define DE_NUM_BODIES 13
|
|
|
|
/*
|
|
* DE reader error codes.
|
|
*/
|
|
#define DE_OK 0
|
|
#define DE_ERR_OPEN -1 /* cannot open file */
|
|
#define DE_ERR_READ -2 /* read() failed or short read */
|
|
#define DE_ERR_HEADER -3 /* header validation failed */
|
|
#define DE_ERR_RANGE -4 /* JD out of ephemeris range */
|
|
#define DE_ERR_BODY -5 /* invalid body target */
|
|
#define DE_ERR_CANARY -6 /* canary validation failed */
|
|
#define DE_ERR_ENDIAN -7 /* byte order detection failed */
|
|
|
|
/*
|
|
* Coefficient layout for one body group.
|
|
* Parsed from the header's IPT array.
|
|
*/
|
|
typedef struct de_body_layout
|
|
{
|
|
int offset; /* word offset within record (1-based) */
|
|
int ncoeff; /* number of Chebyshev coefficients per component */
|
|
int nsub; /* number of sub-intervals per record interval */
|
|
} de_body_layout;
|
|
|
|
/*
|
|
* DE reader handle.
|
|
*
|
|
* One per PostgreSQL backend. Owns its file descriptor and
|
|
* a pre-allocated coefficient buffer.
|
|
*/
|
|
typedef struct de_handle
|
|
{
|
|
int fd; /* file descriptor */
|
|
int swap_bytes; /* 1 if byte-swapping needed */
|
|
|
|
/* Header metadata */
|
|
double start_jd; /* first valid JD */
|
|
double end_jd; /* last valid JD */
|
|
double interval_days; /* days per record */
|
|
double au_km; /* AU in km (from header) */
|
|
double emrat; /* Earth-Moon mass ratio */
|
|
int ncoeff; /* number of doubles per record */
|
|
int de_version; /* e.g. 441 */
|
|
|
|
/* Coefficient layout per body group */
|
|
de_body_layout layout[DE_NUM_BODIES];
|
|
|
|
/* Record buffer: re-used across queries */
|
|
double *record_buf; /* ncoeff doubles */
|
|
int cached_recno; /* which record is loaded, -1 = none */
|
|
|
|
/* Record geometry */
|
|
int record_bytes; /* ncoeff * sizeof(double) */
|
|
long data_offset; /* byte offset to first data record */
|
|
} de_handle;
|
|
|
|
|
|
/*
|
|
* Open and validate a JPL DE binary file.
|
|
*
|
|
* Returns a heap-allocated handle on success, NULL on failure.
|
|
* Sets *errcode to one of DE_OK / DE_ERR_*.
|
|
* Caller must eventually call de_reader_close().
|
|
*
|
|
* Does NOT use ereport() — caller translates error codes.
|
|
*/
|
|
de_handle *de_reader_open(const char *path, int *errcode);
|
|
|
|
|
|
/*
|
|
* Get the position of a body relative to a center.
|
|
*
|
|
* target: DE_MERCURY..DE_SUN (0-10)
|
|
* center: DE_SUN (10) for heliocentric, DE_EMB (2) for geocentric, etc.
|
|
* Use -1 for "raw" (no center subtraction)
|
|
* jd: Julian date (TDB)
|
|
* pos[3]: output position in AU (ICRS equatorial frame)
|
|
*
|
|
* Returns DE_OK on success, DE_ERR_* on failure.
|
|
*
|
|
* For Earth position: returns Earth (derived from EMB and Moon)
|
|
* For Moon position with center=Earth: returns geocentric Moon
|
|
*/
|
|
int de_reader_get_pos(de_handle *h, double jd, int target, int center,
|
|
double pos[3]);
|
|
|
|
|
|
/*
|
|
* Close the DE reader handle and free all resources.
|
|
* Safe to call with NULL handle.
|
|
*/
|
|
void de_reader_close(de_handle *h);
|
|
|
|
|
|
/*
|
|
* Look up a named constant from the DE header.
|
|
* Returns the value, or NAN if not found (use isnan() to check).
|
|
*/
|
|
double de_reader_get_const(de_handle *h, const char *name);
|
|
|
|
#endif /* PG_ORRERY_DE_READER_H */
|