Skip to content

[testable] Full-sensor plate solving + cached optical calibration - #33

Closed
mrosseel wants to merge 2 commits into
nixosfrom
test/fullframe-solve
Closed

[testable] Full-sensor plate solving + cached optical calibration#33
mrosseel wants to merge 2 commits into
nixosfrom
test/fullframe-solve

Conversation

@mrosseel

Copy link
Copy Markdown
Owner

Device-testable build of the full-frame solver work (upstream PR brickbots#554), merged onto nixos so it can actually be built and installed. Not for merge — this exists to produce an installable image.

What to expect on the device

Two changes, both in the solver:

Full-sensor solving. The camera cropped every exposure to a centred square before solving — on the imx462, 1920×1080 down to 980×980, discarding roughly half the sensor. The camera now publishes two frames per exposure: the 512×512 display frame (unchanged — UI, focus, preview, align, SQM) and a full-sensor solve frame at native scale, used only by the plate solver. Bayer sensors bin 2×2 for it.

Cached optical calibration. The solver passed a fixed fov_estimate=12.0, fov_max_error=4.0 and never passed a distortion estimate at all. The first successful solve of a run now captures the measured FOV and lens distortion; later solves get a ±0.5° window and that distortion as a starting point.

What to check

Check Expected
SQM value unchanged — the main regression risk, since solve centroids now route through a projection before reaching photometry
Solved FOV ~11.2° on imx462 (was ~10.2°) — confirms full frame actually engaged
Matches at the same exposure should go up
Alignment put a star on the reticle; confirm it still lands correctly
Preview overlay centroids still sit on stars
Startup log Full-frame solving enabled: solve frame 540x960, then Optical calibration: FOV … distortion …

Escape hatch: solver_full_frame: false in config reverts to the old crop path without reflashing, so you can A/B on the device.

Note the horizontal FOV grows only modestly — after the camera's quarter turn the solve frame's horizontal axis is the sensor's short side, which the square crop already spanned. Total area still roughly doubles; the extra sky arrives vertically.

Status

Never run on real camera hardware. capture_pair, the 2×2 Bayer bin, and the geometry mapping all get their first real exercise here — that is the point of this build.

Verified in CI terms: ruff, mypy and 1202 unit+smoke tests pass. Benchmarked over 220 archived sweep frames (imx462 + HQ): doubling the solve frame's pixel count costs −0.02 ms in cedar-detect, i.e. nothing measurable; total extract+solve stays 0.6–8% of one exposure cycle.

Best target is pifinder-mr2 (imx462): biggest area gain, and it exercises the new Bayer bin path.

Merge conflicts resolved here, worth a glance

  • camera_pi.py — the full-frame refactor splits capture() into _read_raw/_display_frame, which separated the per-frame optical-black measurement from the radiometer sample that consumes it. Threaded via self.last_optical_black; ruff caught the break.
  • camera_interface.py — kept both test_mode_on and solve_frame initialisation.
  • test_full_frame_solve.py — tetra3 is a submodule on some branches and a uv dependency here; the path entry is now conditional.

mrosseel added 2 commits July 29, 2026 10:08
…tion

The camera cropped every exposure to a centred square before solving, throwing
away roughly half the sensor area (imx462: 1920x1080 down to 980x980). Cedar
Solve has no trouble with the whole frame, so that discarded area is free
plate-solving sensitivity.

The camera now publishes two frames per exposure: the 512x512 display frame,
unchanged, and a solve frame covering the whole sensor at native scale. Bayer
sensors are binned 2x2 for the solve frame, which removes the RGGB modulation
the old downscale to 512x512 used to smooth away.

Everything downstream of the solver -- SQM photometry, the preview overlay, the
alignment marker -- is written against the display frame, and SQM in particular
is calibrated to its plate scale. So solves are projected back onto the display
frame in one place (project_solution_to_display), keeping those consumers
unchanged. SolveGeometry owns the mapping, composed from the affine transform of
each pipeline stage.

The solver also stopped rediscovering the optics on every frame. It passed a
fixed 12 +/- 4 degree window and never passed a distortion estimate at all. The
first successful solve of a run now captures the measured FOV and lens
distortion; later solves get a +/-0.5 degree window and the measured distortion
as a starting point, which speeds up pattern matching and improves centroid
matching towards the frame corners -- the part of the image the crop used to
discard. A run of failures discards the calibration and reopens the window.

Full frame is on by default and can be turned off with solver_full_frame. It is
disabled automatically when the camera has no sensor profile, or when
camera_rotation is set to something other than a quarter turn, since rotating a
non-square frame by an arbitrary angle would clip the corners.
@mrosseel mrosseel added the testable Builds the PR and publishes it to the unstable update channel label Jul 29, 2026
@mrosseel

Copy link
Copy Markdown
Owner Author

Wrong repo — the device reads brickbots/PiFinder@nixos-manifest, so a build published to mrosseel's manifest is never visible to it. Reopening against brickbots/main with the same head.

@mrosseel mrosseel closed this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

testable Builds the PR and publishes it to the unstable update channel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant