Skip to content

Hybrid alignment: SEP fallback serves in-progress alignments (stacked on #566) - #567

Draft
hjoungjoo wants to merge 6 commits into
brickbots:mainfrom
hjoungjoo:pr/cedar-sep-hybrid-align
Draft

Hybrid alignment: SEP fallback serves in-progress alignments (stacked on #566)#567
hjoungjoo wants to merge 6 commits into
brickbots:mainfrom
hjoungjoo:pr/cedar-sep-hybrid-align

Conversation

@hjoungjoo

Copy link
Copy Markdown

Stacked on #566 — this branch is #566 plus one commit; only the last commit (feat(solver): hybrid alignment) is new here. Review #566 first; this PR will rebase to a one-commit diff once it lands.

What this adds

#566 deliberately excludes in-progress alignments from the SEP rescue: alignment resolves through the production frame only. But the sky where the rescue matters is exactly the sky where cedar cannot complete an alignment at all — under heavy light pollution, a user cannot align, which blocks push-to entirely.

This PR lifts that exclusion:

  • When an alignment is pending and the production solve fails, the SEP fallback solve now receives the target_sky_coord.
  • tetra3 answers with y/x_target in the rotated full-frame canvas; map_frame_pixel_to_target (the exact inverse of the centre-scale relation Hybrid solving: SEP full-frame rescue when cedar-detect finds no stars (light-polluted skies) #566's equivalence test proves) maps it back into rotated-512 space.
  • The downstream alignment chain (AlignedResult, persisted target_pixel) consumes the result unchanged — it cannot tell which detector served the alignment.

Cedar keeps absolute priority: this path only runs on attempts where the production solve already failed.

Proof

The solve-equivalence test extends to the alignment direction: solving the same target_sky_coord through the production 512 path and the SEP full-frame path must land on the same 512-space target pixel within 1 px (~84″ at this plate scale). Verified against tetra3's own catalog stars projected onto a synthetic sensor.

Field: alignment through the SEP path was confirmed working on the fielded imx462 device under a light-polluted Seoul sky (2026-07-28 session — the first time alignment succeeded at all under that sky). Precision validation on a real telescope + push-to chain is listed as remaining work in the field notes.

Testing

🤖 Generated with Claude Code

hjoungjoo and others added 6 commits August 2, 2026 00:20
sep is the C Source Extractor library behind the SEP fallback detector
(next commits). Optional at runtime: every import site degrades to the
existing cedar-only behaviour when the module is missing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… mapping

Two self-contained modules, no wiring yet:

* sep_detect: detects stars on the UNCROPPED 12-bit raw frame -- 2x2
  binning, sep mesh-background subtraction (removes light-pollution
  gradients / cloud glow that defeat a global threshold), matched-filter
  extraction, then field-measured quality gates: edge margin (vignette),
  saturation guard, point-source shape gate (semi-major axis / npix;
  cloud texture is extended, stars are compact), isolation gate (sep
  deblends bright cloud edges into tight clumps; real stars at this
  plate scale measured zero neighbours within 50 px), and an optional
  static warm-pixel mask (single-pixel sensor defects dominated
  empty-sky counts in field data).

* solver_frame_map: maps between the production rotated-512 solver
  frame and the rotated full-sensor frame. Because the crop is centred
  and the resize isotropic, target_pixel mapping reduces to a scale
  about the frame centre. Rotation conventions are pinned by tests
  against PIL Image.rotate; the stage-5 rule reads camera_interface's
  SCREEN_ROTATE_AMOUNTS as its single source.

sep stays optional: detect_stars returns None when the module is
missing, callers degrade to cedar-only behaviour.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sep_warm_map builds the static warm-pixel map sep_detect masks with:
same-channel neighbour excess recurring at a fixed position across many
dark frames (stars move with the sky, defects do not). Thresholds were
validated on a field corpus: the map covered every recurring empty-sky
detection cell while masking ~0.14% of the frame.

The equivalence test projects tetra3's own catalog stars onto a
synthetic full sensor and solves the same sky through both paths
(production crop-512 vs rotated full frame); Roll must agree to 0.05
deg and camera/aligned pointing to a fit residual -- the proof that
detecting on a different frame cannot disturb tracking or push-to.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g-gated)

When solver_shadow_detect or solver_sep_fallback is enabled, the Pi
camera publishes each exposure's UNCROPPED raw mosaic (profile rotation
applied, crop skipped) alongside the existing cropped cam_raw, with the
frame's actual exposure/gain metadata. The solver-side SEP path detects
on this frame; the production 512 pipeline is untouched.

Both keys default to off, in which case no extra copy or publish
happens -- captures are byte-for-byte the current behaviour.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iled solves

The production path is unchanged and keeps priority: every attempt still
runs cedar-detect on the processed 512 frame first. When that solve
fails and the SEP full-frame detector found enough stars (>= 5), the
attempt is rescued by solving the SEP centroids in the rotated full
frame; the solution carries production 512-frame semantics (Roll,
target_pixel pointing) via solver_frame_map, so the pointing chain
downstream cannot tell which detector solved.

Extras:
* Exponential backoff on failed rescues (a failed fallback burns up to
  solve_timeout of CPU; indoors/thick cloud would pay it every
  attempt), re-armed instantly when the SEP count jumps 1.5x -- a cloud
  gap opening on stars is never delayed.
* Shadow CSV (solver_shadow_detect): one row per attempt comparing both
  detectors, for offline A/B without affecting the solve.
* In-progress alignments are excluded from the rescue: alignment
  resolves through the production frame only (a follow-up PR maps the
  alignment coordinate through the SEP frame as well).

Both config keys default to off; with them off no SEP code runs and no
full frame is published.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gnments

Lifts the alignment exclusion from the SEP rescue: when an alignment is
pending and the production (cedar) solve cannot complete -- exactly the
sky this path exists for -- the SEP solve now resolves the alignment
coordinate. tetra3 answers target_sky_coord in the rotated full-frame
canvas; the y/x_target it returns is mapped back into rotated-512 space
(map_frame_pixel_to_target, the inverse of the proven centre-scale
relation), so the downstream alignment chain (AlignedResult, persisted
target_pixel) consumes it unchanged and cannot tell which detector
served it.

The equivalence test extends to the alignment direction: solving the
same target_sky_coord through both paths must land on the same
512-space pixel within 1 px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant