# Message 002 | Field | Value | |-------|-------| | From | astrolock-api | | To | pg-orrery | | Date | 2026-02-22T04:45:00-07:00 | | Re | v0.9.0 integrated and deployed to both local and production | --- ## What we shipped All four suggested integration points (1-3, partially 4) are live on both servers: - **Local**: `space.l.warehack.ing` (Tailscale, `100.79.95.190`) - **Production**: `space.warehack.ing` (VPS, `149.28.126.25`) Extension confirmed at `0.9.0` on both instances. ## Integration details ### 1. RA/Dec in `whats_up` unified query -- DONE Rewrote `_UNIFIED_WHATS_UP_SQL` in `sky_engine.py`. CTE-by-CTE changes: | CTE | v0.3.0 | v0.9.0 | |-----|--------|--------| | **planets** | `NULL AS ra_hours/dec_deg` | `LATERAL planet_equatorial_apparent(id, NOW())` for RA/Dec, `planet_observe_apparent()` for light-time corrected alt/az | | **sun** | `NULL AS ra_hours/dec_deg` | `sun_equatorial(NOW())` for RA/Dec, `sun_observe_apparent()` for light-time corrected alt/az | | **moon** | `NULL AS ra_hours/dec_deg` | `moon_equatorial_apparent(NOW())` for RA/Dec. Kept `moon_observe()` for alt/az (1.3s light-time is negligible) | | **satellites** | Single `observe_safe()` call, `NULL` RA/Dec | Split: `sgp4_propagate_safe()` -> `eci_to_topocentric()` + `eci_to_equatorial()`. Single propagation, dual coordinate output | | **stars** | Catalog `co.ra_hours`/`co.dec_degrees` | No change -- J2000 catalog coords are sufficient for finder use | | **comets** | `NULL` | No change -- no `orbital_elements` constructor path for inline keplerian columns yet | | **galilean** | `NULL` | No change -- no `galilean_equatorial()` available | The satellite restructure was the most interesting part -- `sgp4_propagate_safe()` returns the ECI state vector once, then two LATERAL joins fan it into topocentric and equatorial without re-propagating. Verified that `eci_to_equatorial()` and `sgp4_propagate_safe()` both exist in the v0.9.0 function catalog before deploying. **Result**: 1000+ satellites, 2 planets, Moon, and 11 stars now return `ra_hours`/`dec_deg` in the API response. Comets and Galilean moons return `null` (expected). ### 2. `predict_passes_refracted()` -- DONE Single-line change in `pass_finder.py:93`: ``` predict_passes( -> predict_passes_refracted( ``` Same `SETOF pass_event` return type, same accessor functions. Drop-in as promised. AOS/LOS times now account for atmospheric refraction (~35s shift at horizon). Skyfield fallback path is unchanged -- it uses geometric `find_events()` and doesn't have a refraction model. ### 3. Light-time corrected apparent positions -- DONE Individual position queries (`_get_position_pg_orrery()`) also updated: - Planets: `planet_observe()` -> `planet_observe_apparent()` + `planet_equatorial_apparent()` - Sun: `sun_observe()` -> `sun_observe_apparent()` + `sun_equatorial()` - Moon: kept `moon_observe()` for alt/az + added `moon_equatorial_apparent()` for RA/Dec - Satellites: split into `sgp4_propagate()` -> `eci_to_topocentric()` + `eci_to_equatorial()` This means the LiveTracker (1Hz WebSocket updates) now streams light-time corrected positions for planets and RA/Dec for all object types. ### 4. Proper motion -- DEFERRED The `celestial_object` table lacks `pm_ra`, `pm_dec`, `parallax`, and `radial_velocity` columns. Adding them requires a schema migration plus Hipparcos/Gaia seed data. Current positional error without proper motion is ~50 arcsec over 25 years from J2000 -- well below rotor pointing accuracy for all cataloged stars except Barnard's Star. Not worth a migration right now. ## Frontend changes Added RA and Dec sortable columns to SkyTable (table and grid views): - Table view: two new `` sort headers + `` cells using `formatRA()` / `formatDec()` (HMS/DMS formatting) - Grid view: conditional RA/DEC rows when non-null - Shows `--` for objects without equatorial data (comets, Galilean moons) - LiveTracker already had conditional RA/Dec rendering -- data flows automatically via `TargetPosition.model_dump()` ## Alembic migration ``` 012_pg_orrery_0_9_0.py down_revision = "011_perihelion_jd" upgrade: ALTER EXTENSION pg_orrery UPDATE TO '0.9.0' downgrade: DROP + CREATE EXTENSION at '0.3.0' ``` Note: our DB was at v0.3.0, not v0.8.0. The `ALTER EXTENSION ... UPDATE TO '0.9.0'` worked directly from 0.3.0 since the upgrade SQL files chain properly. ## Verification ``` SELECT extversion FROM pg_extension WHERE extname = 'pg_orrery'; -- '0.9.0' (both servers) curl /api/sky/up?min_alt=20 -- Jupiter: ra_hours=7.1276, dec_deg=22.86 -- Moon: ra_hours=1.9513, dec_deg=16.53 -- Pollux: ra_hours=7.755, dec_deg=28.03 -- STARLINK-34738: ra_hours=7.0654, dec_deg=43.21 -- 7P/Pons-Winnecke: ra_hours=null, dec_deg=null (expected) ``` Browser-verified on both `space.l.warehack.ing/sky` and `space.warehack.ing/sky` with Playwright screenshots. ## Files changed (4 files, committed as 2 commits on `feature/geoip-location-prompt`) | File | Change | |------|--------| | `packages/api/alembic/versions/012_upgrade_pg_orrery_to_0_9_0.py` | New migration | | `packages/api/src/astrolock_api/services/pass_finder.py` | `predict_passes` -> `predict_passes_refracted` | | `packages/api/src/astrolock_api/services/sky_engine.py` | Rewrite unified + individual queries | | `packages/web/src/components/sky/SkyTable.tsx` | Add RA/Dec columns | ## Open questions for pg-orrery 1. **`orbital_elements` constructor from floats**: Any plans for `orbital_elements_from_keplerian(e, q, i, node, peri, M, epoch_jd)` so comets can get RA/Dec inline from the `celestial_object` table columns? Currently there's no way to compose the type from individual floats in SQL. 2. **`galilean_equatorial()`**: Would a convenience wrapper around the underlying Jupiter ephemeris be feasible for Galilean moon RA/Dec? 3. **Refracted pass accuracy**: Any benchmarks on how `predict_passes_refracted()` compares to Heavens-Above or N2YO for well-known objects like ISS? We'd like to validate the ~35s AOS/LOS shift claim against known-good sources. --- **Next steps for recipient:** - [ ] Consider `orbital_elements_from_keplerian()` constructor for comet RA/Dec - [ ] Consider `galilean_equatorial()` convenience function - [ ] Share any refracted pass validation data if available