diff --git a/CLAUDE.md b/CLAUDE.md index a44548d..fbc6d5d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,9 +1,9 @@ # pg_orrery — A Database Orrery for PostgreSQL ## What This Is -A database orrery — celestial mechanics types and functions for PostgreSQL. Native C extension using PGXS, 68 SQL functions, 7 custom types, covering satellites (SGP4/SDP4), planets (VSOP87 + optional JPL DE441), Moon (ELP2000-82B), 19 planetary moons (L1.2/TASS17/GUST86/MarsSat), stars, comets, Jupiter radio bursts, and interplanetary Lambert transfers. +A database orrery — celestial mechanics types and functions for PostgreSQL. Native C extension using PGXS, 82 SQL functions, 8 custom types, covering satellites (SGP4/SDP4), planets (VSOP87 + optional JPL DE441), Moon (ELP2000-82B), 19 planetary moons (L1.2/TASS17/GUST86/MarsSat), stars, comets, asteroids (MPC catalog), Jupiter radio bursts, and interplanetary Lambert transfers. -**Current version:** 0.3.0 on branch `phase/solar-system-expansion` +**Current version:** 0.8.0 on branch `phase/spgist-orbital-trie` **Repository:** https://git.supported.systems/warehack.ing/pg_orrery **Documentation:** https://pg-orrery.warehack.ing @@ -11,7 +11,7 @@ A database orrery — celestial mechanics types and functions for PostgreSQL. Na ```bash make PG_CONFIG=/usr/bin/pg_config # Compile with PGXS sudo make install PG_CONFIG=/usr/bin/pg_config # Install extension -make installcheck PG_CONFIG=/usr/bin/pg_config # Run 13 regression test suites +make installcheck PG_CONFIG=/usr/bin/pg_config # Run 16 regression test suites ``` Requires: PostgreSQL 17 development headers, GCC, Make. @@ -27,14 +27,24 @@ Image: `git.supported.systems/warehack.ing/pg_orrery:pg17` ## Project Layout ``` -pg_orrery.control # Extension metadata (version 0.3.0) +pg_orrery.control # Extension metadata (version 0.8.0) Makefile # PGXS build + Docker targets sql/ pg_orrery--0.1.0.sql # v0.1.0: satellite types/functions/operators pg_orrery--0.2.0.sql # v0.2.0: solar system (57 functions) - pg_orrery--0.3.0.sql # v0.3.0: complete extension (68 functions) + pg_orrery--0.3.0.sql # v0.3.0: DE ephemeris (68 functions) + pg_orrery--0.4.0.sql # v0.4.0: orbit determination + pg_orrery--0.5.0.sql # v0.5.0: SP-GiST orbital trie + pg_orrery--0.6.0.sql # v0.6.0: conjunction screening + pg_orrery--0.7.0.sql # v0.7.0: GiST improvements + pg_orrery--0.8.0.sql # v0.8.0: orbital_elements type + MPC parser (82 functions) pg_orrery--0.1.0--0.2.0.sql # Migration: v0.1.0 → v0.2.0 (adds solar system) pg_orrery--0.2.0--0.3.0.sql # Migration: v0.2.0 → v0.3.0 (adds DE ephemeris) + pg_orrery--0.3.0--0.4.0.sql # Migration: v0.3.0 → v0.4.0 + pg_orrery--0.4.0--0.5.0.sql # Migration: v0.4.0 → v0.5.0 + pg_orrery--0.5.0--0.6.0.sql # Migration: v0.5.0 → v0.6.0 + pg_orrery--0.6.0--0.7.0.sql # Migration: v0.6.0 → v0.7.0 + pg_orrery--0.7.0--0.8.0.sql # Migration: v0.7.0 → v0.8.0 (orbital_elements type) src/ pg_orrery.c # PG_MODULE_MAGIC + _PG_init() (GUC registration) types.h # All struct definitions + constants + DE body ID mapping @@ -56,6 +66,8 @@ src/ planet_funcs.c # planet_observe(), planet_heliocentric(), sun/moon_observe() star_funcs.c # star_observe(), star_observe_safe() kepler_funcs.c # kepler_propagate(), comet_observe() + kepler.h # Shared Kepler solver interface (kepler_position()) + orbital_elements_type.c # orbital_elements type, MPC parser, small_body_observe() l12.c / l12.h # L1.2 Galilean moon theory (Lieske 1998) tass17.c / tass17.h # TASS 1.7 Saturn moon theory (Vienne & Duriez 1995) gust86.c / gust86.h # GUST86 Uranus moon theory (Laskar & Jacobson 1987) @@ -80,7 +92,7 @@ src/ PROVENANCE.md # Vendoring decision, modifications, verification LICENSE # MIT license (Bill Gray / Project Pluto) test/ - sql/ # 13 regression test suites + sql/ # 16 regression test suites expected/ # Expected output data/vallado_518.json # 518 Vallado test vectors (AIAA 2006-6753-Rev1) docs/ @@ -104,8 +116,9 @@ All types are fixed-size, `STORAGE = plain`, `ALIGNMENT = double`. No TOAST over | `observer` | 24 | lat, lon (radians), alt_m (meters) | | `pass_event` | 48 | AOS/MAX/LOS times + max_el + AOS/LOS azimuth | | `heliocentric` | 24 | x, y, z in AU (ecliptic J2000 frame) | +| `orbital_elements` | 72 | Classical Keplerian elements for comets/asteroids (epoch, q, e, inc, omega, Omega, tp, H, G) | -## Function Domains (68 total) +## Function Domains (82 total) | Domain | Theory | Key Functions | Count | |--------|--------|---------------|-------| @@ -114,7 +127,7 @@ All types are fixed-size, `STORAGE = plain`, `ALIGNMENT = double`. No TOAST over | Sun/Moon | VSOP87 + ELP2000-82B | `sun_observe()`, `moon_observe()` | 2 | | Planetary moons | L1.2, TASS17, GUST86, MarsSat | `galilean_observe()`, `saturn_moon_observe()` | 4 | | Stars | J2000 + IAU 1976 precession | `star_observe()`, `star_observe_safe()` | 2 | -| Comets/asteroids | Two-body Keplerian | `kepler_propagate()`, `comet_observe()` | 2 | +| Comets/asteroids | Two-body Keplerian + MPC | `small_body_observe()`, `oe_from_mpc()`, `kepler_propagate()` | 16 | | Jupiter radio | Carr et al. (1983) | `jupiter_burst_probability()` | 3 | | Transfers | Lambert (Izzo 2015) | `lambert_transfer()`, `lambert_c3()` | 2 | | DE ephemeris | JPL DE440/441 (optional) | `planet_observe_de()`, `moon_observe_de()` | 11 | @@ -239,7 +252,7 @@ All numerical logic is byte-identical to upstream. Verified against 518 Vallado ## Testing -13 regression test suites via `make installcheck`: +16 regression test suites via `make installcheck`: | Suite | What it tests | |-------|--------------| @@ -256,10 +269,13 @@ All numerical logic is byte-identical to upstream. Verified against 518 Vallado | lambert_transfer | Lambert solver, lambert_c3, pork chop grid, error handling | | de_ephemeris | DE function fallback to VSOP87, cross-provider consistency, error handling | | vallado_518 | 518 Vallado test vectors (AIAA 2006-6753-Rev1), per-satellite breakdown | +| od_fit | Orbit determination from ECI/topocentric/angles-only observations | +| orbital_elements | orbital_elements type I/O, MPC parser, small_body_observe/heliocentric | +| spgist_tle | SP-GiST orbital trie index operations | ### PG Version Matrix -Test all 13 regression suites + DE reader unit test across PostgreSQL 14-18 using Docker: +Test all 16 regression suites + DE reader unit test across PostgreSQL 14-18 using Docker: ```bash make test-matrix # Full matrix (PG 14-18) @@ -283,9 +299,9 @@ Logs saved to `test/matrix-logs/pg${ver}.log`. The script reuses the Dockerfile **Live:** https://pg-orrery.warehack.ing -Starlight docs at `docs/` — 36 MDX pages covering all domains. +Starlight docs at `docs/` — 42 MDX pages covering all domains. -Sections: Getting Started, Guides (9 domain walkthroughs incl. DE ephemeris), Workflow Translation (Skyfield/Horizons/GMAT/Radio Jupiter Pro comparisons), Reference (all 68 functions incl. DE variants), Architecture (Hamilton's principles, constant custody, observation pipeline), Performance (benchmarks). +Sections: Getting Started, Guides (9 domain walkthroughs incl. DE ephemeris), Workflow Translation (Skyfield/Horizons/GMAT/Radio Jupiter Pro comparisons), Reference (all 82 functions incl. DE variants + orbital_elements), Architecture (Hamilton's principles, constant custody, observation pipeline), Performance (benchmarks). ### Local Development ```bash @@ -301,7 +317,7 @@ The docs site deploys to the `warehack.ing` VPS (`149.28.126.25`) which runs cad ```bash ssh -A warehack-ing@pg-orrery.warehack.ing cd ~/pg_orrery -git pull origin phase/solar-system-expansion # or the current branch +git pull origin phase/spgist-orbital-trie # or the current branch cd docs make prod # builds image + starts container ``` @@ -310,7 +326,7 @@ make prod # builds image + starts containe ```bash ssh -A warehack-ing@pg-orrery.warehack.ing git clone git@git.supported.systems:warehack.ing/pg_orrery.git -cd pg_orrery && git checkout phase/solar-system-expansion +cd pg_orrery && git checkout phase/spgist-orbital-trie cat > docs/.env << 'EOF' COMPOSE_PROJECT_NAME=pg-orrery-docs NODE_ENV=production @@ -343,6 +359,6 @@ cd docs && make prod ## Git Conventions - One commit per logical change -- Branch per phase: `phase/solar-system-expansion` -- Tag releases: `v0.1.0`, `v0.2.0` +- Branch per phase: `phase/spgist-orbital-trie` +- Tag releases: `v0.1.0`, `v0.2.0`, `v0.3.0` - Commit messages: imperative mood, no AI attribution diff --git a/docs/src/content/docs/getting-started/what-is-pg-orrery.mdx b/docs/src/content/docs/getting-started/what-is-pg-orrery.mdx index e920f02..8f8f5fe 100644 --- a/docs/src/content/docs/getting-started/what-is-pg-orrery.mdx +++ b/docs/src/content/docs/getting-started/what-is-pg-orrery.mdx @@ -22,7 +22,7 @@ PostGIS added spatial awareness to PostgreSQL — suddenly your database underst | Moon | ELP2000-82B (Chapront, 1988) | `moon_observe()` | ~10 arcseconds | | Planetary moons | L1.2, TASS17, GUST86, MarsSat | `galilean_observe()`, etc. | ~1-10 arcseconds | | Stars | J2000 catalog + precession | `star_observe()` | Limited by catalog | -| Comets/asteroids | Two-body Keplerian | `kepler_propagate()`, `comet_observe()` | Varies with eccentricity | +| Comets/asteroids | Two-body Keplerian | `small_body_observe()`, `oe_from_mpc()`, `kepler_propagate()` | Varies with eccentricity | | Jupiter radio | Carr et al. (1983) sources | `jupiter_burst_probability()` | Empirical probability | | Transfers | Lambert (Izzo, 2015) | `lambert_transfer()`, `lambert_c3()` | Ballistic two-body | | DE ephemeris (optional) | JPL DE440/441 | `planet_observe_de()`, `moon_observe_de()` | ~0.1 milliarcsecond | diff --git a/docs/src/content/docs/guides/comets-asteroids.mdx b/docs/src/content/docs/guides/comets-asteroids.mdx index 045132f..72eb217 100644 --- a/docs/src/content/docs/guides/comets-asteroids.mdx +++ b/docs/src/content/docs/guides/comets-asteroids.mdx @@ -21,17 +21,22 @@ The pattern is familiar: download elements, propagate in Python or C, transform ## What changes with pg_orrery -Two functions handle comet/asteroid computation: +Five functions handle comet/asteroid computation: | Function | What it does | |---|---| | `kepler_propagate(q, e, i, omega, Omega, T, time)` | Propagates orbital elements to a heliocentric position (AU) | | `comet_observe(q, e, i, omega, Omega, T, ex, ey, ez, observer, time)` | Full observation pipeline: propagate + geocentric transform + topocentric | +| `oe_from_mpc(line)` | Parses one MPCORB.DAT line into an `orbital_elements` type | +| `small_body_heliocentric(oe, time)` | Heliocentric position from bundled elements | +| `small_body_observe(oe, observer, time)` | Topocentric observation — auto-fetches Earth via VSOP87 | `kepler_propagate()` solves Kepler's equation for elliptic (e < 1), parabolic (e = 1), and hyperbolic (e > 1) orbits. The solver handles all three cases with appropriate numerical methods. `comet_observe()` wraps the full chain: propagate the comet's position, subtract Earth's heliocentric position, and transform to horizon coordinates. You supply Earth's position as three floats (ecliptic J2000, AU) because you might want to compute it once and reuse it across many comets. +`small_body_observe()` (v0.8.0) does the same thing but fetches Earth's position automatically — you just pass the `orbital_elements` type and an observer. See the [orbital_elements type section](#the-orbital_elements-type) below. + The parameters map directly to MPC orbital element format: | Parameter | MPC field | Units | @@ -56,6 +61,107 @@ Keplerian propagation assumes the body is influenced only by the Sun. Real small For MPC elements less than a few months old, two-body propagation is typically accurate to a few arcminutes for asteroids and tens of arcminutes for comets. Fresh elements give better results. +## The `orbital_elements` type + +The raw-parameter functions (`kepler_propagate`, `comet_observe`) work well when you have elements in variables or a table with individual columns. But they require passing 6–11 float8 arguments per call, and `comet_observe` requires you to manually fetch Earth's position. + +The `orbital_elements` type (v0.8.0) bundles all nine classical elements into a single 72-byte PostgreSQL datum: + +```sql +-- Construct from a literal +SELECT '(2460605.5,2.5478,0.0789126,10.58664,73.42937,80.2686,2460319.0,3.33,0.12)'::orbital_elements; + +-- Or parse directly from the MPC catalog +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' +); +``` + +With bundled elements, observation becomes a single function call: + +```sql +-- Before (comet_observe): 11 arguments, manual Earth fetch +WITH earth AS (SELECT planet_heliocentric(3, now()) AS h) +SELECT topo_elevation(comet_observe( + 2.5478, 0.0789, 10.59, 73.43, 80.27, 2460319.0, + helio_x(h), helio_y(h), helio_z(h), + '40.0N 105.3W 1655m'::observer, now())) +FROM earth; + +-- After (small_body_observe): 3 arguments, Earth auto-fetched +SELECT topo_elevation(small_body_observe( + oe, '40.0N 105.3W 1655m'::observer, now())) +FROM asteroid_catalog; +``` + +### Load and query the MPC catalog + +The MPC publishes MPCORB.DAT — orbital elements for every numbered asteroid. Here's how to load it into PostgreSQL: + + +1. **Create a table with an `orbital_elements` column:** + + ```sql + CREATE TABLE asteroids ( + number int PRIMARY KEY, + name text, + oe orbital_elements NOT NULL + ); + ``` + +2. **Load via a staging table:** + + ```sql + -- Stage the raw text lines + CREATE TEMP TABLE mpc_raw (line text); + \copy mpc_raw FROM 'MPCORB.DAT' + + -- Parse into orbital_elements, extract number and name + INSERT INTO asteroids (number, name, oe) + SELECT substring(line FROM 1 FOR 7)::int, + trim(substring(line FROM 167 FOR 30)), + oe_from_mpc(line) + FROM mpc_raw + WHERE length(line) >= 103 + AND substring(line FROM 1 FOR 7) ~ '^\s*\d+$'; + + DROP TABLE mpc_raw; + ``` + +3. **Query: what asteroids are above 20 degrees tonight?** + + ```sql + SELECT name, number, + round(topo_elevation(t)::numeric, 1) AS el, + round(topo_azimuth(t)::numeric, 1) AS az, + round((topo_range(t) / 149597870.7)::numeric, 2) AS dist_au + FROM asteroids, + small_body_observe(oe, '40.0N 105.3W 1655m'::observer, now()) AS t + WHERE topo_elevation(t) > 20 + ORDER BY topo_elevation(t) DESC + LIMIT 20; + ``` + +4. **Query: heliocentric distance of Ceres over 6 months:** + + ```sql + SELECT t::date AS date, + round(helio_distance( + small_body_heliocentric(oe, t))::numeric, 4) AS dist_au + FROM asteroids, + generate_series( + '2025-01-01'::timestamptz, + '2025-07-01'::timestamptz, + interval '15 days' + ) AS t + WHERE number = 1; + ``` + + + + ## Try it ### Circular orbit sanity check diff --git a/docs/src/content/docs/reference/functions-stars-comets.mdx b/docs/src/content/docs/reference/functions-stars-comets.mdx index 6c79b73..bf04a4d 100644 --- a/docs/src/content/docs/reference/functions-stars-comets.mdx +++ b/docs/src/content/docs/reference/functions-stars-comets.mdx @@ -1,12 +1,12 @@ --- -title: "Functions: Stars & Comets" +title: "Functions: Stars, Comets & Asteroids" sidebar: order: 5 --- import { Aside, Tabs, TabItem } from "@astrojs/starlight/components"; -Functions for computing topocentric positions of stars from catalog coordinates, propagating comets and asteroids on Keplerian orbits, and observing them from Earth. +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. --- @@ -255,3 +255,144 @@ FROM comet_catalog, earth, WHERE topo_elevation(c) > 0 ORDER BY topo_range(c); ``` + +--- + +## oe_from_mpc + +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. + +### Signature + +```sql +oe_from_mpc(line text) → orbital_elements +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `line` | `text` | One complete line from MPCORB.DAT (at least 103 characters) | + +### Returns + +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). + + + +### Example + +```sql +-- 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; +``` + +--- + +## small_body_heliocentric + +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. + +### Signature + +```sql +small_body_heliocentric(oe orbital_elements, t timestamptz) → heliocentric +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `oe` | `orbital_elements` | Bundled orbital elements | +| `t` | `timestamptz` | Evaluation time | + +### Returns + +A `heliocentric` position in AU (ecliptic J2000 frame). Identical to calling `kepler_propagate()` with the individual fields extracted from the type. + +### Example + +```sql +-- 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; +``` + +--- + +## small_body_observe + +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()`. + +### Signature + +```sql +small_body_observe(oe orbital_elements, obs observer, t timestamptz) → topocentric +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `oe` | `orbital_elements` | Bundled orbital elements | +| `obs` | `observer` | Observer location on Earth | +| `t` | `timestamptz` | Observation time | + +### Returns + +A `topocentric` with azimuth, elevation (degrees), range (km), and range rate (km/s). + + + +### Example + +```sql +-- 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; +``` + +```sql +-- 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; +``` diff --git a/docs/src/content/docs/reference/types.mdx b/docs/src/content/docs/reference/types.mdx index 42255a8..8135926 100644 --- a/docs/src/content/docs/reference/types.mdx +++ b/docs/src/content/docs/reference/types.mdx @@ -6,7 +6,7 @@ sidebar: import { Aside, Tabs, TabItem } from "@astrojs/starlight/components"; -pg_orrery defines seven fixed-size base types and one SQL composite type that represent the core data structures of orbital mechanics. Each base type has a fixed on-disk size, a text I/O format for readability, and accessor functions for extracting individual fields. +pg_orrery defines eight fixed-size base types and one SQL composite type that represent the core data structures of orbital mechanics. Each base type has a fixed on-disk size, a text I/O format for readability, and accessor functions for extracting individual fields. ## tle @@ -270,6 +270,74 @@ FROM generate_series(1, 8) AS body_id, --- +## orbital_elements + +**Size:** 72 bytes + +Classical Keplerian orbital elements for comets and asteroids. Stores nine doubles: osculation epoch, perihelion distance, eccentricity, inclination, argument of perihelion, longitude of ascending node, time of perihelion passage, absolute magnitude H, and slope parameter G. + +### Input Format + +A parenthesized tuple of nine values: + +```sql +SELECT '(2460605.5,2.5478,0.0789126,10.58664,73.42937,80.2686,2460319.0,3.33,0.12)'::orbital_elements; +``` + +The fields in order are: `(epoch_jd, q_au, e, inc_deg, omega_deg, Omega_deg, tp_jd, H, G)`. + + + +### Constructor + +| Function | Signature | Description | +|----------|-----------|-------------| +| `oe_from_mpc` | `oe_from_mpc(line text) → orbital_elements` | Parses one MPCORB.DAT fixed-width line. Converts MPC packed epoch, computes q and tp from (a, e, M). | + +```sql +-- Parse (1) Ceres from an MPCORB.DAT line +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' +); +``` + +### Accessor Functions + +| Function | Return Type | Description | +|----------|-------------|-------------| +| `oe_epoch(orbital_elements)` | `float8` | Osculation epoch (Julian date) | +| `oe_perihelion(orbital_elements)` | `float8` | Perihelion distance q (AU) | +| `oe_eccentricity(orbital_elements)` | `float8` | Eccentricity | +| `oe_inclination(orbital_elements)` | `float8` | Inclination (degrees) | +| `oe_arg_perihelion(orbital_elements)` | `float8` | Argument of perihelion (degrees) | +| `oe_raan(orbital_elements)` | `float8` | Longitude of ascending node (degrees) | +| `oe_tp(orbital_elements)` | `float8` | Time of perihelion passage (Julian date) | +| `oe_h_mag(orbital_elements)` | `float8` | Absolute magnitude H (NaN if unknown) | +| `oe_g_slope(orbital_elements)` | `float8` | Slope parameter G (NaN if unknown) | +| `oe_semi_major_axis(orbital_elements)` | `float8` | Semi-major axis a = q/(1-e) in AU. NULL for e ≥ 1. | +| `oe_period_years(orbital_elements)` | `float8` | Orbital period a^1.5 in years. NULL for e ≥ 1. | + +```sql +-- Parse Ceres and extract key parameters +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 oe_epoch(oe) AS epoch_jd, + oe_perihelion(oe) AS q_au, + oe_eccentricity(oe) AS ecc, + oe_inclination(oe) AS inc_deg, + oe_semi_major_axis(oe) AS a_au, + oe_period_years(oe) AS period_yr, + oe_h_mag(oe) AS h_mag +FROM ceres; +``` + +--- + ## observer_window **Type:** SQL composite (variable-length)