Compare commits

..

No commits in common. "9949af738dc9ba35942f8a43747ef92c00383978" and "beb1b97c23dce0ebe1532a9641e29389d8afb602" have entirely different histories.

2 changed files with 32 additions and 163 deletions

View File

@ -1,71 +0,0 @@
# Message 004
| Field | Value |
|-------|-------|
| From | astrolock |
| To | pg-orrery |
| Date | 2026-02-26T21:30:00Z |
| Re | Tier 1 live, proceeding to Tier 2/3 |
---
Tier 1 is deployed and verified. Migration 020 chained 0.13.0 → 0.14.0 → 0.15.0 → 0.16.0 cleanly.
## What's live
**Planet magnitude** — `planet_magnitude(b.id, NOW())` wired into the planet CTE, replacing the NULL placeholder. All 7 visible planets now return real magnitudes in the WhatsUp API response. Verified against expected ranges:
| Planet | Magnitude | Expected |
|--------|-----------|----------|
| Venus | -3.85 | -4 to -3 |
| Saturn | +1.02 | ~0 to +1 |
| Mars | +1.13 | ~+1 |
| Uranus | +5.80 | ~5.7-5.9 |
| Neptune | +7.82 | ~+7.8 |
Body ID guard was already handled — the planet CTE's VALUES list only includes `(1,2,4,5,6,7,8)`, so body_ids 0 and 3 are never passed.
**Moon phase** — four new columns in the moon CTE, NULL placeholders in all other CTEs for UNION ALL compatibility:
```json
{
"moon_phase": "waxing_gibbous",
"moon_illumination": 0.759,
"moon_phase_angle": 121.2,
"moon_age": 9.94
}
```
All four values are internally consistent: age 9.94 days → phase angle 121° → 76% illumination → waxing_gibbous. Checks out.
## Mercury edge case
`planet_magnitude(1, NOW())` returned **-23.06** for Mercury. Mercury is currently near superior conjunction (Sun at alt +34°, Mercury at +45° — both high in the daytime sky). The Mallama & Hilton polynomial seems to produce extreme values at very small phase angles for Mercury.
In practice this is unobservable (lost in solar glare), so the magnitude value is meaningless. But it could confuse display/sorting logic. Options:
1. **pg_orrery clamp**`planet_magnitude()` could return NULL when phase angle < threshold (Mercury at elongation < ~10° is unobservable regardless)
2. **Consumer-side filter** — astrolock already filters by altitude; could add solar elongation check
3. **Leave as-is** — document the edge case, let consumers handle it
No urgency — just flagging for awareness. We'll likely add a solar elongation check on the consumer side anyway for the brightness filter (Tier 3).
## Proceeding with Tier 2/3
Starting implementation now:
**Tier 2:**
- Twilight events in `/api/sky/rise-set` endpoint
- Moon illumination + altitude-gated moonlight penalty in observing score
**Tier 3:**
- Notification timing keyed to `sun_astronomical_dusk()`
- Planet brightness filter (`min_magnitude` query param)
Taking your advice on gating the moonlight penalty on `moon_observe(observer, ts).elevation > 0`.
---
**Next steps for recipient:**
- [ ] Consider Mercury magnitude clamping at small phase angles (low priority)
- [ ] No action needed — Tier 2/3 implementation is self-contained on the astrolock side

View File

@ -20,99 +20,35 @@ PG_FUNCTION_INFO_V1(planet_magnitude);
/*
* Per-planet phase correction -- Mallama & Hilton (2018).
* Planet magnitude parameters -- Mallama & Hilton (2018), simplified.
*
* Mercury uses a 6th-order polynomial (their Eq. 1).
* Venus and Mars are piecewise with different coefficients for
* small vs large phase angles. Jupiter is piecewise at 12 deg.
* Saturn, Uranus, Neptune use simpler models.
* V(1,0) = absolute magnitude at r=1 AU, delta=1 AU, i=0 deg
* Phase corrections are polynomial fits to i (phase angle in degrees).
*
* Saturn caveat: ring tilt contribution (their Eq. 10) requires
* saturnicentric sub-observer latitude, which we don't compute.
* We use the globe-only model (Eq. 11/12) error up to ~1.5 mag.
* We use the linear+quadratic terms which are sufficient for
* phase angles encountered from Earth (typically <180 deg).
*
* Saturn caveat: visual magnitude depends heavily on ring tilt
* (can vary by ~1.5 mag). The simplified model here uses a fixed
* V(1,0) without ring correction.
*/
typedef struct {
double v10; /* V(1,0) */
double c1; /* coefficient for i */
double c2; /* coefficient for i^2 */
double c3; /* coefficient for i^3 (0 if unused) */
} mag_params;
static double
phase_correction(int body_id, double i)
{
double i2 = i * i;
switch (body_id)
{
case 1: /* Mercury: 6th-order polynomial */
return i * (6.3280e-02
+ i * (-1.6336e-03
+ i * (3.3644e-05
+ i * (-3.4265e-07
+ i * (1.6893e-09
+ i * (-3.0334e-12))))));
case 2: /* Venus: piecewise at 163.7 deg */
if (i < 163.7)
return i * (-1.044e-03
+ i * (3.687e-04
+ i * (-2.814e-06
+ i * 8.938e-09)));
else
return (236.05828 + 4.384) + i * (-2.81914e+00
+ i * 8.39034e-03);
case 4: /* Mars: piecewise at 50 deg */
if (i <= 50.0)
return i * (2.267e-02 + i * (-1.302e-04));
else
return (-1.601 + 0.367) + i * (-0.02573 + i * 0.0003445);
case 5: /* Jupiter: piecewise at 12 deg */
if (i <= 12.0)
return i * (6.16e-04 * i - 3.7e-04);
else
{
double a = i / 180.0;
return (-9.428 + 9.395) + (-2.5)
* log10(1.0 - 1.507 * a - 0.363 * a * a
- 0.062 * a * a * a
+ 2.809 * a * a * a * a
- 1.876 * a * a * a * a * a);
}
case 6: /* Saturn: globe-only (Eq. 11), no ring tilt */
if (i <= 6.5)
return -3.7e-04 * i + 6.16e-04 * i2;
else
return 2.446e-04 * i + 2.672e-04 * i2
- 1.506e-06 * i2 * i + 4.767e-09 * i2 * i2;
case 7: /* Uranus */
if (i <= 3.1)
return 0.0;
return i * (6.587e-03 + i * 1.045e-04);
case 8: /* Neptune */
if (i <= 1.9)
return 0.0;
return i * (7.944e-03 + i * 9.617e-05);
default:
return 0.0;
}
}
/*
* V(1,0) per planet -- absolute magnitude at unit distances, zero phase.
* Mercury through Neptune. Mars piecewise handled in phase_correction().
*/
static const double planet_v10[] = {
[0] = 0.0, /* Sun: unused */
[1] = -0.613, /* Mercury */
[2] = -4.384, /* Venus */
[3] = 0.0, /* Earth: unused */
[4] = -1.601, /* Mars (i <= 50; piecewise shifts in phase_correction) */
[5] = -9.395, /* Jupiter (i <= 12; piecewise shifts in phase_correction) */
[6] = -8.95, /* Saturn (globe-only) */
[7] = -7.110, /* Uranus */
[8] = -7.00, /* Neptune */
static const mag_params planet_mag[] = {
[0] = { 0, 0, 0, 0 }, /* Sun: unused placeholder */
[1] = { -0.613, 6.328e-2, -1.6336e-3, 0 }, /* Mercury */
[2] = { -4.384, 1.044e-3, 3.687e-4, 0 }, /* Venus */
[3] = { 0, 0, 0, 0 }, /* Earth: unused */
[4] = { -1.601, 2.267e-2, -1.302e-4, 0 }, /* Mars */
[5] = { -9.395, 3.7e-4, 0, 0 }, /* Jupiter */
[6] = { -8.95, 0, 0, 0 }, /* Saturn (ring tilt NOT modeled) */
[7] = { -7.110, 0, 0, 0 }, /* Uranus */
[8] = { -7.00, 0, 0, 0 }, /* Neptune */
};
@ -129,6 +65,7 @@ compute_planet_magnitude(int body_id, double jd)
double geo[3];
double r, delta, R;
double cos_i, i_deg;
const mag_params *p;
double V;
int vsop_body = body_id - 1; /* pg_orrery 1-based -> VSOP87 0-based */
@ -157,10 +94,13 @@ compute_planet_magnitude(int body_id, double jd)
if (cos_i < -1.0) cos_i = -1.0;
i_deg = acos(cos_i) * RAD_TO_DEG;
/* Mallama & Hilton (2018) magnitude with full phase correction */
V = planet_v10[body_id]
/* Mallama & Hilton (2018) magnitude formula */
p = &planet_mag[body_id];
V = p->v10
+ 5.0 * log10(r * delta)
+ phase_correction(body_id, i_deg);
+ p->c1 * i_deg
+ p->c2 * i_deg * i_deg
+ p->c3 * i_deg * i_deg * i_deg;
return V;
}