pg_orrery/sql/pg_orrery--0.16.0--0.17.0.sql
Ryan Malloy 22b272fd0c Implement v0.17.0: solar elongation, planet phase, satellite eclipse, observing night quality, lunar libration
162 → 174 SQL objects, 27 → 28 test suites, 3 new C source files.

Features:
- solar_elongation(body_id, ts): Sun-Earth-Planet angle [0,180] degrees
- planet_phase(body_id, ts): illuminated disk fraction [0,1]
- satellite_is_eclipsed/next_eclipse_entry/exit/eclipse_fraction:
  cylindrical shadow model (Vallado §5.3) for Earth shadow prediction
- observing_night_quality(observer, ts): composite PL/pgSQL scoring
  based on astronomical darkness duration and Moon interference
- moon_libration_longitude/latitude/position_angle/libration/subsolar_longitude:
  optical libration from Meeus (1998) Ch. 53

Refactored magnitude_funcs.c to extract shared compute_planet_geometry()
used by magnitude, elongation, and phase — single VSOP87 evaluation per call.

All 28 regression suites pass. Zero compiler warnings.
2026-02-26 18:47:30 -07:00

140 lines
6.1 KiB
PL/PgSQL

-- pg_orrery 0.16.0 -> 0.17.0: solar elongation, planet phase, satellite eclipse,
-- observing night quality, lunar libration
-- ============================================================
-- Solar elongation (1)
-- ============================================================
CREATE FUNCTION solar_elongation(int4, timestamptz) RETURNS float8
AS 'MODULE_PATHNAME', 'solar_elongation'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION solar_elongation(int4, timestamptz) IS
'Sun-Earth-Planet angle in degrees [0, 180]. How far a planet appears from the Sun. Body IDs 1-8.';
-- ============================================================
-- Planet phase fraction (1)
-- ============================================================
CREATE FUNCTION planet_phase(int4, timestamptz) RETURNS float8
AS 'MODULE_PATHNAME', 'planet_phase'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION planet_phase(int4, timestamptz) IS
'Illuminated fraction of a planet disk as seen from Earth [0.0, 1.0]. Body IDs 1-8.';
-- ============================================================
-- Satellite eclipse prediction (4)
-- ============================================================
CREATE FUNCTION satellite_is_eclipsed(tle, timestamptz) RETURNS bool
AS 'MODULE_PATHNAME', 'satellite_is_eclipsed'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION satellite_is_eclipsed(tle, timestamptz) IS
'True if the satellite is in Earth cylindrical shadow at the given time.';
CREATE FUNCTION satellite_next_eclipse_entry(tle, timestamptz) RETURNS timestamptz
AS 'MODULE_PATHNAME', 'satellite_next_eclipse_entry'
LANGUAGE C STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION satellite_next_eclipse_entry(tle, timestamptz) IS
'Next time the satellite enters Earth shadow (up to 7-day search). NULL if none found.';
CREATE FUNCTION satellite_next_eclipse_exit(tle, timestamptz) RETURNS timestamptz
AS 'MODULE_PATHNAME', 'satellite_next_eclipse_exit'
LANGUAGE C STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION satellite_next_eclipse_exit(tle, timestamptz) IS
'Next time the satellite exits Earth shadow (up to 7-day search). NULL if none found.';
CREATE FUNCTION satellite_eclipse_fraction(tle, timestamptz, timestamptz) RETURNS float8
AS 'MODULE_PATHNAME', 'satellite_eclipse_fraction'
LANGUAGE C STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION satellite_eclipse_fraction(tle, timestamptz, timestamptz) IS
'Fraction of the given time window the satellite spends in eclipse [0.0, 1.0].';
-- ============================================================
-- Observing night quality (1)
-- ============================================================
CREATE FUNCTION observing_night_quality(observer, timestamptz DEFAULT NOW())
RETURNS text AS $$
DECLARE
astro_dusk timestamptz;
astro_dawn timestamptz;
dark_hours float8;
illum float8;
moon_up bool;
score int := 100;
BEGIN
-- Astronomical darkness window
astro_dusk := sun_astronomical_dusk($1, $2);
IF astro_dusk IS NULL THEN
RETURN 'poor'; -- No astronomical darkness (polar summer)
END IF;
astro_dawn := sun_astronomical_dawn($1, astro_dusk);
IF astro_dawn IS NULL THEN
RETURN 'poor';
END IF;
dark_hours := extract(epoch FROM astro_dawn - astro_dusk) / 3600.0;
-- Short dark window penalty
IF dark_hours < 2.0 THEN score := score - 40;
ELSIF dark_hours < 4.0 THEN score := score - 20;
ELSIF dark_hours < 6.0 THEN score := score - 10;
END IF;
-- Moon illumination penalty
illum := moon_illumination(astro_dusk);
IF illum > 0.75 THEN
-- Check if Moon is above horizon during darkness
moon_up := (moon_observe($1, astro_dusk)).elevation > 0
OR (moon_observe($1, astro_dusk + (astro_dawn - astro_dusk) / 2)).elevation > 0;
IF moon_up THEN
score := score - (illum * 30)::int; -- Up to -30 for full moon
END IF;
END IF;
-- Classify
IF score >= 80 THEN RETURN 'excellent';
ELSIF score >= 60 THEN RETURN 'good';
ELSIF score >= 40 THEN RETURN 'fair';
ELSE RETURN 'poor';
END IF;
END;
$$ LANGUAGE plpgsql STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION observing_night_quality(observer, timestamptz) IS
'Composite observing quality assessment: excellent/good/fair/poor based on darkness duration and Moon interference.';
-- ============================================================
-- Lunar libration (5)
-- ============================================================
CREATE FUNCTION moon_libration_longitude(timestamptz) RETURNS float8
AS 'MODULE_PATHNAME', 'moon_libration_longitude'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_libration_longitude(timestamptz) IS
'Optical libration in longitude (degrees, typically [-8, +8]). Meeus Ch. 53.';
CREATE FUNCTION moon_libration_latitude(timestamptz) RETURNS float8
AS 'MODULE_PATHNAME', 'moon_libration_latitude'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_libration_latitude(timestamptz) IS
'Optical libration in latitude (degrees, typically [-7, +7]). Meeus Ch. 53.';
CREATE FUNCTION moon_libration_position_angle(timestamptz) RETURNS float8
AS 'MODULE_PATHNAME', 'moon_libration_position_angle'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_libration_position_angle(timestamptz) IS
'Position angle of the Moon axis (degrees, [0, 360)). Meeus Ch. 53.';
CREATE FUNCTION moon_libration(timestamptz,
OUT l float8, OUT b float8, OUT p float8) RETURNS record
AS 'MODULE_PATHNAME', 'moon_libration'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_libration(timestamptz) IS
'All three libration values: longitude (l), latitude (b), position angle (p) in degrees.';
CREATE FUNCTION moon_subsolar_longitude(timestamptz) RETURNS float8
AS 'MODULE_PATHNAME', 'moon_subsolar_longitude'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_subsolar_longitude(timestamptz) IS
'Selenographic longitude of the sub-solar point (degrees, [0, 360)). Determines the lunar terminator position.';