Skip to content

Functions: Solar System

Functions for computing planetary positions, observing the Sun, Moon, and planets from an Earth-based observer. Planetary positions use the VSOP87 theory (Bretagnon & Francou, 1988). Lunar position uses ELP2000-82B (Chapront-Touze & Chapront, 1983). All functions are IMMUTABLE STRICT PARALLEL SAFE.

For higher precision, v0.3.0 adds optional _de() variants that use JPL DE440/441 ephemeris files. See Functions: DE Ephemeris.


Computes the heliocentric ecliptic J2000 position of a solar system body using VSOP87 series C (heliocentric, ecliptic, rectangular). Returns position in Astronomical Units.

planet_heliocentric(body_id int4, t timestamptz) → heliocentric
ParameterTypeDescription
body_idint4Planet identifier (see table below)
ttimestamptzEvaluation time
IDBody
0Sun (returns origin: 0, 0, 0)
1Mercury
2Venus
3Earth
4Mars
5Jupiter
6Saturn
7Uranus
8Neptune

A heliocentric position in AU (ecliptic J2000 frame). For body_id = 0 (Sun), all components are zero.

-- Distance of each planet from the Sun
SELECT body_id,
CASE body_id
WHEN 1 THEN 'Mercury' WHEN 2 THEN 'Venus'
WHEN 3 THEN 'Earth' WHEN 4 THEN 'Mars'
WHEN 5 THEN 'Jupiter' WHEN 6 THEN 'Saturn'
WHEN 7 THEN 'Uranus' WHEN 8 THEN 'Neptune'
END AS planet,
round(helio_distance(planet_heliocentric(body_id, now()))::numeric, 6) AS dist_au
FROM generate_series(1, 8) AS body_id;
-- Earth's position over one year at weekly intervals
SELECT t,
helio_x(h) AS x_au,
helio_y(h) AS y_au,
helio_z(h) AS z_au
FROM generate_series(
'2024-01-01'::timestamptz,
'2025-01-01'::timestamptz,
interval '7 days'
) AS t,
planet_heliocentric(3, t) AS h;

Computes the topocentric position of a planet as seen from an Earth-based observer. Internally computes the heliocentric positions of both Earth and the target planet, applies geometric transformation to geocentric, then converts to topocentric coordinates.

planet_observe(body_id int4, obs observer, t timestamptz) → topocentric
ParameterTypeDescription
body_idint4Planet identifier (1-8, same as planet_heliocentric excluding 0 and 3)
obsobserverObserver location on Earth
ttimestamptzObservation time

A topocentric with azimuth, elevation, range (km), and range rate (km/s).

-- Where is Mars tonight from Greenwich?
SELECT topo_azimuth(t) AS az_deg,
topo_elevation(t) AS el_deg,
topo_range(t) / 149597870.7 AS dist_au
FROM planet_observe(4, '51.4769N 0.0005W 11m'::observer, now()) AS t;
-- All planets' current positions from Boulder
SELECT body_id,
CASE body_id
WHEN 1 THEN 'Mercury' WHEN 2 THEN 'Venus'
WHEN 4 THEN 'Mars' WHEN 5 THEN 'Jupiter'
WHEN 6 THEN 'Saturn' WHEN 7 THEN 'Uranus'
WHEN 8 THEN 'Neptune'
END AS planet,
round(topo_azimuth(t)::numeric, 2) AS az,
round(topo_elevation(t)::numeric, 2) AS el
FROM unnest(ARRAY[1,2,4,5,6,7,8]) AS body_id,
planet_observe(body_id, '40.0N 105.3W 1655m'::observer, now()) AS t
ORDER BY topo_elevation(t) DESC;

Computes the topocentric position of the Sun from an Earth-based observer.

sun_observe(obs observer, t timestamptz) → topocentric
ParameterTypeDescription
obsobserverObserver location on Earth
ttimestamptzObservation time

A topocentric with azimuth, elevation, range (km), and range rate (km/s).

-- Sun position right now
SELECT topo_azimuth(t) AS az,
topo_elevation(t) AS el,
topo_range(t) / 149597870.7 AS dist_au
FROM sun_observe('40.0N 105.3W 1655m'::observer, now()) AS t;
-- Find today's solar noon (maximum elevation)
SELECT t,
round(topo_elevation(s)::numeric, 2) AS el
FROM generate_series(
now()::date::timestamptz,
now()::date::timestamptz + interval '24 hours',
interval '1 minute'
) AS t,
sun_observe('40.0N 105.3W 1655m'::observer, t) AS s
ORDER BY topo_elevation(s) DESC
LIMIT 1;

Computes the topocentric position of the Moon from an Earth-based observer. Uses the ELP2000-82B lunar theory (Chapront-Touze & Chapront, 1983).

moon_observe(obs observer, t timestamptz) → topocentric
ParameterTypeDescription
obsobserverObserver location on Earth
ttimestamptzObservation time

A topocentric with azimuth, elevation, range (km), and range rate (km/s). The Moon’s range is typically 356,500 to 406,700 km.

-- Current Moon position and distance
SELECT topo_azimuth(t) AS az,
topo_elevation(t) AS el,
topo_range(t) AS range_km
FROM moon_observe('40.0N 105.3W 1655m'::observer, now()) AS t;
-- Moon's path across the sky tonight at 5-minute intervals
SELECT t,
round(topo_azimuth(m)::numeric, 1) AS az,
round(topo_elevation(m)::numeric, 1) AS el,
round(topo_range(m)::numeric, 0) AS range_km
FROM generate_series(
'2024-06-15 02:00:00+00',
'2024-06-15 10:00:00+00',
interval '5 minutes'
) AS t,
moon_observe('40.0N 105.3W 1655m'::observer, t) AS m
WHERE topo_elevation(m) > 0;

Computes the geocentric apparent equatorial coordinates (RA/Dec) of a planet at a given time using VSOP87. The heliocentric ecliptic position is converted to geocentric equatorial and precessed to the date of observation via IAU 1976 precession.

planet_equatorial(body_id int4, t timestamptz) → equatorial
ParameterTypeDescription
body_idint4Planet identifier (1-8, same as planet_heliocentric excluding 0 and 3)
ttimestamptzEvaluation time

An equatorial with RA (hours), Dec (degrees), and distance (km) from Earth’s center.

-- Current RA/Dec of all planets
SELECT body_id,
CASE body_id
WHEN 1 THEN 'Mercury' WHEN 2 THEN 'Venus'
WHEN 4 THEN 'Mars' WHEN 5 THEN 'Jupiter'
WHEN 6 THEN 'Saturn' WHEN 7 THEN 'Uranus'
WHEN 8 THEN 'Neptune'
END AS planet,
round(eq_ra(e)::numeric, 4) AS ra_h,
round(eq_dec(e)::numeric, 4) AS dec_deg,
round(eq_distance(e)::numeric, 0) AS dist_km
FROM unnest(ARRAY[1,2,4,5,6,7,8]) AS body_id,
planet_equatorial(body_id, now()) AS e;

Computes the geocentric apparent equatorial coordinates (RA/Dec) of the Sun at a given time using VSOP87.

sun_equatorial(t timestamptz) → equatorial
ParameterTypeDescription
ttimestamptzEvaluation time

An equatorial with RA (hours), Dec (degrees), and distance (km) from Earth’s center.

-- Sun's current RA/Dec
SELECT round(eq_ra(e)::numeric, 4) AS ra_hours,
round(eq_dec(e)::numeric, 4) AS dec_deg,
round(eq_distance(e)::numeric, 0) AS dist_km
FROM sun_equatorial(now()) AS e;

Computes the geocentric apparent equatorial coordinates (RA/Dec) of the Moon at a given time using ELP2000-82B.

moon_equatorial(t timestamptz) → equatorial
ParameterTypeDescription
ttimestamptzEvaluation time

An equatorial with RA (hours), Dec (degrees), and distance (km) from Earth’s center.

-- Moon's current RA/Dec and distance
SELECT round(eq_ra(e)::numeric, 4) AS ra_hours,
round(eq_dec(e)::numeric, 4) AS dec_deg,
round(eq_distance(e)::numeric, 0) AS dist_km
FROM moon_equatorial(now()) AS e;
-- Moon's RA/Dec path over one lunation at daily intervals
SELECT t::date AS date,
round(eq_ra(e)::numeric, 3) AS ra_h,
round(eq_dec(e)::numeric, 3) AS dec_deg
FROM generate_series(
now(), now() + interval '29 days', interval '1 day'
) AS t,
moon_equatorial(t) AS e;

Computes the topocentric position of a planet with single-iteration light-time correction. The planet’s position is evaluated at the retarded time (observation time minus light travel time), while Earth’s position is evaluated at the observation time. Uses VSOP87.

planet_observe_apparent(body_id int4, obs observer, t timestamptz) → topocentric
ParameterTypeDescription
body_idint4Planet identifier (1-8, excluding 0 and 3)
obsobserverObserver location on Earth
ttimestamptzObservation time

A topocentric with azimuth, elevation, range (km), and range rate (km/s). The range reflects the geometric distance at the retarded time.

-- Compare geometric vs light-time corrected Mars observation
SELECT round(topo_azimuth(g)::numeric, 4) AS az_geo,
round(topo_azimuth(a)::numeric, 4) AS az_apparent,
round(topo_elevation(g)::numeric, 4) AS el_geo,
round(topo_elevation(a)::numeric, 4) AS el_apparent
FROM planet_observe(4, '40.0N 105.3W 1655m'::observer, now()) AS g,
planet_observe_apparent(4, '40.0N 105.3W 1655m'::observer, now()) AS a;

Computes the topocentric position of the Sun with light-time correction (approximately 8.3 minutes). Uses VSOP87.

sun_observe_apparent(obs observer, t timestamptz) → topocentric
ParameterTypeDescription
obsobserverObserver location on Earth
ttimestamptzObservation time

A topocentric with azimuth, elevation, range (km), and range rate (km/s).

-- Sun position with light-time correction
SELECT round(topo_azimuth(t)::numeric, 4) AS az,
round(topo_elevation(t)::numeric, 4) AS el
FROM sun_observe_apparent('40.0N 105.3W 1655m'::observer, now()) AS t;

Computes the geocentric apparent equatorial coordinates (RA/Dec) of a planet with light-time correction. The planet is evaluated at the retarded time. Uses VSOP87.

planet_equatorial_apparent(body_id int4, t timestamptz) → equatorial
ParameterTypeDescription
body_idint4Planet identifier (1-8, excluding 0 and 3)
ttimestamptzEvaluation time

An equatorial with RA (hours), Dec (degrees), and distance (km), corrected for light travel time.

-- Light-time corrected RA/Dec of Jupiter
SELECT round(eq_ra(e)::numeric, 4) AS ra_hours,
round(eq_dec(e)::numeric, 4) AS dec_deg,
round(eq_distance(e)::numeric, 0) AS dist_km
FROM planet_equatorial_apparent(5, now()) AS e;

Computes the geocentric apparent equatorial coordinates (RA/Dec) of the Moon with light-time correction (approximately 1.3 seconds). Uses ELP2000-82B.

moon_equatorial_apparent(t timestamptz) → equatorial
ParameterTypeDescription
ttimestamptzEvaluation time

An equatorial with RA (hours), Dec (degrees), and distance (km), corrected for light travel time.

-- Compare geometric vs light-time corrected Moon RA/Dec
SELECT round(eq_ra(g)::numeric, 6) AS ra_geo,
round(eq_ra(a)::numeric, 6) AS ra_apparent,
round(eq_dec(g)::numeric, 6) AS dec_geo,
round(eq_dec(a)::numeric, 6) AS dec_apparent
FROM moon_equatorial(now()) AS g,
moon_equatorial_apparent(now()) AS a;