/* * star_funcs.c -- Star and fixed-position object observation * * Takes J2000 catalog coordinates (RA in hours, Dec in degrees), * applies IAU 1976 precession to date of observation, computes * local hour angle, and converts to topocentric azimuth/elevation. * * Range and range_rate are zero -- stars are effectively at infinity. * For objects with known proper motion, apply it to (RA, Dec) before * calling star_observe. */ #include "postgres.h" #include "fmgr.h" #include "utils/timestamp.h" #include "types.h" #include "astro_math.h" PG_FUNCTION_INFO_V1(star_observe); PG_FUNCTION_INFO_V1(star_observe_safe); /* * star_observe(ra_hours, dec_degrees, observer, timestamptz) -> topocentric * * Compute az/el of a fixed celestial object from an observer at a time. * Uses IAU 1976 precession (~1 arcsecond accuracy for centuries near J2000). */ Datum star_observe(PG_FUNCTION_ARGS) { double ra_hours = PG_GETARG_FLOAT8(0); double dec_deg = PG_GETARG_FLOAT8(1); pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(2); int64 ts = PG_GETARG_INT64(3); double jd; double ra_j2000, dec_j2000; double ra_date, dec_date; double gmst, lst, ha; double az, el; pg_topocentric *result; if (ra_hours < 0.0 || ra_hours >= 24.0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("right ascension out of range: %.6f", ra_hours), errhint("RA must be in [0, 24) hours."))); if (dec_deg < -90.0 || dec_deg > 90.0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("declination out of range: %.6f", dec_deg), errhint("Declination must be between -90 and +90 degrees."))); jd = timestamptz_to_jd(ts); ra_j2000 = ra_hours * (M_PI / 12.0); dec_j2000 = dec_deg * DEG_TO_RAD; precess_j2000_to_date(jd, ra_j2000, dec_j2000, &ra_date, &dec_date); gmst = gmst_from_jd(jd); lst = gmst + obs->lon; ha = lst - ra_date; equatorial_to_horizontal(ha, dec_date, obs->lat, &az, &el); result = (pg_topocentric *) palloc(sizeof(pg_topocentric)); result->azimuth = az; result->elevation = el; result->range_km = 0.0; result->range_rate = 0.0; PG_RETURN_POINTER(result); } /* * star_observe_safe -- returns NULL if inputs are out of range. * For batch queries over star catalogs. */ Datum star_observe_safe(PG_FUNCTION_ARGS) { double ra_hours = PG_GETARG_FLOAT8(0); double dec_deg = PG_GETARG_FLOAT8(1); pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(2); int64 ts = PG_GETARG_INT64(3); double jd; double ra_j2000, dec_j2000; double ra_date, dec_date; double gmst, lst, ha; double az, el; pg_topocentric *result; if (ra_hours < 0.0 || ra_hours >= 24.0) PG_RETURN_NULL(); if (dec_deg < -90.0 || dec_deg > 90.0) PG_RETURN_NULL(); jd = timestamptz_to_jd(ts); ra_j2000 = ra_hours * (M_PI / 12.0); dec_j2000 = dec_deg * DEG_TO_RAD; precess_j2000_to_date(jd, ra_j2000, dec_j2000, &ra_date, &dec_date); gmst = gmst_from_jd(jd); lst = gmst + obs->lon; ha = lst - ra_date; equatorial_to_horizontal(ha, dec_date, obs->lat, &az, &el); result = (pg_topocentric *) palloc(sizeof(pg_topocentric)); result->azimuth = az; result->elevation = el; result->range_km = 0.0; result->range_rate = 0.0; PG_RETURN_POINTER(result); }