Quality Screening

Screen light frames for problems that ruin integrations but slip past conventional grading: occlusion from trees or a dome edge, small clouds, thin veils, errant light, static glow, off-target pointing, tracking loss, and meaningful plate-solve failures. Spatial detections can be rendered as annotated diagnostics; astrometry results carry solved-center and field-relative evidence.

All detections are classical statistics. Thresholds were calibrated against real sessions (measured clean-frame envelopes across multiple nights and filters) and each detector carries regression tests pinning its behavior.

Why global metrics are not enough

Star count and HFR — what conventional graders use — barely move when a frame is partially ruined. Measured on a real session where a tree line progressively occluded the field:

The screening stack answers with signals that are local (per grid cell), photometric (flux ratios, not counts), and temporal (each cell compared to its own history) — with baselines that refuse to normalize slow-growing problems away.

The detection stack

SignalCatchesHow
Dead cellsOcclusion (trees, dome, dew shield) Fraction of 8×6 grid cells whose star density collapsed vs the frame's own median cell
TransparencyThin uniform veils Median flux ratio of stars matched against a per-sequence reference catalog; 0.7 = whole frame ~0.4 mag dimmer
Localized extinctionSmall clouds Per-cell flux ratios ÷ global transparency; a passing cloud is a coherent dip in one patch of stars
Star-share dropsSmall opaque clouds Each cell's share of the frame's stars vs that cell's own temporal median (Poisson-aware)
Background riseErrant light (headlights, flashlights) Per-cell background vs the cell's own history, after subtracting the frame's gradient (robust plane fit)
Background fallDark occluders, cloud shadow Same, downward: something blocking skyglow reads darker, not milky
Static glowCorner haze, lit occluder edges Cells brighter than the frame's own gradient model — catches problems present from a session's first frame, which temporal baselines can never see
Fresh plate solutionOff-target frames, pointing jumps/drift, deterministic no-solves Seiza solves the current pixels; solved centers are compared with the authoritative target, stable framing clusters, and within-segment drift

The signals feed a sequence analyzer that scores every frame 0–1 relative to its session (same target, filter, and exposure; sessions split on 60-minute gaps) and classifies the likely cause. Verdicts: OK, WARN (recoverable or review-worthy — e.g. gradients that flat-ish processing can remove, glow that stacks into artifacts), REJECT (clouds and occlusion).

Quick start

In the app
Open a target's Sequence view and choose Scan Quality

Results appear on the affected frames and stay cached across restarts.

Sequence view →

The selectors focus Clouded, Off Target, Unsolved, or all Recommended frames. PSF Guard opens a review before it writes a rejection. See Astrometry Quality.

CLI / NAS
Screen folders in a batch or without a database

Add --regrade-db only for target-aware checks and grade proposals.

CLI reference →
# Screen a night of lights (no database needed)
psf-guard screen-fits "/path/to/2026-06-30/LIGHT"

# Render an annotated diagnostic PNG for every WARN/REJECT frame
psf-guard screen-fits "/path/to/LIGHT" --annotate /tmp/diagnostics

# Add target-aware astrometry and propose/write supported [Auto] rejections
# (dry-run first; frames matched by filename AND capture timestamp)
psf-guard screen-fits "/path/to/LIGHT" --regrade-db my-db-slug --dry-run
psf-guard screen-fits "/path/to/LIGHT" --regrade-db my-db-slug

# Then archive the rejected files out of your stacking tree
psf-guard move-rejects --db my-db-slug

When orbital elements are already cached, the same quality scan projects satellite crossings and tests a narrow pixel corridor around each path. Prediction-only candidates warn; only a pixel-aligned high-risk trail can recommend rejection. See Satellite Tracks.

Reading the diagnostics

--annotate renders each flagged frame with the analysis grid overlaid. Cells are marked by the signal that fired; the caption strip carries the verdict, score, per-frame metrics, and the classifier's explanation.

MarkingMeaning
red filldead cell — star density collapsed (occlusion)
orange filllocalized extinction — stars dimmed (small cloud), labeled with the cell's flux ratio
magenta filltransient drop in the cell's share of stars
yellow bordertransient background rise (errant light)
blue bordertransient background fall (dark occluder / cloud shadow)
cyan fillstatic glow above the frame's own gradient model

Occlusion arriving

Occlusion onset: red cells trace the tree line
25% of the field's cells have lost their stars; the red region traces the visible out-of-focus occluder exactly. Transparency is 1.01 — the surviving field is photometrically perfect, which is why global metrics miss frames like this.

Heavy occlusion with a stray-lit edge

Heavy occlusion with a yellow-bordered stray-lit cell
Half the field is dead and a yellow border marks a cell whose background rose above its own temporal baseline — the occluder's stray-lit edge bleeding into a live cell.

The advancing frontier

Blue cells darker than history, yellow lit fringe
Late in the same session: blue borders mark cells reading darker than their own history (the dark occluder blocking skyglow as it advances), yellow marks its lit fringe.

Thin cloud veil — pure photometry

Clean frame: 2973 stars, transparency 1.05
Clean frame: 2,973 stars, transparency 1.05.
Veiled frame: 1417 stars, transparency 0.63, REJECT
Same field 13 minutes later: 1,417 stars, transparency 0.63 (~0.5 mag of uniform extinction). REJECT.

No cell is tinted because nothing is locally wrong — only the matched-star flux ratios see it. This frame was Accepted by conventional grading.

Static corner glow

Cyan cells sitting on corner haze at 4.7% above the gradient plane
A haze present from the session's first frame — every temporal detector is structurally blind to it. The static glow signal compares each cell against the frame's own gradient model instead: the cyan cells sit exactly on the haze at 4.7% above the plane.

Tuning

Defaults were calibrated against measured clean-frame envelopes (42+ frames, 4 nights, multiple filters). The main knobs:

KnobDefaultNotes
--min-score0.35Composite score below which a frame is rejected
--dead-cell-rise0.08Occlusion onset sensitivity; clean-frame jitter is ≤0.04, so 0.08 is a 2× margin
--session-gap60 minSplits sequences into sessions
glow threshold2.5% of sky and >30 ADUThe ADU floor keeps real narrowband nebulosity (measured 19–22 ADU) from false-flagging; true haze measured 48–103 ADU. Rig-specific.
transparency threshold0.80Global veil rejection level

Safety properties

Limitations

The full technical document lives in the repository: docs/SCREENING.md.