Files
dwarf-go/dwarfctl/README.md
Jacquin Antoine 8da878cdd2 docs: add daemon mode section to README
Cover serve command, HTTP API endpoints, daemon client usage, and
systemd deployment. Update architecture tree with new modules.

💘 Generated with Crush

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

253 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```bash
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
```bash
# 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 + heartbeat
│ ├── api/
│ │ ├── commands.go # 323 command IDs + module routing
│ │ └── client.go # typed Telescope API (camera/motor/astro/...)
│ ├── daemon/ # persistent server with HTTP control API
│ │ ├── server.go # WebSocket + camera + odometry manager
│ │ └── client.go # thin HTTP client for the daemon
│ ├── rtspgrab/ # RTSP frame capture via ffmpeg
│ └── odometry/ # FFT phase-correlation visual odometry
│ ├── fft.go # radix-2 Cooley-Tukey FFT (1D + 2D)
│ ├── odometry.go # Estimate() — translation + rotation
│ ├── rotation.go # log-polar phase correlation (rotation only)
│ ├── rotate.go # image rotation for de-rotation pass
│ └── tracker.go # Tracker — cumulative orientation integrator
└── cmd/dwarfctl/main.go # cobra CLI
```
## Daemon mode
The `serve` subcommand runs dwarfctl as a **persistent background service** that
keeps the WebSocket connection, camera, and odometry tracker alive — exposing a
local HTTP API for short-lived clients to query state or issue commands without
reconnecting each time.
```bash
# Start the daemon (connects to telescope, opens camera, starts odometry loop)
dwarfctl serve --ip 192.168.88.1 --cam wide
# With custom settings
dwarfctl serve --ip 192.168.88.1 --cam wide --addr 127.0.0.1:7777 \
--interval 5s --fov-h 8.0 --fov-v 6.5
```
### How it works
1. Connects to the telescope WebSocket (port 9900) and keeps it alive with
periodic ping/pong heartbeats
2. Switches to shooting mode 1 (photo/preview), then opens the camera — this
activates the RTSP server
3. Starts the odometry loop: grabs frames at the configured interval, feeds
them to the Tracker, logs cumulative orientation
4. Serves the HTTP API on the configured address until SIGTERM/SIGINT
The odometry loop **auto-pauses** during motor slew (to avoid motion blur)
and resumes after a settle delay (default 3 s). You can also pause and
resume it manually via the HTTP API.
### HTTP API
All endpoints are relative to the daemon address (default `http://127.0.0.1:7777`).
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/health` | Daemon status, IP, camera state |
| `GET` | `/orientation` | Cumulative pan/tilt/rotation in degrees |
| `GET` | `/grab?path=/tmp/x.jpg&raw=1` | Capture RTSP frame (returns path or raw JPEG) |
| `POST` | `/reset` | Zero the odometry tracker |
| `POST` | `/camera?action=open&cam=wide` | Open or close a camera |
| `POST` | `/slew` | Slew joystick (`{"angle":90,"length":0.5}`) |
| `POST` | `/stop` | Stop all motors |
| `GET` | `/pause` | Pause odometry frame capture |
| `GET` | `/resume` | Resume odometry frame capture |
| `POST` | `/fov` | Override FoV (`{"h":8.0,"v":6.5}`) |
### Using the daemon client
The `--daemon` flag points CLI commands at a running daemon instead of connecting
directly to the telescope:
```bash
# Terminal 1: start daemon
dwarfctl serve --ip 192.168.88.1
# Terminal 2: query orientation via daemon
dwarfctl --daemon 127.0.0.1:7777 state
# Or use curl directly
curl http://127.0.0.1:7777/orientation
curl http://127.0.0.1:7777/health
curl -X POST http://127.0.0.1:7777/slew -d '{"angle":90,"length":0.3}'
```
### systemd deployment
A systemd unit is provided in `deploy/dwarfctl.service`. Install it with:
```bash
sudo cp deploy/dwarfctl.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now dwarfctl
```
Edit the unit to adjust `--ip`, `--cam`, and `--addr` to match your setup.
### 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:
```bash
# 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
- [x] **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.