Improve odometry with rotation estimation, daemon stability, and RTSP fixes

- Add rotation estimation via log-polar phase correlation (EstimateRotation,
  rotateGray) — de-rotate before translation to avoid aliasing
- Track cumulative field rotation in Tracker, reject low-confidence frames
- Extract RTSP grabber into internal/rtspgrab module
- Daemon: pause odometry during slew (motion blur), auto-resume after settle
  delay, add /pause and /resume HTTP endpoints, switch shooting mode before
  opening camera, use ReqOpenCamera with rtsp_encode_type=1
- WebSocket heartbeat (ping/pong every 10s) to prevent idle disconnects
- FFmpeg low-latency flags (-fflags nobuffer, -probesize, -analyzeduration)
- Add SendRaw API for custom payloads, increase daemon HTTP timeout to 60s

💘 Generated with Crush

Assisted-by: Crush:/models/Qwen3.6-27B-uncensored-heretic-v2-Native-MTP-Preserved-Q4_K_M.gguf
This commit is contained in:
Jacquin Antoine
2026-07-13 23:00:30 +02:00
parent 8daa9b4d58
commit d319ef6b4a
12 changed files with 522 additions and 143 deletions

View File

@ -19,20 +19,19 @@ import (
"os"
)
// Result holds the rotation estimated between two frames.
// Result holds the motion estimated between two frames.
type Result struct {
// PanPx / TiltPx are the image-plane shift (in resized pixels) of the scene
// from frame 1 to frame 2. Positive PanPx = scene moved right; positive
// TiltPx = scene moved down. To point back at the original target, slew the
// motors in the opposite direction.
// PanPx / TiltPx are the image-plane translation (in resized pixels).
PanPx float64
TiltPx float64
// PanDeg / TiltDeg convert the pixel shift to degrees using the supplied FoV.
PanDeg float64
TiltDeg float64
// Confidence in [0,1]: the normalized phase-correlation peak height. Higher
// is better; below ~0.05 the frames are likely too flat or the motion too
// large to be trusted.
// RotDeg is the estimated field rotation (degrees). Positive = scene rotated
// counter-clockwise. Dominant when the telescope points near zenith on an
// alt-az mount (azimuth rotation → field rotation, not translation).
RotDeg float64
// Confidence in [0,1].
Confidence float64
// Size is the FFT grid dimension actually used.
Size int
@ -78,13 +77,31 @@ func Estimate(img1, img2 image.Image, fovXDeg, fovYDeg float64, size int) (Resul
g1 := toGrayDownscaled(img1, size, size)
g2 := toGrayDownscaled(img2, size, size)
dx, dy, conf := phaseCorrelation(g1, g2, size, size)
// Pass 1: estimate rotation (log-polar phase correlation).
rotDeg, confRot := EstimateRotation(g1, g2, size, size)
// Pass 2: de-rotate g2, then estimate translation on de-rotated images.
// This removes the rotation-induced aliasing that corrupts translation.
var dx, dy, confTrans float64
if absf(rotDeg) > 0.3 {
g2Derot := rotateGray(g2, size, rotDeg)
dx, dy, confTrans = phaseCorrelation(g1, g2Derot, size, size)
} else {
dx, dy, confTrans = phaseCorrelation(g1, g2, size, size)
}
// Use the higher-confidence metric as the overall confidence.
conf := confTrans
if confRot > conf {
conf = confRot
}
r := Result{
PanPx: dx,
TiltPx: dy,
PanDeg: dx * fovXDeg / float64(size),
TiltDeg: dy * fovYDeg / float64(size),
RotDeg: rotDeg,
Confidence: conf,
Size: size,
}