Files
dwarf-go/dwarfctl
Jacquin Antoine e8ae644d58 docs: add visual odometry technical guide
Cover phase correlation pipeline, log-polar rotation estimation,
de-rotation pass, Tracker integration, motion-blur handling, FoV
calibration, and limitations.

💘 Generated with Crush

Assisted-by: Crush:/models/Qwen3.6-27B-uncensored-heretic-v2-Native-MTP-Preserved-Q4_K_M.gguf
2026-07-13 23:10:14 +02:00
..

dwarfctl — Open-source DWARF telescope control CLI

A Go command-line tool for controlling DWARF II (and upcoming DWARF III) smart telescopes over Wi-Fi via their WebSocket API.

Built from reverse-engineered protocol specs (see ../analysis/).

Quick start

make build
./dwarfctl --ip 192.168.1.100 state

Prerequisites

The telescope must be connected to your Wi-Fi (or your phone joined to the telescope's AP hotspot). Once you can ping the telescope's IP, dwarfctl can reach it.

Find the IP from:

  • The official DWARFLAB app's Settings → My Device
  • Your router's DHCP table (hostname like DWARFxxxx)
  • The BLE handshake (see ../analysis/API_REFERENCE.md)

Usage examples

# Device state
dwarfctl --ip 192.168.1.100 state

# Camera
dwarfctl camera open --cam tele
dwarfctl camera photo --cam tele
dwarfctl camera photo --cam wide --raw
dwarfctl camera burst start --cam tele
dwarfctl camera exp --cam tele 156       # 1/4s exposure
dwarfctl camera gain --cam tele 60
dwarfctl camera params --cam tele

# Motor / slew
dwarfctl motor slew 90 0.5               # angle 90°, half speed
dwarfctl motor stop 0                     # stop RA motor
dwarfctl motor goto 0 45.0 5.0           # RA motor to 45° at speed 5

# Astrophotography
dwarfctl astro calibrate start
dwarfctl astro goto-dso 10.6847 41.2687 "M31 Andromeda"
dwarfctl astro goto-solar 3              # Earth = index 3
dwarfctl astro stack start
dwarfctl astro eq-solve start            # polar alignment

# Focus
dwarfctl focus auto
dwarfctl focus step 1                     # 1=out, 0=in
dwarfctl focus astro-af start

# Tracking
dwarfctl track start 640 360 100 100 0   # bbox at center of tele cam
dwarfctl track stop

# System
dwarfctl system sync-time
dwarfctl system set-location 48.8566 2.3522 35

# Power
dwarfctl power rgb-on
dwarfctl power rgb-off
dwarfctl power reboot
dwarfctl power off

# Monitor notifications (live status events)
dwarfctl monitor

# Visual odometry — image-based orientation (no star/plate solving)
dwarfctl orient compare before.jpg after.jpg   # measure rotation between 2 frames
dwarfctl orient live --cam wide                 # live pointing tracker from RTSP
dwarfctl orient live --fov-h 42 --fov-v 24      # override FoV with measured values

Architecture

dwarfctl/
├── proto/dwarf.proto          # unified proto3 definitions (397 messages)
├── proto/dwarf.pb.go          # generated Go bindings (24948 lines)
├── internal/
│   ├── transport/client.go    # WebSocket client + WsPacket envelope
│   ├── api/
│   │   ├── commands.go        # 323 command IDs + module routing
│   │   └── client.go          # typed Telescope API (camera/motor/astro/...)
│   └── odometry/              # FFT phase-correlation visual odometry
│       ├── fft.go             # radix-2 Cooley-Tukey FFT (1D + 2D)
│       ├── odometry.go        # Estimate() — shift between 2 frames → degrees
│       └── tracker.go         # Tracker — cumulative orientation integrator
└── cmd/dwarfctl/main.go       # cobra CLI

Protocol summary

Layer Transport Port
Control WebSocket (binary protobuf) 9900
Preview RTSP (not implemented here) 554

Every command is a serialized WsPacket{major=2, minor=3, device_id, module_id, cmd, type, data, client_id}. The module_id is derived from cmd by range. See ../analysis/API_REFERENCE.md for details.

Regenerating protos

If the proto definitions change:

# From the APK analysis directory:
python3 ../analysis/extract_protos.py ../extracted/jadx/.../proto ../analysis/protos

# Merge into unified proto:
cd dwarfctl/proto
python3 merge.py . dwarf.proto
protoc --go_out=. --go_opt=paths=source_relative dwarf.proto

Roadmap

  • Visual odometry — image-based orientation via FFT phase correlation (orient)
  • BLE discovery + handshake (DwarfPing/DwarfEcho)
  • RTSP preview viewer
  • Interactive REPL mode
  • Schedule plan management
  • OTA firmware update
  • Full Notify event parsing (typed)
  • Closed-loop tracking: feed orient output into motor corrections

Visual odometry (orient)

The orient command estimates the telescope's pointing rotation by comparing wide-angle frames using FFT phase correlation. It does NOT use star identification or plate solving — it works on any textured scene (sky, horizon, clouds, daytime landscape), making it suitable for:

  • Daytime airplane tracking — the wide cam sees sky/horizon texture
  • Nighttime satellite tracking — the wide cam sees star fields as texture

How it works

  1. Grab two wide-angle frames (via RTSP ffmpeg grab, same as preview grab)
  2. Downscale both to a square grid (default 256×256, power of two)
  3. Compute the 2-D FFT of each, then the normalized cross-power spectrum
  4. The inverse FFT gives a correlation surface; its peak = image-plane shift
  5. Sub-pixel refinement via parabolic interpolation
  6. Convert pixel shift → degrees using the camera field of view

For a static alt-az mount, the accumulated shifts give the cumulative pointing orientation relative to the starting frame — no absolute encoders or polar alignment needed.

Field of view calibration

The default FoV (8.0°×6.5°) comes from the DWARF Mini's display values in DEVICE_MODELS.md, but the firmware-reported live FoV can differ. To calibrate: slew the motors by a known angle and compare with the measured rotation. Override with --fov-h / --fov-v once you have measured values.

Prerequisites

  • ffmpeg must be installed (for RTSP frame capture)
  • The wide camera must be opened first (camera open --cam wide) — orient live does this automatically.