Skip to content

Functions: Stars, Comets & Asteroids

Functions for computing topocentric positions of stars from catalog coordinates, propagating comets and asteroids on Keplerian orbits, and observing them from Earth. The orbital_elements type (v0.8.0) bundles Keplerian elements into a first-class PostgreSQL datum, with oe_from_mpc() for bulk-loading the MPC catalog and small_body_observe() for ergonomic topocentric observation.


Converts a J2000 equatorial position (right ascension and declination) to topocentric coordinates for an Earth-based observer. Applies sidereal time rotation and horizon transformation. Stars are treated as being at infinite distance, so topo_range is always 0.

star_observe(ra_hours float8, dec_deg float8, obs observer, t timestamptz) → topocentric
ParameterTypeUnitDescription
ra_hoursfloat8hoursRight Ascension in J2000 equatorial frame (0-24)
dec_degfloat8degreesDeclination in J2000 equatorial frame (-90 to +90)
obsobserverObserver location on Earth
ttimestamptzObservation time

A topocentric with azimuth and elevation in degrees. topo_range is 0 (infinite distance). topo_range_rate is 0.

-- Where is Sirius (RA 6h 45m 8.9s, Dec -16d 42m 58s)?
SELECT topo_azimuth(t) AS az,
topo_elevation(t) AS el
FROM star_observe(6.7525, -16.7161, '40.0N 105.3W 1655m'::observer, now()) AS t;
-- Which bright stars are above the horizon right now?
-- (Assumes a star_catalog table with ra_hours, dec_deg, magnitude columns)
SELECT name, magnitude,
round(topo_azimuth(t)::numeric, 1) AS az,
round(topo_elevation(t)::numeric, 1) AS el
FROM star_catalog,
star_observe(ra_hours, dec_deg, '40.0N 105.3W 1655m'::observer, now()) AS t
WHERE magnitude < 2.0
AND topo_elevation(t) > 0
ORDER BY magnitude;

Identical to star_observe, but returns NULL instead of raising an exception on invalid inputs (e.g., RA outside 0-24, Dec outside -90 to +90).

star_observe_safe(ra_hours float8, dec_deg float8, obs observer, t timestamptz) → topocentric
ParameterTypeUnitDescription
ra_hoursfloat8hoursRight Ascension (0-24)
dec_degfloat8degreesDeclination (-90 to +90)
obsobserverObserver location
ttimestamptzObservation time

A topocentric, or NULL if the input coordinates are invalid.

-- Batch-process a catalog, skipping any rows with bad coordinates
SELECT catalog_id, name,
topo_azimuth(t) AS az,
topo_elevation(t) AS el
FROM star_catalog,
star_observe_safe(ra_hours, dec_deg, '40.0N 105.3W 1655m'::observer, now()) AS t
WHERE t IS NOT NULL
AND topo_elevation(t) > 10;

Propagates an object on a Keplerian orbit (two-body problem) to a given time. Returns the heliocentric ecliptic J2000 position in AU. Handles elliptic (e < 1), parabolic (e = 1), and hyperbolic (e > 1) orbits.

kepler_propagate(
q_au float8,
ecc float8,
inc_deg float8,
arg_peri_deg float8,
long_node_deg float8,
perihelion_jd float8,
t timestamptz
) → heliocentric
ParameterTypeUnitDescription
q_aufloat8AUPerihelion distance
eccfloat8Eccentricity. 0 < e < 1 for elliptic, e = 1 for parabolic, e > 1 for hyperbolic
inc_degfloat8degreesOrbital inclination
arg_peri_degfloat8degreesArgument of perihelion
long_node_degfloat8degreesLongitude of ascending node
perihelion_jdfloat8JDTime of perihelion passage as Julian Date
ttimestamptzEvaluation time

A heliocentric position in AU (ecliptic J2000 frame).

-- Propagate Comet Halley (1P/Halley)
-- q=0.586 AU, e=0.967, i=162.3, w=111.3, node=58.4, T=2446467.4 (1986 Feb 9)
SELECT helio_x(h) AS x_au,
helio_y(h) AS y_au,
helio_z(h) AS z_au,
helio_distance(h) AS r_au
FROM kepler_propagate(
0.586, -- perihelion distance
0.967, -- eccentricity
162.3, -- inclination
111.3, -- argument of perihelion
58.4, -- longitude of ascending node
2446467.4, -- perihelion Julian Date
now()
) AS h;
-- Track a near-parabolic comet over 6 months
SELECT t,
helio_distance(h) AS r_au
FROM generate_series(
'2024-01-01'::timestamptz,
'2024-07-01'::timestamptz,
interval '1 day'
) AS t,
kepler_propagate(1.01, 0.9995, 45.0, 130.0, 210.0, 2460400.5, t) AS h;

Computes the topocentric position of a comet or asteroid as seen from an Earth-based observer. This function combines Keplerian propagation with the Earth’s heliocentric position to produce observer-relative coordinates.

comet_observe(
q_au float8,
ecc float8,
inc_deg float8,
arg_peri_deg float8,
long_node_deg float8,
perihelion_jd float8,
earth_x float8,
earth_y float8,
earth_z float8,
obs observer,
t timestamptz
) → topocentric
ParameterTypeUnitDescription
q_aufloat8AUPerihelion distance
eccfloat8Eccentricity
inc_degfloat8degreesOrbital inclination
arg_peri_degfloat8degreesArgument of perihelion
long_node_degfloat8degreesLongitude of ascending node
perihelion_jdfloat8JDTime of perihelion passage as Julian Date
earth_xfloat8AUEarth’s heliocentric X (ecliptic J2000)
earth_yfloat8AUEarth’s heliocentric Y (ecliptic J2000)
earth_zfloat8AUEarth’s heliocentric Z (ecliptic J2000)
obsobserverObserver location on Earth
ttimestamptzObservation time

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

-- Observe Comet Halley from Boulder
WITH earth AS (
SELECT planet_heliocentric(3, now()) AS h
)
SELECT topo_azimuth(c) AS az,
topo_elevation(c) AS el,
topo_range(c) / 149597870.7 AS dist_au
FROM earth,
comet_observe(
0.586, 0.967, 162.3, 111.3, 58.4, 2446467.4,
helio_x(earth.h), helio_y(earth.h), helio_z(earth.h),
'40.0N 105.3W 1655m'::observer,
now()
) AS c;
-- Batch-observe all comets from a catalog
WITH earth AS (
SELECT planet_heliocentric(3, now()) AS h
)
SELECT name,
round(topo_azimuth(c)::numeric, 2) AS az,
round(topo_elevation(c)::numeric, 2) AS el,
round((topo_range(c) / 149597870.7)::numeric, 4) AS dist_au
FROM comet_catalog, earth,
comet_observe(
q_au, ecc, inc_deg, arg_peri_deg, long_node_deg, perihelion_jd,
helio_x(earth.h), helio_y(earth.h), helio_z(earth.h),
'40.0N 105.3W 1655m'::observer,
now()
) AS c
WHERE topo_elevation(c) > 0
ORDER BY topo_range(c);

Parses one line of the MPC MPCORB.DAT fixed-width format into an orbital_elements type. The MPC publishes orbital elements for over 1.3 million numbered asteroids in this format.

oe_from_mpc(line text) → orbital_elements
ParameterTypeDescription
linetextOne complete line from MPCORB.DAT (at least 103 characters)

An orbital_elements with all nine fields populated. The parser performs several conversions at parse time:

  • Packed epoch (e.g. K24AM) is decoded to a Julian date. The century letter (I=1800, J=1900, K=2000), two-digit year, packed month (1-9, A-C), and packed day (1-9, A-V) are expanded to a calendar date.
  • Perihelion distance is derived from the MPC’s semi-major axis and eccentricity: q = a × (1 − e).
  • Perihelion time is computed from the epoch and mean anomaly via Gauss’s constant: tp = epoch − M / n, where n = k / a^(3/2).
-- Parse Ceres, extract semi-major axis and period
WITH ceres AS (
SELECT oe_from_mpc(
'00001 3.33 0.12 K24AM 60.07966 73.42937 80.26860 10.58664 0.0789126 0.21406048 2.7660961 0 MPO838504 8738 115 1801-2024 0.65 M-v 30k MPCLINUX 0000 (1) Ceres 20240825'
) AS oe
)
SELECT round(oe_semi_major_axis(oe)::numeric, 4) AS a_au,
round(oe_period_years(oe)::numeric, 2) AS period_yr,
round(oe_inclination(oe)::numeric, 3) AS inc_deg,
round(oe_h_mag(oe)::numeric, 2) AS h_mag
FROM ceres;

Propagates an orbital_elements to a heliocentric ecliptic J2000 position at a given time using two-body Keplerian mechanics. Extracts q, e, inc, omega, Omega, and tp from the type and calls the internal Kepler solver.

small_body_heliocentric(oe orbital_elements, t timestamptz) → heliocentric
ParameterTypeDescription
oeorbital_elementsBundled orbital elements
ttimestamptzEvaluation time

A heliocentric position in AU (ecliptic J2000 frame). Identical to calling kepler_propagate() with the individual fields extracted from the type.

-- Propagate Ceres to 2025-01-01, check heliocentric distance
WITH ceres AS (
SELECT oe_from_mpc(
'00001 3.33 0.12 K24AM 60.07966 73.42937 80.26860 10.58664 0.0789126 0.21406048 2.7660961 0 MPO838504 8738 115 1801-2024 0.65 M-v 30k MPCLINUX 0000 (1) Ceres 20240825'
) AS oe
)
SELECT round(helio_x(h)::numeric, 4) AS x_au,
round(helio_y(h)::numeric, 4) AS y_au,
round(helio_z(h)::numeric, 4) AS z_au,
round(helio_distance(h)::numeric, 3) AS dist_au
FROM ceres,
small_body_heliocentric(oe, '2025-01-01 00:00:00+00') AS h;

Computes the topocentric position of a comet or asteroid from its orbital_elements as seen by an Earth-based observer. Auto-fetches Earth’s heliocentric position via VSOP87, matching the ergonomics of planet_observe().

small_body_observe(oe orbital_elements, obs observer, t timestamptz) → topocentric
ParameterTypeDescription
oeorbital_elementsBundled orbital elements
obsobserverObserver location on Earth
ttimestamptzObservation time

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

-- Observe Ceres from Boulder
WITH ceres AS (
SELECT oe_from_mpc(
'00001 3.33 0.12 K24AM 60.07966 73.42937 80.26860 10.58664 0.0789126 0.21406048 2.7660961 0 MPO838504 8738 115 1801-2024 0.65 M-v 30k MPCLINUX 0000 (1) Ceres 20240825'
) AS oe
)
SELECT round(topo_azimuth(t)::numeric, 1) AS az,
round(topo_elevation(t)::numeric, 1) AS el,
round((topo_range(t) / 149597870.7)::numeric, 3) AS dist_au
FROM ceres,
small_body_observe(oe, '40.0N 105.3W 1655m'::observer, now()) AS t;
-- Which asteroids in the catalog are above 20 degrees tonight?
SELECT name,
round(topo_elevation(t)::numeric, 1) AS el,
round(topo_azimuth(t)::numeric, 1) AS az
FROM asteroid_catalog,
small_body_observe(oe, '40.0N 105.3W 1655m'::observer, now()) AS t
WHERE topo_elevation(t) > 20
ORDER BY topo_elevation(t) DESC;

Computes the apparent equatorial coordinates (RA/Dec) of a star at a given time by precessing J2000 catalog coordinates to the date of observation via IAU 1976 precession. Does not account for proper motion.

star_equatorial(ra_hours float8, dec_deg float8, t timestamptz) → equatorial
ParameterTypeUnitDescription
ra_hoursfloat8hoursRight Ascension in J2000 (0-24)
dec_degfloat8degreesDeclination in J2000 (-90 to +90)
ttimestamptzEvaluation time

An equatorial with RA (hours) and Dec (degrees) precessed to the date of observation. Distance is 0 (infinite).

-- Precessed RA/Dec of Sirius at current date
SELECT round(eq_ra(e)::numeric, 6) AS ra_hours,
round(eq_dec(e)::numeric, 6) AS dec_deg
FROM star_equatorial(6.7525, -16.7161, now()) AS e;

Computes the topocentric position of a star with full proper motion, parallax, and radial velocity corrections. The proper motion convention follows Hipparcos/Gaia: pm_ra is mu_alpha * cos(delta) in milliarcseconds per year. Cos(dec) is clamped near the poles to avoid division by zero.

star_observe_pm(
ra_hours float8,
dec_deg float8,
pm_ra_masyr float8,
pm_dec_masyr float8,
parallax_mas float8,
rv_kms float8,
obs observer,
t timestamptz
) → topocentric
ParameterTypeUnitDescription
ra_hoursfloat8hoursJ2000 Right Ascension (0-24)
dec_degfloat8degreesJ2000 Declination (-90 to +90)
pm_ra_masyrfloat8mas/yrProper motion in RA (mu_alpha * cos(delta), Hipparcos/Gaia convention)
pm_dec_masyrfloat8mas/yrProper motion in Declination
parallax_masfloat8masTrigonometric parallax (0 if unknown)
rv_kmsfloat8km/sRadial velocity (0 if unknown)
obsobserverObserver location
ttimestamptzObservation time

A topocentric with azimuth, elevation, range (km from parallax, or 0 if parallax is 0), and range rate (km/s).

-- Observe Barnard's Star (large proper motion: 10.3 arcsec/yr total)
-- Hipparcos: RA 17h 57m 48.5s, Dec +4d 41m 36.2s
-- pm_ra = -798.58 mas/yr, pm_dec = 10328.12 mas/yr
-- parallax = 548.31 mas, rv = -110.6 km/s
SELECT round(topo_azimuth(t)::numeric, 2) AS az,
round(topo_elevation(t)::numeric, 2) AS el,
round(topo_range(t)::numeric, 0) AS dist_km
FROM star_observe_pm(
17.9635, 4.6934, -- J2000 RA/Dec
-798.58, 10328.12, -- proper motion (mas/yr)
548.31, -110.6, -- parallax (mas), RV (km/s)
'40.0N 105.3W 1655m'::observer, now()
) AS t;

Computes the apparent equatorial coordinates of a star with proper motion, parallax, and radial velocity corrections. Returns precessed coordinates of date. Distance is derived from parallax if greater than zero.

star_equatorial_pm(
ra_hours float8,
dec_deg float8,
pm_ra_masyr float8,
pm_dec_masyr float8,
parallax_mas float8,
rv_kms float8,
t timestamptz
) → equatorial
ParameterTypeUnitDescription
ra_hoursfloat8hoursJ2000 Right Ascension (0-24)
dec_degfloat8degreesJ2000 Declination (-90 to +90)
pm_ra_masyrfloat8mas/yrProper motion in RA (mu_alpha * cos(delta))
pm_dec_masyrfloat8mas/yrProper motion in Declination
parallax_masfloat8masTrigonometric parallax (0 if unknown)
rv_kmsfloat8km/sRadial velocity (0 if unknown)
ttimestamptzEvaluation time

An equatorial with RA (hours) and Dec (degrees) corrected for proper motion and precessed to date. Distance in km from parallax (0 if parallax is 0 or unknown).

-- RA/Dec of Barnard's Star, corrected for proper motion
SELECT round(eq_ra(e)::numeric, 6) AS ra_hours,
round(eq_dec(e)::numeric, 6) AS dec_deg,
round(eq_distance(e)::numeric, 0) AS dist_km
FROM star_equatorial_pm(
17.9635, 4.6934,
-798.58, 10328.12,
548.31, -110.6,
now()
) AS e;
-- Track the motion of Barnard's Star over 50 years
SELECT extract(year from t) AS year,
round(eq_ra(e)::numeric, 6) AS ra_h,
round(eq_dec(e)::numeric, 6) AS dec_deg
FROM generate_series(
'2000-01-01'::timestamptz,
'2050-01-01'::timestamptz,
interval '10 years'
) AS t,
star_equatorial_pm(17.9635, 4.6934, -798.58, 10328.12, 548.31, -110.6, t) AS e;

Computes the geocentric apparent equatorial coordinates (RA/Dec) of a comet or asteroid from its orbital_elements. The body is propagated on a Keplerian orbit, and Earth’s position is obtained from VSOP87.

small_body_equatorial(oe orbital_elements, t timestamptz) → equatorial
ParameterTypeDescription
oeorbital_elementsBundled orbital elements
ttimestamptzEvaluation time

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

-- RA/Dec of Ceres
WITH ceres AS (
SELECT oe_from_mpc(
'00001 3.33 0.12 K24AM 60.07966 73.42937 80.26860 10.58664 0.0789126 0.21406048 2.7660961 0 MPO838504 8738 115 1801-2024 0.65 M-v 30k MPCLINUX 0000 (1) Ceres 20240825'
) AS oe
)
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 ceres,
small_body_equatorial(oe, now()) AS e;

Computes the topocentric position of a comet or asteroid with single-iteration light-time correction. The body is propagated at the retarded time (observation time minus light travel time), while Earth’s position is evaluated at the observation time via VSOP87.

small_body_observe_apparent(oe orbital_elements, obs observer, t timestamptz) → topocentric
ParameterTypeDescription
oeorbital_elementsBundled orbital elements
obsobserverObserver location on Earth
ttimestamptzObservation time

A topocentric with azimuth, elevation, range (km), and range rate (km/s), corrected for light travel time.

-- Observe Ceres with light-time correction from Boulder
WITH ceres AS (
SELECT oe_from_mpc(
'00001 3.33 0.12 K24AM 60.07966 73.42937 80.26860 10.58664 0.0789126 0.21406048 2.7660961 0 MPO838504 8738 115 1801-2024 0.65 M-v 30k MPCLINUX 0000 (1) Ceres 20240825'
) AS oe
)
SELECT round(topo_azimuth(t)::numeric, 2) AS az,
round(topo_elevation(t)::numeric, 2) AS el,
round((topo_range(t) / 149597870.7)::numeric, 4) AS dist_au
FROM ceres,
small_body_observe_apparent(oe, '40.0N 105.3W 1655m'::observer, now()) AS t;

Computes the geocentric apparent equatorial coordinates (RA/Dec) of a comet or asteroid with light-time correction. The body is propagated at the retarded time.

small_body_equatorial_apparent(oe orbital_elements, t timestamptz) → equatorial
ParameterTypeDescription
oeorbital_elementsBundled orbital elements
ttimestamptzEvaluation time

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

-- Light-time corrected RA/Dec of Ceres
WITH ceres AS (
SELECT oe_from_mpc(
'00001 3.33 0.12 K24AM 60.07966 73.42937 80.26860 10.58664 0.0789126 0.21406048 2.7660961 0 MPO838504 8738 115 1801-2024 0.65 M-v 30k MPCLINUX 0000 (1) Ceres 20240825'
) AS oe
)
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 ceres,
small_body_equatorial_apparent(oe, now()) AS e;