Add waxing/waning crescent phases; fix phase-shadow double-application

moon_detect.py
- detect_moon() accepts saturated_threshold / min_roundness / max_halo_ratio
  so crescent phases (dim, non-circular arc) can be detected with relaxed
  thresholds without changing defaults for full/quarter detection.

moon_phase.py
- crescent_times_in_range(): 2-hour scan returning the UTC moment illumination
  crosses a target fraction (default 28%) on the waxing or waning side.
  Used for scheduling just like phase_events_in_range() for named phases.

moon_composite.py
- Remove _apply_phase_shadow() from composite_full_moon.  NASA SVS Dial-a-Moon
  renders already include the correct terminator, libration and earthshine for
  the exact hour; applying the shadow on top double-darkened the unlit limb on
  every non-full phase.  The function stays available for callers that need it.

moon_phase_monthly.py
- Add waxing-crescent (🌒) and waning-crescent (🌘) to PHASE_SPEC.
  Timing via crescent_times_in_range(); scheduling identical to other phases.
- CRESCENT_* tuning constants (all overridable from sky-cam.conf):
    MOON_CRESCENT_TARGET_ILLUM   default 0.28
    MOON_CRESCENT_MIN_QUALITY    default 0.35
    MOON_CRESCENT_SATURATED_THR  default 180
    MOON_CRESCENT_MIN_ROUNDNESS  default 0.15
    MOON_CRESCENT_MAX_HALO_RATIO default 12.0
- run_phase() and auto_run() branch on spec['crescent'] to use crescent
  timing and detection params.
- Atmospheric background naturally captures twilight colours (dawn for waning
  crescent, dusk/dawn for waxing) from the real east frame.

https://claude.ai/code/session_01HuJ83KvMvshiY6HxJbtMsc
This commit is contained in:
Claude
2026-05-03 23:21:44 +00:00
parent b21803151b
commit 29a864375b
4 changed files with 131 additions and 24 deletions
+5 -6
View File
@@ -254,12 +254,11 @@ def composite_full_moon(
ImageFilter.UnsharpMask(radius=2, percent=160, threshold=2)
)
# ── Phase shadow (skip when essentially full) ──
illum = moon_phase.illumination(when_utc)
pa = moon_phase.phase_angle(when_utc)
if illum < 0.995:
wax = moon_phase.waxing(when_utc)
ref_resized = _apply_phase_shadow(ref_resized, pa, wax)
# Phase shadow is NOT applied here: the NASA SVS Dial-a-Moon source already
# renders the correct terminator, libration and earthshine for the exact
# hour. Applying _apply_phase_shadow on top would double-darken the unlit
# limb. (_apply_phase_shadow remains available for callers that supply a
# full-moon reference photo rather than a phase-correct NASA render.)
# ── Parallactic-angle rotation ──
par = moon_phase.parallactic_angle(when_utc)