pg_orrery/docs/public/llms-full.txt
Ryan Malloy be7e28e2b6 Update docs for v0.10.0: benchmarks, Skyfield parity, LLM references
- Add timing numbers for equatorial, aberration, angular distance,
  refraction, and star proper motion+parallax to benchmarks page
- Update From Skyfield page: v0.10.0 now has light-time + aberration
  parity; remaining gap narrowed to nutation (~9 arcsec) and polar motion
- Update llms.txt and llms-full.txt for 114 functions, new DE apparent
  variants, equatorial spatial operators, and aberration/parallax notes
2026-02-21 22:00:52 -07:00

613 lines
25 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# pg_orrery — Complete LLM Reference
> Celestial mechanics types and functions for PostgreSQL. Native C extension (v0.10.0) with 114 SQL functions, 9 custom types + 1 composite, GiST/SP-GiST indexing. All functions PARALLEL SAFE.
- Source: https://git.supported.systems/warehack.ing/pg_orrery
- Docs: https://pg-orrery.warehack.ing
- Requires: PostgreSQL 1418
- Install: `CREATE EXTENSION pg_orrery;`
## Types
All base types are fixed-size, STORAGE = plain, ALIGNMENT = double. No TOAST.
### tle (112 bytes)
Parsed Two-Line Element set for SGP4/SDP4 propagation. Text I/O is the standard two-line format.
```sql
-- Input: standard TLE two-line format (line1 + newline + line2)
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002
2 25544 51.6400 208.5000 0007417 35.0000 325.0000 15.49000000000000'::tle;
-- Or from separate line columns:
SELECT tle_from_lines(line1, line2) FROM raw_tles;
```
Accessors: `tle_epoch(tle) → float8` (Julian date), `tle_norad_id(tle) → int4`, `tle_inclination(tle) → float8` (degrees), `tle_eccentricity(tle) → float8`, `tle_raan(tle) → float8` (degrees), `tle_arg_perigee(tle) → float8` (degrees), `tle_mean_anomaly(tle) → float8` (degrees), `tle_mean_motion(tle) → float8` (rev/day), `tle_bstar(tle) → float8` (1/earth-radii), `tle_period(tle) → float8` (minutes), `tle_age(tle, timestamptz) → float8` (days), `tle_perigee(tle) → float8` (km), `tle_apogee(tle) → float8` (km), `tle_intl_desig(tle) → text` (COSPAR ID).
### eci_position (48 bytes)
Earth-Centered Inertial position and velocity in TEME frame.
```sql
-- Output format: (x, y, z, vx, vy, vz) in km and km/s
-- Example: (4283.007,-2459.213,4717.924,3.837,5.662,-2.969)
```
Accessors: `eci_x`, `eci_y`, `eci_z` (km), `eci_vx`, `eci_vy`, `eci_vz` (km/s), `eci_speed(eci_position) → float8` (km/s), `eci_altitude(eci_position) → float8` (km, approximate geocentric).
### geodetic (24 bytes)
WGS-84 latitude, longitude, altitude.
```sql
-- Output format: (lat_deg, lon_deg, alt_km)
-- Example: (42.3601,-71.0589,408.123)
```
Accessors: `geodetic_lat`, `geodetic_lon` (degrees), `geodetic_alt` (km).
### topocentric (32 bytes)
Observer-relative azimuth, elevation, range, range rate.
```sql
-- Output format: (azimuth_deg, elevation_deg, range_km, range_rate_km_s)
-- Example: (185.234,45.678,1234.56,-2.345)
```
Accessors: `topo_azimuth` (degrees, 0=N 90=E 180=S 270=W), `topo_elevation` (degrees, 0=horizon 90=zenith), `topo_range` (km), `topo_range_rate` (km/s, positive=receding).
### observer (24 bytes)
Ground station location. Flexible text input.
```sql
-- Multiple input formats:
SELECT '40.0N 105.3W 1655m'::observer; -- DMS with cardinal directions
SELECT '40.0 -105.3 1655m'::observer; -- Decimal degrees (negative=W/S)
SELECT '40.0N 105.3W'::observer; -- Altitude defaults to 0m
-- Programmatic construction:
SELECT observer_from_geodetic(40.0, -105.3, 1655.0); -- (lat_deg, lon_deg, alt_m)
```
Accessors: `observer_lat` (degrees, +N), `observer_lon` (degrees, +E), `observer_alt` (meters).
### pass_event (48 bytes)
Satellite pass visibility window with AOS/MAX/LOS.
```sql
-- Output format: (aos_time, max_el_time, los_time, max_el_deg, aos_az_deg, los_az_deg)
```
Accessors: `pass_aos_time`, `pass_max_el_time`, `pass_los_time` (timestamptz), `pass_max_elevation` (degrees), `pass_aos_azimuth`, `pass_los_azimuth` (degrees), `pass_duration(pass_event) → interval`.
### heliocentric (24 bytes)
Ecliptic J2000 position in AU.
```sql
-- Output format: (x_au, y_au, z_au)
-- Example: (0.983271,-0.182724,0.000021)
```
Accessors: `helio_x`, `helio_y`, `helio_z` (AU), `helio_distance(heliocentric) → float8` (AU).
### orbital_elements (72 bytes)
Classical Keplerian elements for comets and asteroids.
```sql
-- Text I/O format: (epoch_jd, q_au, e, inc_deg, omega_deg, Omega_deg, tp_jd, H, G)
-- Example: (2460200.5,1.0123,0.2156,10.587,72.891,80.329,2460180.5,15.2,0.15)
-- From MPC MPCORB.DAT:
SELECT oe_from_mpc('00001 3.52 0.15 K249V 14.81198 ...fixed-width MPC line...');
```
Accessors: `oe_epoch` (JD), `oe_perihelion` (AU), `oe_eccentricity`, `oe_inclination` (degrees), `oe_arg_perihelion` (degrees), `oe_raan` (degrees), `oe_tp` (JD), `oe_h_mag` (NaN if unknown), `oe_g_slope` (NaN if unknown), `oe_semi_major_axis` (AU, NULL if e≥1), `oe_period_years` (NULL if e≥1).
### equatorial (24 bytes)
Apparent equatorial coordinates of date: RA, Dec, distance. Solar system bodies: J2000 precessed via IAU 1976. Satellites: TEME frame (~of-date to ~arcsecond).
```sql
-- Output format: (ra_hours, dec_degrees, distance_km)
-- Example: (4.29220000,20.60000000,885412345.678)
```
Accessors: `eq_ra(equatorial) → float8` (hours [0,24)), `eq_dec(equatorial) → float8` (degrees [-90,90]), `eq_distance(equatorial) → float8` (km; 0 for stars without parallax).
### observer_window (composite)
Query parameter bundle for SP-GiST visibility cone operator.
```sql
-- Constructed inline as a ROW:
SELECT * FROM satellites WHERE elements &? ROW(
'40.0N 105.3W 1655m'::observer,
'2024-01-01'::timestamptz,
'2024-01-02'::timestamptz,
10.0 -- min_elevation_degrees
)::observer_window;
```
Fields: `obs` (observer), `t_start` (timestamptz), `t_end` (timestamptz), `min_el` (float8, degrees).
## Body IDs
### Planets (VSOP87 convention)
| ID | Body | ID | Body |
|----|---------|----|---------|
| 0 | Sun | 5 | Jupiter |
| 1 | Mercury | 6 | Saturn |
| 2 | Venus | 7 | Uranus |
| 3 | Earth | 8 | Neptune |
| 4 | Mars | 10 | Moon |
### Galilean moons (03)
0=Io, 1=Europa, 2=Ganymede, 3=Callisto
### Saturn moons (07)
0=Mimas, 1=Enceladus, 2=Tethys, 3=Dione, 4=Rhea, 5=Titan, 6=Iapetus, 7=Hyperion
### Uranus moons (04)
0=Miranda, 1=Ariel, 2=Umbriel, 3=Titania, 4=Oberon
### Mars moons (01)
0=Phobos, 1=Deimos
## Functions by Domain
### Satellite — SGP4/SDP4 Propagation (25 functions)
```
sgp4_propagate(tle, timestamptz) → eci_position IMMUTABLE
sgp4_propagate_safe(tle, timestamptz) → eci_position IMMUTABLE -- NULL on error
sgp4_propagate_series(tle, start, end, step) → SETOF (t, x,y,z, vx,vy,vz) IMMUTABLE
tle_distance(tle, tle, timestamptz) → float8 IMMUTABLE -- km between two TLEs
eci_to_geodetic(eci_position, timestamptz) → geodetic IMMUTABLE
eci_to_topocentric(eci_position, observer, timestamptz) → topocentric IMMUTABLE
eci_to_equatorial(eci_position, observer, timestamptz) → equatorial IMMUTABLE -- topocentric RA/Dec (parallax-corrected)
eci_to_equatorial_geo(eci_position, timestamptz) → equatorial IMMUTABLE -- geocentric RA/Dec (observer-independent)
subsatellite_point(tle, timestamptz) → geodetic IMMUTABLE
ground_track(tle, start, end, step) → SETOF (t, lat, lon, alt) IMMUTABLE
observe(tle, observer, timestamptz) → topocentric IMMUTABLE -- propagate + observe in one call
observe_safe(tle, observer, timestamptz) → topocentric IMMUTABLE -- NULL on error
next_pass(tle, observer, timestamptz) → pass_event STABLE -- searches up to 7 days
predict_passes(tle, observer, start, end, min_el DEFAULT 0.0) → SETOF pass_event STABLE
predict_passes_refracted(tle, observer, start, end, min_el DEFAULT 0.0) → SETOF pass_event STABLE -- refracted horizon (-0.569°)
pass_visible(tle, observer, start, end) → boolean STABLE
tle_from_lines(text, text) → tle IMMUTABLE
observer_from_geodetic(lat_deg, lon_deg, alt_m DEFAULT 0.0) → observer IMMUTABLE
```
TLE accessors (15): `tle_epoch`, `tle_norad_id`, `tle_inclination`, `tle_eccentricity`, `tle_raan`, `tle_arg_perigee`, `tle_mean_anomaly`, `tle_mean_motion`, `tle_bstar`, `tle_period`, `tle_age`, `tle_perigee`, `tle_apogee`, `tle_intl_desig`, `tle_from_lines`.
### Solar System — VSOP87 + ELP2000-82B (14 functions)
```
planet_heliocentric(body_id int4, timestamptz) → heliocentric IMMUTABLE -- IDs 0-8
planet_observe(body_id int4, observer, timestamptz) → topocentric IMMUTABLE -- IDs 1-8
sun_observe(observer, timestamptz) → topocentric IMMUTABLE
moon_observe(observer, timestamptz) → topocentric IMMUTABLE
-- Equatorial RA/Dec (apparent, of date)
planet_equatorial(body_id int4, timestamptz) → equatorial IMMUTABLE -- geocentric
sun_equatorial(timestamptz) → equatorial IMMUTABLE
moon_equatorial(timestamptz) → equatorial IMMUTABLE
-- Light-time corrected (body at retarded time, Earth at observation time)
planet_observe_apparent(body_id int4, observer, timestamptz) → topocentric IMMUTABLE
sun_observe_apparent(observer, timestamptz) → topocentric IMMUTABLE
planet_equatorial_apparent(body_id int4, timestamptz) → equatorial IMMUTABLE
moon_equatorial_apparent(timestamptz) → equatorial IMMUTABLE
-- All _apparent() functions include annual aberration correction (~20 arcsec) + light-time
```
### Planetary Moons (4 functions)
```
galilean_observe(moon_id int4, observer, timestamptz) → topocentric IMMUTABLE -- L1.2 theory, IDs 0-3
saturn_moon_observe(moon_id int4, observer, timestamptz) → topocentric IMMUTABLE -- TASS 1.7, IDs 0-7
uranus_moon_observe(moon_id int4, observer, timestamptz) → topocentric IMMUTABLE -- GUST86, IDs 0-4
mars_moon_observe(moon_id int4, observer, timestamptz) → topocentric IMMUTABLE -- MarsSat, IDs 0-1
```
### Stars (5 functions)
```
star_observe(ra_hours float8, dec_degrees float8, observer, timestamptz) → topocentric IMMUTABLE
star_observe_safe(ra_hours float8, dec_degrees float8, observer, timestamptz) → topocentric IMMUTABLE -- NULL on error
star_equatorial(ra_hours, dec_degrees, timestamptz) → equatorial IMMUTABLE -- precesses J2000 to date
-- Proper motion (Hipparcos/Gaia convention: pm_ra = mu_alpha * cos(delta) in mas/yr)
star_observe_pm(ra_h, dec_deg, pm_ra_masyr, pm_dec_masyr, parallax_mas, rv_kms, observer, timestamptz) → topocentric IMMUTABLE
star_equatorial_pm(ra_h, dec_deg, pm_ra_masyr, pm_dec_masyr, parallax_mas, rv_kms, timestamptz) → equatorial IMMUTABLE
-- When parallax_mas > 0, annual stellar parallax is applied using Earth's heliocentric position (Green 1985)
```
RA in hours [0,24), Dec in degrees [-90,90]. Range returned as 0 (infinite distance) unless parallax > 0 in _pm variants.
### Comets & Asteroids — Keplerian + MPC (9 functions)
```
kepler_propagate(q_au, eccentricity, inc_deg, arg_peri_deg, raan_deg, perihelion_jd, timestamptz) → heliocentric IMMUTABLE
comet_observe(q_au, e, inc, omega, Omega, tp_jd, earth_x, earth_y, earth_z, observer, timestamptz) → topocentric IMMUTABLE
oe_from_mpc(text) → orbital_elements IMMUTABLE -- parse MPC MPCORB.DAT line
small_body_heliocentric(orbital_elements, timestamptz) → heliocentric IMMUTABLE
small_body_observe(orbital_elements, observer, timestamptz) → topocentric IMMUTABLE -- auto-fetches Earth via VSOP87
small_body_equatorial(orbital_elements, timestamptz) → equatorial IMMUTABLE -- geocentric RA/Dec
small_body_observe_apparent(orbital_elements, observer, timestamptz) → topocentric IMMUTABLE -- light-time corrected
small_body_equatorial_apparent(orbital_elements, timestamptz) → equatorial IMMUTABLE -- light-time corrected RA/Dec
```
orbital_elements accessors (11): `oe_epoch`, `oe_perihelion`, `oe_eccentricity`, `oe_inclination`, `oe_arg_perihelion`, `oe_raan`, `oe_tp`, `oe_h_mag`, `oe_g_slope`, `oe_semi_major_axis`, `oe_period_years`.
### Jupiter Radio (3 functions)
```
io_phase_angle(timestamptz) → float8 IMMUTABLE -- degrees [0,360)
jupiter_cml(observer, timestamptz) → float8 IMMUTABLE -- CML III degrees [0,360)
jupiter_burst_probability(io_phase_deg, cml_deg) → float8 IMMUTABLE -- 0-1 probability
```
### Interplanetary Transfers — Lambert Solver (2 functions)
```
lambert_transfer(dep_body int4, arr_body int4, dep_time, arr_time)
→ (c3_departure, c3_arrival, v_inf_departure, v_inf_arrival, tof_days, transfer_sma) IMMUTABLE
lambert_c3(dep_body int4, arr_body int4, dep_time, arr_time) → float8 IMMUTABLE -- departure C3 only, for pork chop plots
```
Body IDs 18 (MercuryNeptune). C3 in km²/s², v_inf in km/s, TOF in days, SMA in AU.
### Atmospheric Refraction — Bennett 1982 (4 functions)
```
atmospheric_refraction(elevation_deg float8) → float8 IMMUTABLE -- degrees; standard atmosphere P=1010, T=10°C
atmospheric_refraction_ext(elevation_deg, pressure_mbar, temp_celsius) → float8 IMMUTABLE -- with Meeus P/T correction
topo_elevation_apparent(topocentric) → float8 IMMUTABLE -- geometric + refraction, in degrees
predict_passes_refracted(tle, observer, start, end, min_el DEFAULT 0.0) → SETOF pass_event STABLE -- horizon at -0.569° geometric
```
Bennett formula: `R = 1/tan(h + 7.31/(h + 4.4))` arcminutes. Domain guard: clamps at -1°, returns 0.0 below. At horizon (0°) refraction is ~0.57°, meaning satellites become visible ~35 seconds earlier.
### Equatorial Spatial — Angular Separation (2 functions)
```
eq_angular_distance(equatorial, equatorial) → float8 IMMUTABLE -- degrees, Vincenty formula (stable at 0° and 180°)
eq_within_cone(equatorial, equatorial, float8) → bool IMMUTABLE -- true if within radius_deg, cosine shortcut
```
Operator: `equatorial <-> equatorial → float8` (angular separation in degrees, commutative).
### DE Ephemeris — Optional High-Precision (19 functions)
All _de() functions fall back to VSOP87/ELP2000-82B when DE is unavailable. All STABLE (external file dependency).
```
planet_heliocentric_de(body_id int4, timestamptz) → heliocentric STABLE
planet_observe_de(body_id int4, observer, timestamptz) → topocentric STABLE
sun_observe_de(observer, timestamptz) → topocentric STABLE
moon_observe_de(observer, timestamptz) → topocentric STABLE
lambert_transfer_de(dep_body, arr_body, dep_time, arr_time) → RECORD STABLE
lambert_c3_de(dep_body, arr_body, dep_time, arr_time) → float8 STABLE
galilean_observe_de(moon_id, observer, timestamptz) → topocentric STABLE
saturn_moon_observe_de(moon_id, observer, timestamptz) → topocentric STABLE
uranus_moon_observe_de(moon_id, observer, timestamptz) → topocentric STABLE
mars_moon_observe_de(moon_id, observer, timestamptz) → topocentric STABLE
planet_equatorial_de(body_id int4, timestamptz) → equatorial STABLE -- geocentric RA/Dec via DE
moon_equatorial_de(timestamptz) → equatorial STABLE -- geocentric RA/Dec via DE
pg_orrery_ephemeris_info() → (provider, file_path, start_jd, end_jd, version, au_km) STABLE
-- Apparent DE variants (light-time + aberration, falls back to VSOP87)
planet_observe_apparent_de(body_id int4, observer, timestamptz) → topocentric STABLE
sun_observe_apparent_de(observer, timestamptz) → topocentric STABLE
moon_observe_apparent_de(observer, timestamptz) → topocentric STABLE
planet_equatorial_apparent_de(body_id int4, timestamptz) → equatorial STABLE
moon_equatorial_apparent_de(timestamptz) → equatorial STABLE
small_body_observe_apparent_de(orbital_elements, observer, timestamptz) → topocentric STABLE
```
Configure: `ALTER SYSTEM SET pg_orrery.ephemeris_path = '/path/to/de441.bin'; SELECT pg_reload_conf();`
### Orbit Determination (5 functions)
All return: `(fitted_tle, iterations, rms_final, rms_initial, status, condition_number, covariance, nstate)`. All STABLE.
```
tle_from_eci(positions eci_position[], times timestamptz[], seed tle DEFAULT NULL,
fit_bstar bool DEFAULT false, max_iter int4 DEFAULT 15, weights float8[] DEFAULT NULL) → RECORD
tle_from_topocentric(observations topocentric[], times timestamptz[], obs observer,
seed DEFAULT NULL, fit_bstar DEFAULT false, max_iter DEFAULT 15,
fit_range_rate DEFAULT false, weights DEFAULT NULL) → RECORD
tle_from_topocentric(observations topocentric[], times timestamptz[],
observers observer[], observer_ids int4[], -- multi-observer variant
seed DEFAULT NULL, fit_bstar DEFAULT false, max_iter DEFAULT 15,
fit_range_rate DEFAULT false, weights DEFAULT NULL) → RECORD
tle_from_angles(ra_hours float8[], dec_degrees float8[], times timestamptz[], obs observer,
seed DEFAULT NULL, fit_bstar DEFAULT false, max_iter DEFAULT 15, weights DEFAULT NULL) → RECORD
tle_from_angles(ra_hours float8[], dec_degrees float8[], times timestamptz[],
observers observer[], observer_ids int4[], -- multi-observer variant
seed DEFAULT NULL, fit_bstar DEFAULT false, max_iter DEFAULT 15, weights DEFAULT NULL) → RECORD
tle_fit_residuals(fitted tle, positions eci_position[], times timestamptz[])
→ SETOF (t, dx_km, dy_km, dz_km, pos_err_km) IMMUTABLE
```
## Operators & Indexes
### GiST — tle_ops (DEFAULT for type tle)
```sql
CREATE INDEX ON satellites USING gist (elements);
```
| Operator | Meaning | Usage |
|----------|---------|-------|
| `&&` | Orbital key overlap (altitude band AND inclination range) | `WHERE a.elements && b.elements` |
| `<->` | 2-D orbital distance (km) — L2 norm of altitude gap + inclination gap | `ORDER BY elements <-> ref_tle LIMIT 10` |
### SP-GiST — tle_spgist_ops (opt-in)
```sql
CREATE INDEX ON satellites USING spgist (elements tle_spgist_ops);
```
| Operator | Meaning | Usage |
|----------|---------|-------|
| `&?` | Visibility cone check — could satellite be visible from observer? | `WHERE elements &? ROW(obs, t0, t1, 10.0)::observer_window` |
SP-GiST is a 2-level orbital trie (SMA → inclination) with query-time RAAN filter. Returns a conservative superset — survivors need `predict_passes()` for ground truth.
### Equatorial distance
| Operator | Meaning | Usage |
|----------|---------|-------|
| `<->` (equatorial) | Angular separation in degrees (Vincenty formula) | `ORDER BY pos1 <-> pos2` or `WHERE pos1 <-> pos2 < 5.0` |
## Common Query Patterns
### Observe a satellite
```sql
SELECT topo_elevation(observe(elements, '40.0N 105.3W 1655m'::observer, NOW()))
FROM satellites WHERE name = 'ISS';
```
### Batch propagation over a catalog
```sql
SELECT name,
topo_elevation(observe_safe(elements, '40.0N 105.3W'::observer, NOW())) AS el
FROM satellites
WHERE topo_elevation(observe_safe(elements, '40.0N 105.3W'::observer, NOW())) > 10;
```
### Predict passes for one satellite
```sql
SELECT pass_aos_time(p), pass_max_elevation(p), pass_duration(p)
FROM satellites,
LATERAL predict_passes(elements, '40.0N 105.3W 1655m'::observer,
NOW(), NOW() + '3 days'::interval, 10.0) AS p
WHERE name = 'ISS';
```
### SP-GiST accelerated pass prediction
```sql
SELECT s.name, p.*
FROM satellites s,
LATERAL predict_passes(s.elements, '40.0N 105.3W 1655m'::observer,
NOW(), NOW() + '1 day'::interval, 10.0) AS p
WHERE s.elements &? ROW(
'40.0N 105.3W 1655m'::observer, NOW(), NOW() + '1 day'::interval, 10.0
)::observer_window;
```
### Observe a planet
```sql
SELECT topo_azimuth(planet_observe(4, '40.0N 105.3W'::observer, NOW())) AS mars_az,
topo_elevation(planet_observe(4, '40.0N 105.3W'::observer, NOW())) AS mars_el;
```
### Tonight's visible planets
```sql
SELECT body_name, topo_elevation(obs) AS el, topo_azimuth(obs) AS az
FROM (VALUES (1,'Mercury'),(2,'Venus'),(4,'Mars'),(5,'Jupiter'),(6,'Saturn')) AS p(id, body_name),
LATERAL planet_observe(p.id, '40.0N 105.3W'::observer, NOW()) AS obs
WHERE topo_elevation(obs) > 0;
```
### GiST conjunction screening
```sql
SELECT a.name, b.name,
tle_distance(a.elements, b.elements, NOW()) AS dist_km
FROM satellites a, satellites b
WHERE a.id < b.id
AND a.elements && b.elements
AND tle_distance(a.elements, b.elements, NOW()) < 50;
```
### Observe a comet/asteroid from MPC data
```sql
-- From orbital_elements type:
SELECT topo_elevation(small_body_observe(oe, '40.0N 105.3W'::observer, NOW()))
FROM asteroids WHERE name = 'Ceres';
-- Bulk MPC import:
COPY mpc_raw(line) FROM '/path/to/MPCORB.DAT';
INSERT INTO asteroids (name, oe)
SELECT substring(line FROM 1 FOR 7), oe_from_mpc(line) FROM mpc_raw;
```
### Lambert transfer — Earth to Mars
```sql
SELECT * FROM lambert_transfer(3, 4,
'2026-07-01'::timestamptz,
'2027-01-15'::timestamptz);
-- Returns: c3_departure, c3_arrival, v_inf_departure, v_inf_arrival, tof_days, transfer_sma
```
### Pork chop plot grid
```sql
SELECT dep, arr, lambert_c3(3, 4, dep, arr) AS c3
FROM generate_series('2026-01-01'::timestamptz, '2026-12-01', '10 days') AS dep,
generate_series('2026-07-01'::timestamptz, '2027-06-01', '10 days') AS arr;
```
### Jupiter radio burst prediction
```sql
SELECT io_phase_angle(t) AS io_phase,
jupiter_cml('40.0N 105.3W'::observer, t) AS cml,
jupiter_burst_probability(io_phase_angle(t),
jupiter_cml('40.0N 105.3W'::observer, t)) AS prob
FROM generate_series(NOW(), NOW() + '24 hours', '15 minutes') AS t
WHERE jupiter_burst_probability(io_phase_angle(t),
jupiter_cml('40.0N 105.3W'::observer, t)) > 0.3;
```
### Orbit determination from observations
```sql
SELECT (tle_from_eci(
ARRAY[eci1, eci2, eci3, eci4, eci5],
ARRAY[t1, t2, t3, t4, t5]
)).*
-- Returns: fitted_tle, iterations, rms_final, rms_initial, status, condition_number, covariance, nstate
```
### Get RA/Dec for telescope GoTo
```sql
-- Planet RA/Dec (apparent, of date — what telescope mounts expect)
SELECT eq_ra(planet_equatorial(5, NOW())) AS jupiter_ra_hours,
eq_dec(planet_equatorial(5, NOW())) AS jupiter_dec_deg;
-- With light-time correction (Jupiter light-travel ~35-52 min)
SELECT eq_ra(planet_equatorial_apparent(5, NOW())) AS ra_h,
eq_dec(planet_equatorial_apparent(5, NOW())) AS dec_deg;
-- Star with proper motion (Barnard's Star from Hipparcos/Gaia catalog)
SELECT eq_ra(star_equatorial_pm(17.963472, 4.6933, -798.58, 10328.12, 545.4, -110.51, NOW())) AS ra_h,
eq_dec(star_equatorial_pm(17.963472, 4.6933, -798.58, 10328.12, 545.4, -110.51, NOW())) AS dec_deg;
```
### Apparent elevation with atmospheric refraction
```sql
-- Compare geometric vs apparent elevation
SELECT topo_elevation(obs) AS geometric_el,
topo_elevation_apparent(obs) AS apparent_el,
atmospheric_refraction(topo_elevation(obs)) AS refraction
FROM planet_observe(5, '40.0N 105.3W'::observer, NOW()) AS obs;
```
### Refracted satellite passes (extended visibility windows)
```sql
SELECT pass_aos_time(p), pass_max_elevation(p), pass_duration(p)
FROM satellites,
LATERAL predict_passes_refracted(elements, '40.0N 105.3W 1655m'::observer,
NOW(), NOW() + '3 days'::interval, 10.0) AS p
WHERE name = 'ISS';
```
### Angular separation and cone search
```sql
-- Find angular separation between two objects
SELECT planet_equatorial(5, NOW()) <-> moon_equatorial(NOW()) AS jupiter_moon_sep_deg;
-- Objects within 10 degrees of Jupiter
SELECT eq_within_cone(
star_equatorial(ra_h, dec_deg, NOW()),
planet_equatorial(5, NOW()),
10.0
) AS near_jupiter
FROM star_catalog;
```
## Error Handling
### _safe() variants
`sgp4_propagate_safe()`, `observe_safe()`, `star_observe_safe()` return NULL on error instead of raising exceptions. Use for batch queries over potentially invalid data.
### SGP4 error codes (raised by non-_safe functions)
| Code | Meaning |
|------|---------|
| -1 | Nearly parabolic orbit |
| -2 | Negative semi-major axis (decayed) |
| -3 | Orbit within Earth radius (continues with NOTICE) |
| -4 | Orbit within Earth radius (continues with NOTICE) |
| -5 | Negative mean motion |
| -6 | Kepler solver convergence failure |
### Input validation errors
- Lambert: same-body check, arrival before departure, invalid body_id (not 18)
- Stars: RA outside [0,24), Dec outside [-90,90]
- Comets: negative perihelion distance
- Observer: invalid coordinate format
## Key Constants
### WGS-72 (SGP4 propagation only)
```
mu = 398600.8 km³/s²
ae = 6378.135 km
J2 = 0.001082616
ke = 0.0743669161331734132 min⁻¹
```
### WGS-84 (coordinate output only)
```
a = 6378.137 km
f = 1/298.257223563
```
### Astronomical
```
AU = 149597870.7 km (IAU 2012)
Gauss k = 0.01720209895 AU^(3/2)/day
Obliquity J2000 = 23.4392911°
J2000 epoch = JD 2451545.0 (2000 Jan 1.5 TT)
c (light) = 173.1446327 AU/day (for light-time correction)
```
### Critical rule
TLEs are fitted against WGS-72 constants. Propagation MUST use WGS-72. Coordinate output uses WGS-84. Never mix. This is handled internally — all pg_orrery functions use the correct constants automatically.