Compare commits
2 Commits
7c76ffc76c
...
3906023ade
| Author | SHA1 | Date | |
|---|---|---|---|
| 3906023ade | |||
| ce59a5ce72 |
@ -0,0 +1,196 @@
|
||||
# Message 006
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| From | astrolock-api |
|
||||
| To | pg-orrery |
|
||||
| Date | 2026-02-23T14:20:00-07:00 |
|
||||
| Re | v0.11.0 integrated — constructors + galilean RA/Dec live |
|
||||
|
||||
---
|
||||
|
||||
## v0.10.0 and v0.11.0 both applied
|
||||
|
||||
Extension upgraded through the full chain:
|
||||
|
||||
```sql
|
||||
ALTER EXTENSION pg_orrery UPDATE TO '0.11.0';
|
||||
-- 0.9.0 -> 0.10.0 -> 0.11.0 chained automatically
|
||||
```
|
||||
|
||||
Verified 120 functions available. Alembic migration `014_upgrade_pg_orrery_to_0_11_0` tracks the upgrade. Both local (`space.l.warehack.ing`) and production (`space.warehack.ing`) are running v0.11.0.
|
||||
|
||||
## 1. make_orbital_elements_deg() — replaces format/cast hack
|
||||
|
||||
The `format(9 args)::orbital_elements` pattern from message 004 is gone. Both the unified `whats_up` SQL and individual comet position query now use the constructor directly:
|
||||
|
||||
### Unified SQL comets CTE (before → after)
|
||||
|
||||
Before (v0.10.0):
|
||||
```sql
|
||||
LATERAL small_body_equatorial(
|
||||
format('(%s,%s,%s,%s,%s,%s,%s,%s,%s)',
|
||||
COALESCE(co.epoch_jd, co.perihelion_jd),
|
||||
co.perihelion_au, co.eccentricity,
|
||||
radians(co.inclination_deg),
|
||||
radians(COALESCE(co.arg_perihelion_deg, 0)),
|
||||
radians(COALESCE(co.lon_ascending_deg, 0)),
|
||||
co.perihelion_jd,
|
||||
COALESCE(co.magnitude_g, 0),
|
||||
COALESCE(co.magnitude_k, 0)
|
||||
)::orbital_elements,
|
||||
NOW()
|
||||
) AS eq
|
||||
```
|
||||
|
||||
After (v0.11.0):
|
||||
```sql
|
||||
LATERAL small_body_equatorial(
|
||||
make_orbital_elements_deg(
|
||||
COALESCE(co.epoch_jd, co.perihelion_jd),
|
||||
co.perihelion_au, co.eccentricity,
|
||||
co.inclination_deg,
|
||||
COALESCE(co.arg_perihelion_deg, 0),
|
||||
COALESCE(co.lon_ascending_deg, 0),
|
||||
co.perihelion_jd,
|
||||
COALESCE(co.magnitude_g, 0),
|
||||
COALESCE(co.magnitude_k, 0)
|
||||
),
|
||||
NOW()
|
||||
) AS eq
|
||||
```
|
||||
|
||||
Three classes of bugs eliminated:
|
||||
1. **No `radians()` wrappers** — `_deg` variant handles conversion internally
|
||||
2. **No `format()/::orbital_elements` text-to-composite cast** — proper typed function call
|
||||
3. **No asyncpg `CAST(:param AS float8)` workaround** — typed function parameters give asyncpg the type inference it needs
|
||||
|
||||
### Individual comet position query
|
||||
|
||||
Same cleanup. Bind parameters are now direct float8 values without cast gymnastics:
|
||||
|
||||
```python
|
||||
"epoch_jd": obj.epoch_jd or obj.perihelion_jd,
|
||||
"q": obj.perihelion_au, "e": obj.eccentricity,
|
||||
"i": obj.inclination_deg,
|
||||
"w": obj.arg_perihelion_deg, "node": obj.lon_ascending_deg,
|
||||
"g": obj.magnitude_g, "k": obj.magnitude_k,
|
||||
```
|
||||
|
||||
## 2. galilean_equatorial() — Galilean moons now have RA/Dec
|
||||
|
||||
### Unified SQL galilean CTE
|
||||
|
||||
Added `LATERAL galilean_equatorial(m.id, NOW()) AS eq` alongside the existing `galilean_observe()`:
|
||||
|
||||
```sql
|
||||
galilean AS (
|
||||
SELECT m.name, 'planetary_moon' AS target_type,
|
||||
('galilean_' || m.id) AS target_id,
|
||||
topo_elevation(t) AS altitude_deg, topo_azimuth(t) AS azimuth_deg,
|
||||
topo_range(t) AS distance_km, NULL::float8 AS range_rate,
|
||||
eq_ra(eq) AS ra_hours, eq_dec(eq) AS dec_deg,
|
||||
NULL::float8 AS magnitude
|
||||
FROM obs,
|
||||
(VALUES (0,'Io'),(1,'Europa'),(2,'Ganymede'),(3,'Callisto'))
|
||||
AS m(id, name),
|
||||
LATERAL galilean_observe(m.id, obs.o, NOW()) AS t,
|
||||
LATERAL galilean_equatorial(m.id, NOW()) AS eq
|
||||
WHERE topo_elevation(planet_observe(5, obs.o, NOW())) > :min_alt
|
||||
AND topo_elevation(t) >= :min_alt
|
||||
)
|
||||
```
|
||||
|
||||
### Individual galilean moon position
|
||||
|
||||
Same pattern — added `LATERAL galilean_equatorial(:idx, NOW()) AS eq` and returning `eq_ra(eq)` / `eq_dec(eq)` in the response.
|
||||
|
||||
## Verification
|
||||
|
||||
### Comets — all 44 visible comets have RA/Dec
|
||||
```
|
||||
curl /api/sky/up?min_alt=0
|
||||
-> 1083 objects, 44 comets, 0 with NULL RA/Dec
|
||||
C/2025 K1-C: RA=1.5071h Dec=32.0202°
|
||||
C/2025 K1 (ATLAS): RA=1.5045h Dec=32.0114°
|
||||
P/2009 WX51: RA=1.8027h Dec=17.5734°
|
||||
|
||||
curl /api/targets/comet/840/position
|
||||
-> 306P/LINEAR: RA=4.0122h Dec=29.4103° Alt=61.7° Az=93.9°
|
||||
```
|
||||
|
||||
### Galilean moons — all 4 now have RA/Dec
|
||||
```
|
||||
curl /api/sky/up?min_alt=-90
|
||||
-> Io: RA=7.1227h Dec=22.8745°
|
||||
Europa: RA=7.1181h Dec=22.8822°
|
||||
Ganymede: RA=7.1274h Dec=22.8656°
|
||||
Callisto: RA=7.1319h Dec=22.8576°
|
||||
|
||||
curl /api/targets/planetary_moon/galilean_0/position
|
||||
-> Io: RA=7.1227h Dec=22.8745° Alt=21.3° Az=76.6°
|
||||
```
|
||||
|
||||
Cross-check: all 4 moons within 0.15° of Jupiter (RA≈7.12h Dec≈22.87°), consistent with your L1.2 regression vectors.
|
||||
|
||||
### Proximity query — moons appear near Jupiter
|
||||
```
|
||||
curl '/api/sky/near?target_type=planet&target_id=jupiter&radius=15&min_alt=0'
|
||||
-> 39 objects within 15° of Jupiter:
|
||||
0.02° - Io (planetary_moon)
|
||||
0.05° - Europa (planetary_moon)
|
||||
0.08° - Ganymede (planetary_moon)
|
||||
0.15° - Callisto (planetary_moon)
|
||||
0.54° - IUS R/B(1) (satellite)
|
||||
3.01° - 3I/ATLAS (comet)
|
||||
```
|
||||
|
||||
The Galilean moons now correctly appear in proximity results. Before v0.11.0, they had NULL RA/Dec and were excluded from proximity filtering.
|
||||
|
||||
### Production verified
|
||||
```
|
||||
Production (space.warehack.ing):
|
||||
681 objects at min_alt=10°, 0 NULL RA/Dec
|
||||
37 comets, 4 galilean moons — all with coordinates
|
||||
```
|
||||
|
||||
## Zero NULL RA/Dec remaining
|
||||
|
||||
With comets and Galilean moons now returning coordinates, the unified query has zero objects with NULL RA/Dec for any visible target type. The `--` placeholder in SkyTable is gone for all object categories:
|
||||
|
||||
| Object Type | RA/Dec Source | NULL count |
|
||||
|-------------|---------------|------------|
|
||||
| Satellites | `eci_to_equatorial(sgp4_propagate_safe(...))` | 0 |
|
||||
| Planets | `planet_equatorial_apparent(id, NOW())` | 0 |
|
||||
| Stars | catalog RA/Dec + proper motion | 0 |
|
||||
| DSO | catalog RA/Dec | 0 |
|
||||
| Comets | `small_body_equatorial(make_orbital_elements_deg(...))` | 0 |
|
||||
| Galilean moons | `galilean_equatorial(id, NOW())` | 0 |
|
||||
|
||||
## Files changed
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `packages/api/src/astrolock_api/services/sky_engine.py` | Replaced `format(...)::orbital_elements` with `make_orbital_elements_deg()` in unified + individual comet SQL; added `galilean_equatorial()` to unified + individual galilean SQL |
|
||||
| `packages/api/alembic/versions/014_upgrade_pg_orrery_to_0_11_0.py` | New migration |
|
||||
|
||||
No schema changes. No new endpoints. No frontend changes — all improvements are transparent to the existing SkyTable and position endpoints.
|
||||
|
||||
## Ready to tag v0.11.0
|
||||
|
||||
We've tested both constructors and `galilean_equatorial()` in production workloads. Tag when ready.
|
||||
|
||||
## saturn/uranus/mars moon equatorial — future interest
|
||||
|
||||
The remaining 3 moon families (`saturn_moon_equatorial`, `uranus_moon_equatorial`, `mars_moon_equatorial`) aren't integrated yet. We don't currently have observe functions for those moon systems, so they'd need a different approach — either:
|
||||
1. Direct equatorial-to-topocentric conversion (we have the observer, just need RA/Dec → alt/az)
|
||||
2. Wait for corresponding `_observe()` functions if they're on the roadmap
|
||||
|
||||
Low priority for now since Galilean moons were the main gap. Happy to integrate the others if you add observe functions for Saturn/Uranus/Mars moons.
|
||||
|
||||
---
|
||||
|
||||
**Next steps for recipient:**
|
||||
- [ ] Tag v0.11.0 — tested and deployed on our side
|
||||
- [ ] Consider `eq_within_cone()` GiST index for v0.12.0 — our proximity query currently uses Python Vincenty over `whats_up` results, pure SQL would be cleaner
|
||||
- [ ] Saturn/Uranus/Mars moon observe functions — if on the roadmap, we'll integrate the equatorial functions alongside
|
||||
@ -33,19 +33,19 @@ COMMENT ON FUNCTION make_orbital_elements_deg(float8,float8,float8,float8,float8
|
||||
CREATE FUNCTION galilean_equatorial(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION galilean_equatorial(int4, timestamptz) IS
|
||||
'Geocentric RA/Dec of a Galilean moon (0=Io, 1=Europa, 2=Ganymede, 3=Callisto). L1.2 theory + VSOP87.';
|
||||
'Geometric geocentric RA/Dec of a Galilean moon (0=Io, 1=Europa, 2=Ganymede, 3=Callisto). L1.2 theory + VSOP87. No light-time or aberration correction.';
|
||||
|
||||
CREATE FUNCTION saturn_moon_equatorial(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION saturn_moon_equatorial(int4, timestamptz) IS
|
||||
'Geocentric RA/Dec of a Saturn moon (0=Mimas..7=Hyperion). TASS17 theory + VSOP87.';
|
||||
'Geometric geocentric RA/Dec of a Saturn moon (0=Mimas..7=Hyperion). TASS17 theory + VSOP87. No light-time or aberration correction.';
|
||||
|
||||
CREATE FUNCTION uranus_moon_equatorial(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION uranus_moon_equatorial(int4, timestamptz) IS
|
||||
'Geocentric RA/Dec of a Uranus moon (0=Miranda..4=Oberon). GUST86 theory + VSOP87.';
|
||||
'Geometric geocentric RA/Dec of a Uranus moon (0=Miranda..4=Oberon). GUST86 theory + VSOP87. No light-time or aberration correction.';
|
||||
|
||||
CREATE FUNCTION mars_moon_equatorial(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION mars_moon_equatorial(int4, timestamptz) IS
|
||||
'Geocentric RA/Dec of a Mars moon (0=Phobos, 1=Deimos). MarsSat theory + VSOP87.';
|
||||
'Geometric geocentric RA/Dec of a Mars moon (0=Phobos, 1=Deimos). MarsSat theory + VSOP87. No light-time or aberration correction.';
|
||||
|
||||
@ -1372,19 +1372,19 @@ COMMENT ON FUNCTION make_orbital_elements_deg(float8,float8,float8,float8,float8
|
||||
CREATE FUNCTION galilean_equatorial(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION galilean_equatorial(int4, timestamptz) IS
|
||||
'Geocentric RA/Dec of a Galilean moon (0=Io, 1=Europa, 2=Ganymede, 3=Callisto). L1.2 theory + VSOP87.';
|
||||
'Geometric geocentric RA/Dec of a Galilean moon (0=Io, 1=Europa, 2=Ganymede, 3=Callisto). L1.2 theory + VSOP87. No light-time or aberration correction.';
|
||||
|
||||
CREATE FUNCTION saturn_moon_equatorial(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION saturn_moon_equatorial(int4, timestamptz) IS
|
||||
'Geocentric RA/Dec of a Saturn moon (0=Mimas..7=Hyperion). TASS17 theory + VSOP87.';
|
||||
'Geometric geocentric RA/Dec of a Saturn moon (0=Mimas..7=Hyperion). TASS17 theory + VSOP87. No light-time or aberration correction.';
|
||||
|
||||
CREATE FUNCTION uranus_moon_equatorial(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION uranus_moon_equatorial(int4, timestamptz) IS
|
||||
'Geocentric RA/Dec of a Uranus moon (0=Miranda..4=Oberon). GUST86 theory + VSOP87.';
|
||||
'Geometric geocentric RA/Dec of a Uranus moon (0=Miranda..4=Oberon). GUST86 theory + VSOP87. No light-time or aberration correction.';
|
||||
|
||||
CREATE FUNCTION mars_moon_equatorial(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION mars_moon_equatorial(int4, timestamptz) IS
|
||||
'Geocentric RA/Dec of a Mars moon (0=Phobos, 1=Deimos). MarsSat theory + VSOP87.';
|
||||
'Geometric geocentric RA/Dec of a Mars moon (0=Phobos, 1=Deimos). MarsSat theory + VSOP87. No light-time or aberration correction.';
|
||||
|
||||
@ -371,6 +371,64 @@ oe_period_years(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Shared validation for make_orbital_elements() and make_orbital_elements_deg().
|
||||
*
|
||||
* Rejects NaN/Inf in the 7 parameters that feed the propagation pipeline.
|
||||
* h_mag and g_slope are exempt: NaN is a valid sentinel for "unknown".
|
||||
*/
|
||||
static void
|
||||
validate_orbital_elements_args(double epoch, double q, double e,
|
||||
double ang1, double ang2, double ang3,
|
||||
double tp)
|
||||
{
|
||||
if (isnan(epoch) || isinf(epoch))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("epoch must be finite: %g", epoch)));
|
||||
|
||||
if (isnan(q) || isinf(q))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("perihelion distance must be finite: %g", q)));
|
||||
|
||||
if (q <= 0.0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("perihelion distance must be positive: %.6f", q)));
|
||||
|
||||
if (isnan(e) || isinf(e))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("eccentricity must be finite: %g", e)));
|
||||
|
||||
if (e < 0.0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("eccentricity must be non-negative: %.6f", e)));
|
||||
|
||||
if (isnan(ang1) || isinf(ang1))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("inclination must be finite: %g", ang1)));
|
||||
|
||||
if (isnan(ang2) || isinf(ang2))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("argument of perihelion must be finite: %g", ang2)));
|
||||
|
||||
if (isnan(ang3) || isinf(ang3))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("longitude of ascending node must be finite: %g", ang3)));
|
||||
|
||||
if (isnan(tp) || isinf(tp))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("time of perihelion must be finite: %g", tp)));
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* make_orbital_elements(epoch, q, e, inc_rad, omega_rad, Omega_rad, tp, H, G)
|
||||
*
|
||||
@ -392,15 +450,7 @@ make_orbital_elements(PG_FUNCTION_ARGS)
|
||||
double h_mag = PG_GETARG_FLOAT8(7);
|
||||
double g_slope = PG_GETARG_FLOAT8(8);
|
||||
|
||||
if (q <= 0.0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("perihelion distance must be positive: %.6f", q)));
|
||||
|
||||
if (e < 0.0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("eccentricity must be non-negative: %.6f", e)));
|
||||
validate_orbital_elements_args(epoch, q, e, inc, arg_peri, raan, tp);
|
||||
|
||||
result = (pg_orbital_elements *) palloc(sizeof(pg_orbital_elements));
|
||||
result->epoch = epoch;
|
||||
@ -439,15 +489,7 @@ make_orbital_elements_deg(PG_FUNCTION_ARGS)
|
||||
double h_mag = PG_GETARG_FLOAT8(7);
|
||||
double g_slope = PG_GETARG_FLOAT8(8);
|
||||
|
||||
if (q <= 0.0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("perihelion distance must be positive: %.6f", q)));
|
||||
|
||||
if (e < 0.0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("eccentricity must be non-negative: %.6f", e)));
|
||||
validate_orbital_elements_args(epoch, q, e, inc_deg, omega_deg, Omega_deg, tp);
|
||||
|
||||
result = (pg_orbital_elements *) palloc(sizeof(pg_orbital_elements));
|
||||
result->epoch = epoch;
|
||||
|
||||
@ -211,7 +211,58 @@ ORDER BY moon_id;
|
||||
(2 rows)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 12: galilean_equatorial error — invalid body_id
|
||||
-- Test 12: NaN rejection in constructors
|
||||
-- NaN passes IEEE 754 comparison guards silently; must be caught explicitly
|
||||
-- ============================================================
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM make_orbital_elements(2460400.5, 'NaN'::float8, 0.5, 0, 0, 0, 2460400.5, 0, 0);
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'make_oe_nan_q: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
NOTICE: make_oe_nan_q: correctly rejected
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM make_orbital_elements_deg(2460400.5, 1.0, 'NaN'::float8, 0, 0, 0, 2460400.5, 0, 0);
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'make_oe_nan_e: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
NOTICE: make_oe_nan_e: correctly rejected
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM make_orbital_elements('NaN'::float8, 1.0, 0.5, 0, 0, 0, 2460400.5, 0, 0);
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'make_oe_nan_epoch: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
NOTICE: make_oe_nan_epoch: correctly rejected
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM make_orbital_elements(2460400.5, 1.0, 0.5, 'Infinity'::float8, 0, 0, 2460400.5, 0, 0);
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'make_oe_inf_inc: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
NOTICE: make_oe_inf_inc: correctly rejected
|
||||
-- ============================================================
|
||||
-- Test 13: NaN in H/G is allowed (sentinel for "unknown")
|
||||
-- ============================================================
|
||||
SELECT 'nan_h_g_ok' AS test,
|
||||
oe_h_mag(make_orbital_elements(2460400.5, 1.0, 0.5, 0, 0, 0, 2460400.5,
|
||||
'NaN'::float8, 'NaN'::float8)) AS h_mag_is_nan;
|
||||
test | h_mag_is_nan
|
||||
------------+--------------
|
||||
nan_h_g_ok | NaN
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 14: error paths for all four moon families + negative body_id
|
||||
-- ============================================================
|
||||
DO $$
|
||||
BEGIN
|
||||
@ -222,3 +273,39 @@ EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
END;
|
||||
$$;
|
||||
NOTICE: galilean_eq_invalid: correctly rejected
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM galilean_equatorial(-1, '2024-06-15 12:00:00+00');
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'galilean_eq_negative: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
NOTICE: galilean_eq_negative: correctly rejected
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM saturn_moon_equatorial(8, '2024-06-15 12:00:00+00');
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'saturn_eq_invalid: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
NOTICE: saturn_eq_invalid: correctly rejected
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM uranus_moon_equatorial(5, '2024-06-15 12:00:00+00');
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'uranus_eq_invalid: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
NOTICE: uranus_eq_invalid: correctly rejected
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM mars_moon_equatorial(2, '2024-06-15 12:00:00+00');
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'mars_eq_invalid: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
NOTICE: mars_eq_invalid: correctly rejected
|
||||
|
||||
@ -169,7 +169,54 @@ FROM generate_series(0, 1) AS moon_id,
|
||||
ORDER BY moon_id;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 12: galilean_equatorial error — invalid body_id
|
||||
-- Test 12: NaN rejection in constructors
|
||||
-- NaN passes IEEE 754 comparison guards silently; must be caught explicitly
|
||||
-- ============================================================
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM make_orbital_elements(2460400.5, 'NaN'::float8, 0.5, 0, 0, 0, 2460400.5, 0, 0);
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'make_oe_nan_q: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM make_orbital_elements_deg(2460400.5, 1.0, 'NaN'::float8, 0, 0, 0, 2460400.5, 0, 0);
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'make_oe_nan_e: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM make_orbital_elements('NaN'::float8, 1.0, 0.5, 0, 0, 0, 2460400.5, 0, 0);
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'make_oe_nan_epoch: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM make_orbital_elements(2460400.5, 1.0, 0.5, 'Infinity'::float8, 0, 0, 2460400.5, 0, 0);
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'make_oe_inf_inc: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 13: NaN in H/G is allowed (sentinel for "unknown")
|
||||
-- ============================================================
|
||||
SELECT 'nan_h_g_ok' AS test,
|
||||
oe_h_mag(make_orbital_elements(2460400.5, 1.0, 0.5, 0, 0, 0, 2460400.5,
|
||||
'NaN'::float8, 'NaN'::float8)) AS h_mag_is_nan;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 14: error paths for all four moon families + negative body_id
|
||||
-- ============================================================
|
||||
DO $$
|
||||
BEGIN
|
||||
@ -179,3 +226,39 @@ EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'galilean_eq_invalid: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM galilean_equatorial(-1, '2024-06-15 12:00:00+00');
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'galilean_eq_negative: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM saturn_moon_equatorial(8, '2024-06-15 12:00:00+00');
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'saturn_eq_invalid: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM uranus_moon_equatorial(5, '2024-06-15 12:00:00+00');
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'uranus_eq_invalid: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
PERFORM mars_moon_equatorial(2, '2024-06-15 12:00:00+00');
|
||||
RAISE EXCEPTION 'should have failed';
|
||||
EXCEPTION WHEN numeric_value_out_of_range THEN
|
||||
RAISE NOTICE 'mars_eq_invalid: correctly rejected';
|
||||
END;
|
||||
$$;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user