/* * 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 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); }