Skip to content

Functions: Atmospheric Refraction

Functions for computing atmospheric refraction corrections using Bennett’s (1982) empirical formula. Earth’s atmosphere bends light from celestial objects, making them appear higher above the horizon than their true geometric position. Near the horizon, refraction is approximately 0.57 degrees --- enough to extend satellite visibility windows by roughly 35 seconds at AOS and LOS, and to make the Sun appear above the horizon when it has already geometrically set.


Computes the atmospheric refraction correction in degrees for a given geometric elevation using Bennett’s (1982) formula under standard atmosphere conditions (pressure 1010 mbar, temperature 10 C). The domain is clamped at -1 degree to avoid singularity in the cotangent term.

atmospheric_refraction(elevation_deg float8) → float8
ParameterTypeUnitDescription
elevation_degfloat8degreesGeometric elevation of the object above the horizon

Refraction correction in degrees. Always positive --- add this value to the geometric elevation to get the apparent elevation. At the horizon (0 degrees), refraction is approximately 0.57 degrees. It drops rapidly with increasing elevation and is negligible above 45 degrees.

-- Refraction at various elevations
SELECT elevation,
round(atmospheric_refraction(elevation)::numeric, 4) AS refraction_deg
FROM unnest(ARRAY[-1, 0, 5, 10, 20, 45, 90]) AS elevation;
-- How much does refraction shift the Sun at sunset?
SELECT round(atmospheric_refraction(0)::numeric, 4) AS horizon_refraction_deg;

Computes atmospheric refraction with a pressure and temperature correction factor applied to Bennett’s formula, following the Meeus formulation. Useful for high-altitude observatories or extreme weather conditions where standard atmosphere assumptions break down.

atmospheric_refraction_ext(elevation_deg float8, pressure_mbar float8, temp_celsius float8) → float8
ParameterTypeUnitDescription
elevation_degfloat8degreesGeometric elevation of the object above the horizon
pressure_mbarfloat8mbarAtmospheric pressure at the observer
temp_celsiusfloat8CAir temperature at the observer

Refraction correction in degrees, adjusted for the given pressure and temperature. The correction factor is (P / 1010) * (283 / (273 + T)) applied to the standard Bennett formula result.

-- Refraction at Mauna Kea summit (4205m, ~620 mbar, -2C)
SELECT round(atmospheric_refraction_ext(5.0, 620.0, -2.0)::numeric, 4) AS refraction_mauna_kea,
round(atmospheric_refraction(5.0)::numeric, 4) AS refraction_standard;
-- Compare standard vs corrected refraction across a range of elevations
SELECT elevation,
round(atmospheric_refraction(elevation)::numeric, 4) AS standard,
round(atmospheric_refraction_ext(elevation, 850.0, -10.0)::numeric, 4) AS high_altitude_cold
FROM unnest(ARRAY[0, 2, 5, 10, 30]) AS elevation;

Convenience function that returns the apparent elevation of an object by adding the atmospheric refraction correction to the geometric elevation stored in a topocentric value. The result is in degrees.

topo_elevation_apparent(topocentric) → float8
ParameterTypeDescription
(unnamed)topocentricA topocentric observation result from any *_observe function

Apparent elevation in degrees --- the geometric elevation plus the Bennett refraction correction under standard atmosphere. Always higher than the geometric topo_elevation() value.

-- Compare geometric vs apparent elevation for the Moon
SELECT round(topo_elevation(t)::numeric, 3) AS geometric_el,
round(topo_elevation_apparent(t)::numeric, 3) AS apparent_el,
round(topo_elevation_apparent(t) - topo_elevation(t)::numeric, 4) AS refraction_correction
FROM moon_observe('40.0N 105.3W 1655m'::observer, now()) AS t;
-- Find objects that are geometrically below horizon but visible due to refraction
SELECT norad_id,
round(topo_elevation(o)::numeric, 3) AS geometric_el,
round(topo_elevation_apparent(o)::numeric, 3) AS apparent_el
FROM satellite_catalog,
observe_safe(tle, '40.0N 105.3W 1655m'::observer, now()) AS o
WHERE o IS NOT NULL
AND topo_elevation(o) < 0
AND topo_elevation_apparent(o) > 0;

Predicts satellite passes using a refracted horizon threshold instead of the geometric horizon. The geometric threshold is set to -0.569 degrees, which corresponds to the apparent horizon after atmospheric refraction. This means satellites become visible approximately 35 seconds earlier at AOS and remain visible approximately 35 seconds later at LOS compared to predict_passes.

predict_passes_refracted(
tle tle,
obs observer,
start_time timestamptz,
end_time timestamptz,
min_el float8 DEFAULT 0.0
) → SETOF pass_event
ParameterTypeDefaultDescription
tletleSatellite TLE
obsobserverObserver location
start_timetimestamptzStart of the search window
end_timetimestamptzEnd of the search window
min_elfloat80.0Minimum peak elevation in degrees. Passes whose maximum elevation is below this threshold are excluded.

A set of pass_event records, ordered by AOS time. Each pass will show slightly earlier AOS and later LOS times compared to predict_passes due to the refracted horizon.

-- Compare geometric vs refracted pass predictions for the ISS
WITH iss AS (
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle AS tle
)
SELECT pass_aos_time(p) AS rise,
pass_max_elevation(p) AS max_el,
pass_los_time(p) AS set,
pass_duration(p) AS dur
FROM iss,
predict_passes_refracted(tle, '40.0N 105.3W 1655m'::observer,
now(), now() + interval '24 hours', 10.0) AS p;
-- How much extra visibility does refraction add?
WITH iss AS (
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle AS tle
),
geo AS (
SELECT pass_duration(p) AS dur
FROM iss, predict_passes(tle, '40.0N 105.3W 1655m'::observer,
now(), now() + interval '24 hours') AS p
LIMIT 1
),
refr AS (
SELECT pass_duration(p) AS dur
FROM iss, predict_passes_refracted(tle, '40.0N 105.3W 1655m'::observer,
now(), now() + interval '24 hours') AS p
LIMIT 1
)
SELECT geo.dur AS geometric_duration,
refr.dur AS refracted_duration
FROM geo, refr;