/* * planet_funcs.c -- VSOP87 planet and ELP82B Moon observation * * SQL functions for planetary/Sun/Moon position and observation. * Wraps VSOP87 (Bretagnon & Francou) for 8 planets + Sun, and * ELP2000-82B (Chapront-Touze) for the Moon. * * The observation pipeline: * 1. Heliocentric ecliptic J2000 position (VSOP87 or ELP82B) * 2. Geocentric ecliptic (subtract Earth's heliocentric) * 3. Ecliptic -> equatorial J2000 (obliquity rotation) * 4. Spherical coordinates (RA, Dec, distance) * 5. Precession J2000 -> date (IAU 1976, Lieske) * 6. Sidereal time -> hour angle -> az/el * 7. Return topocentric result */ #include "postgres.h" #include "fmgr.h" #include "funcapi.h" #include "utils/timestamp.h" #include "types.h" #include "astro_math.h" #include "vsop87.h" #include "elp82b.h" #include PG_FUNCTION_INFO_V1(planet_heliocentric); PG_FUNCTION_INFO_V1(planet_observe); PG_FUNCTION_INFO_V1(sun_observe); PG_FUNCTION_INFO_V1(moon_observe); /* * observe_from_geocentric() is now in astro_math.h as a static inline, * shared by planet_funcs.c, moon_funcs.c, and de_funcs.c. */ /* ================================================================ * planet_heliocentric(body_id int, timestamptz) -> heliocentric * * Returns the heliocentric ecliptic J2000 position of a planet * (or the Sun, as the barycenter proxy at (0,0,0)). * * Body IDs: 0=Sun, 1=Mercury, ..., 8=Neptune * ================================================================ */ Datum planet_heliocentric(PG_FUNCTION_ARGS) { int32 body_id = PG_GETARG_INT32(0); int64 ts = PG_GETARG_INT64(1); double jd; double xyz[6]; int vsop_body; pg_heliocentric *result; if (body_id == BODY_SUN) { /* Sun is at the origin of heliocentric coordinates */ result = (pg_heliocentric *) palloc(sizeof(pg_heliocentric)); result->x = 0.0; result->y = 0.0; result->z = 0.0; PG_RETURN_POINTER(result); } if (body_id < BODY_MERCURY || body_id > BODY_NEPTUNE) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("invalid body_id %d: must be 0 (Sun) or 1-8 (Mercury-Neptune)", body_id))); jd = timestamptz_to_jd(ts); vsop_body = body_id - 1; /* pg_orrery 1-based -> VSOP87 0-based */ GetVsop87Coor(jd, vsop_body, xyz); result = (pg_heliocentric *) palloc(sizeof(pg_heliocentric)); result->x = xyz[0]; result->y = xyz[1]; result->z = xyz[2]; PG_RETURN_POINTER(result); } /* ================================================================ * planet_observe(body_id int, observer, timestamptz) -> topocentric * * Full pipeline: VSOP87 position -> geocentric -> equatorial -> * precession -> topocentric az/el. * * Body IDs: 1=Mercury, ..., 8=Neptune (not Sun or Moon) * ================================================================ */ Datum planet_observe(PG_FUNCTION_ARGS) { int32 body_id = PG_GETARG_INT32(0); pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(1); int64 ts = PG_GETARG_INT64(2); double jd; double earth_xyz[6]; double planet_xyz[6]; double geo_ecl[3]; int vsop_body; pg_topocentric *result; if (body_id < BODY_MERCURY || body_id > BODY_NEPTUNE) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("planet_observe: body_id %d must be 1-8 (Mercury-Neptune)", body_id))); if (body_id == BODY_EARTH) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot observe Earth from Earth"))); jd = timestamptz_to_jd(ts); /* Earth's heliocentric position */ GetVsop87Coor(jd, 2, earth_xyz); /* VSOP87 body 2 = Earth */ /* Target planet heliocentric position */ vsop_body = body_id - 1; GetVsop87Coor(jd, vsop_body, planet_xyz); /* Geocentric ecliptic = planet - Earth */ geo_ecl[0] = planet_xyz[0] - earth_xyz[0]; geo_ecl[1] = planet_xyz[1] - earth_xyz[1]; geo_ecl[2] = planet_xyz[2] - earth_xyz[2]; result = (pg_topocentric *) palloc(sizeof(pg_topocentric)); observe_from_geocentric(geo_ecl, jd, obs, result); PG_RETURN_POINTER(result); } /* ================================================================ * sun_observe(observer, timestamptz) -> topocentric * * The Sun's geocentric position is the negation of Earth's * heliocentric VSOP87 position. * ================================================================ */ Datum sun_observe(PG_FUNCTION_ARGS) { pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(0); int64 ts = PG_GETARG_INT64(1); double jd; double earth_xyz[6]; double geo_ecl[3]; pg_topocentric *result; jd = timestamptz_to_jd(ts); /* Earth heliocentric -> Sun geocentric by negation */ GetVsop87Coor(jd, 2, earth_xyz); geo_ecl[0] = -earth_xyz[0]; geo_ecl[1] = -earth_xyz[1]; geo_ecl[2] = -earth_xyz[2]; result = (pg_topocentric *) palloc(sizeof(pg_topocentric)); observe_from_geocentric(geo_ecl, jd, obs, result); PG_RETURN_POINTER(result); } /* ================================================================ * moon_observe(observer, timestamptz) -> topocentric * * ELP2000-82B returns geocentric ecliptic J2000 Moon position. * No Earth subtraction needed. * ================================================================ */ Datum moon_observe(PG_FUNCTION_ARGS) { pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(0); int64 ts = PG_GETARG_INT64(1); double jd; double moon_ecl[3]; pg_topocentric *result; jd = timestamptz_to_jd(ts); /* Moon geocentric ecliptic J2000 in AU */ GetElp82bCoor(jd, moon_ecl); result = (pg_topocentric *) palloc(sizeof(pg_topocentric)); observe_from_geocentric(moon_ecl, jd, obs, result); PG_RETURN_POINTER(result); }