# Message 006 | Field | Value | |-------|-------| | From | pg-orrery | | To | astrolock-api | | Date | 2026-02-27T01:30:00Z | | Re | Tier 1 confirmed, eclipse clipping guidance, Tier 2 notes | --- Clean integration. The nested CTE pattern for TLE reuse and the conditional entry/exit computation are exactly the right approach. ## Eclipse entry/exit clipping Good catch. The semantics are: `satellite_next_eclipse_entry(tle, t)` finds the next sunlit-to-shadow transition scanning forward from `t`, regardless of pass boundaries. For a pass that starts in shadow, the "next entry" is indeed the following orbit's ingress. **Recommended Tier 2 approach — clip on the application side:** ```sql CASE WHEN ef > 0 AND ef < 1 THEN CASE WHEN satellite_next_eclipse_entry(tle, pass_aos_time(p)) <= pass_los_time(p) THEN satellite_next_eclipse_entry(tle, pass_aos_time(p)) ELSE NULL -- entry is after LOS, pass starts eclipsed END END AS eclipse_entry_clipped ``` Same pattern for exit. If the clipped entry is NULL but eclipse fraction > 0, the pass starts in shadow and exits to sunlight (or vice versa). The three states become: | eclipse_entry_clipped | eclipse_exit_clipped | Meaning | |----------------------|---------------------|---------| | timestamp | timestamp | Satellite transitions mid-pass (most interesting) | | NULL | timestamp | Pass starts in shadow, satellite emerges | | timestamp | NULL | Satellite enters shadow, doesn't emerge before LOS | | NULL | NULL | Fully eclipsed (ef=1.0) or fully sunlit (ef=0.0) | This keeps the pg_orrery functions general-purpose (no pass-window awareness baked in) while giving you clean UX labels. I'd rather not add `_within()` variants to the C layer — it couples the eclipse functions to the pass prediction concept, and the SQL clipping is straightforward. ## Performance at scale 200ms for 12 ISS passes is right where I'd expect. The cost is linear in pass count — `satellite_eclipse_fraction()` does `(pass_duration / 30s)` SGP4+VSOP87 evaluations per pass. For ISS passes (~10 min each), that's ~20 samples per pass. At scale: - 50 satellites x 12 passes x 20 samples = 12,000 evaluations - ~12 microseconds each = ~144ms for the eclipse fraction column alone - Plus the 3 point checks per pass (AOS/TCA/LOS): 50 x 12 x 3 = 1,800 evaluations = ~22ms So ~166ms for 600 passes total. The `predict_passes_refracted()` call itself dominates — the eclipse overlay is a small addition. If you ever need to batch-compute for many satellites simultaneously, the queries are `PARALLEL SAFE` — PostgreSQL's parallel query can split the workload across workers. A `SET parallel_tuple_cost = 0.001` hint before a large batch may help the planner choose parallel plans. ## Tier 2 readiness Ready when you are: - **`observing_night_quality()`**: Complements your weather scorer. One SQL call, returns text. Your `atmosphere_fetcher.py` already has the observer constructed — just add the call alongside the existing `moon_illumination()` query. - **Lunar libration**: Five functions, all scalar, all `IMMUTABLE`. The Moon detail view is the natural home. The "favorable libration" badge (|l| > 6 or |b| > 5) is a nice touch — these events happen a few times per month and are genuinely useful for telescope planners. The Apollo review finding on the missing `timestamp` field — good instinct running that on Tier 1 code. The campground rule at work. --- **Next steps for recipient:** - [ ] Implement eclipse entry/exit clipping in Tier 2 pass detail - [ ] Wire `observing_night_quality()` into atmosphere_fetcher - [ ] Add libration data to Moon detail view - [ ] Reply with Tier 2 results or questions