Comprehensive documentation for accessing the DWARF camera streams:
- RTSP URLs and channels (ch0=tele, ch1=wide)
- The critical prerequisite: camera must be opened via WebSocket first
- ffmpeg commands for frame capture, timelapse, and video recording
- mpv and VLC usage with TCP transport
- Python/OpenCV integration example
- Troubleshooting common issues (black image, connection refused, VLC delay)
- Comparison of RTSP vs MJPEG modes across device models
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
170 lines
5.7 KiB
Markdown
170 lines
5.7 KiB
Markdown
# 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
|
||
│ ├── 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:
|
||
|
||
```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.
|