pg_orrery/src/refraction_funcs.c
Ryan Malloy b33d63034b Add v0.9.0 apparent position features: equatorial type, refraction, proper motion, light-time
New equatorial type (24 bytes: RA/Dec/distance) captures apparent coordinates
of date — what the observation pipeline computes at precession step 3 but was
discarding before hour angle conversion. Matches telescope GoTo mount conventions.

24 new SQL functions (82 → 106 total):
- equatorial type I/O + 3 accessors (eq_ra, eq_dec, eq_distance)
- Satellite RA/Dec: eci_to_equatorial (topocentric), eci_to_equatorial_geo (geocentric)
- Solar system equatorial: planet/sun/moon/small_body_equatorial
- Atmospheric refraction: Bennett (1982) with domain clamp at -1 deg
- Refracted pass prediction: predict_passes_refracted (horizon at -0.569 deg)
- Stellar proper motion: star_observe_pm, star_equatorial_pm (Hipparcos/Gaia convention)
- Light-time correction: planet/sun/small_body_observe_apparent, *_equatorial_apparent
- DE equatorial variants: planet_equatorial_de, moon_equatorial_de

Also includes v0.8.0 orbital_elements type (MPC parser, small_body_observe),
GiST 0-based indexing fix, llms.txt updates, and doc improvements.

All 18 regression suites pass. Zero build warnings (GCC + Clang).
2026-02-21 15:31:46 -07:00

121 lines
2.7 KiB
C

/*
* refraction_funcs.c -- Atmospheric refraction for pg_orrery
*
* Bennett's (1982) formula for standard atmosphere, with optional
* pressure/temperature correction per Meeus (1991).
*
* Domain guard: clamp geometric elevation to -1 deg before tan()
* evaluation. Below that, Bennett's formula is outside its valid
* range and we return 0.
*/
#include "postgres.h"
#include "fmgr.h"
#include "types.h"
#include <math.h>
PG_FUNCTION_INFO_V1(atmospheric_refraction);
PG_FUNCTION_INFO_V1(atmospheric_refraction_ext);
PG_FUNCTION_INFO_V1(topo_elevation_apparent);
#define RAD_TO_DEG (180.0 / M_PI)
#define DEG_TO_RAD (M_PI / 180.0)
/*
* bennett_refraction -- core Bennett (1982) formula
*
* Input: geometric elevation in degrees
* Output: refraction correction in degrees (>= 0)
*
* R = 1/tan(h + 7.31/(h + 4.4)) arcminutes
* where h is the geometric elevation in degrees.
*
* The formula diverges near h = -4.4 deg; clamp to -1 deg
* (below which the atmosphere model is meaningless anyway).
*/
static double
bennett_refraction(double h_deg)
{
double h, R;
if (h_deg < -1.0)
return 0.0;
h = fmax(h_deg, -1.0);
/* Bennett's formula: arcminutes */
R = 1.0 / tan((h + 7.31 / (h + 4.4)) * DEG_TO_RAD);
if (!isfinite(R))
return 0.0;
/* Convert arcminutes to degrees */
R /= 60.0;
if (R < 0.0)
R = 0.0;
return R;
}
/*
* atmospheric_refraction(elevation_deg) -> refraction_deg
*
* Standard atmosphere: P = 1010 mbar, T = 10 C.
*/
Datum
atmospheric_refraction(PG_FUNCTION_ARGS)
{
double h_deg = PG_GETARG_FLOAT8(0);
PG_RETURN_FLOAT8(bennett_refraction(h_deg));
}
/*
* atmospheric_refraction_ext(elevation_deg, pressure_mbar, temp_celsius)
* -> refraction_deg
*
* Meeus correction for non-standard atmosphere:
* R_corrected = R * (P / 1010.0) * (283.0 / (273.0 + T))
*/
Datum
atmospheric_refraction_ext(PG_FUNCTION_ARGS)
{
double h_deg = PG_GETARG_FLOAT8(0);
double P = PG_GETARG_FLOAT8(1);
double T = PG_GETARG_FLOAT8(2);
double R;
R = bennett_refraction(h_deg);
/* Meeus pressure/temperature correction */
R *= (P / 1010.0) * (283.0 / (273.0 + T));
if (!isfinite(R) || R < 0.0)
R = 0.0;
PG_RETURN_FLOAT8(R);
}
/*
* topo_elevation_apparent(topocentric) -> apparent_elevation_deg
*
* Geometric elevation from the topocentric type plus Bennett's
* atmospheric refraction correction.
*/
Datum
topo_elevation_apparent(PG_FUNCTION_ARGS)
{
pg_topocentric *topo = (pg_topocentric *) PG_GETARG_POINTER(0);
double el_deg;
double refr;
el_deg = topo->elevation * RAD_TO_DEG;
refr = bennett_refraction(el_deg);
PG_RETURN_FLOAT8(el_deg + refr);
}