fwt_software/docs/configuration.md

191 lines
11 KiB
Markdown
Raw Permalink 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.

# Configuration & Commands
Behaviour is controlled by three layers: the **`config.ini`** file, **command-line flags** (which override the
config), and **interactive console commands** typed while running.
## Config file resolution
There are no hardcoded paths. The file is resolved in this order ([src/core/Paths.cpp](../src/core/Paths.cpp)):
1. `--config <path>` CLI flag
2. `$FGC_CONFIG` environment variable
3. `./config.ini` (current directory)
4. `<executable dir>/config.ini`
5. `$XDG_CONFIG_HOME/fire_gimbal_control/config.ini` (else `~/.config/...`)
If none exist, the program prints every location it searched and exits. Start from the template:
```bash
cp config/config.example.ini config.ini
```
## `config.ini` keys
Parsed and validated by `ConfigLoader` ([src/core/Config.cpp](../src/core/Config.cpp)) into a typed
`AppConfig`. Invalid types/values fail fast with a clear message.
| Section | Key | Type | Default | Meaning |
|---------|-----|------|---------|---------|
| `General` | `tower_name` | string | `Unnamed` | Tower identity; used in all MQTT topics/payloads |
| `General` | `image_interval` | int > 0 | `5` | Seconds between captures (→ `image_rate = 1/interval`) |
| `General` | `debug` | bool | `false` | Start with debug-level logging |
| `Network` | `zkms_server_ip` | string | `127.0.0.1` | MQTT broker address |
| `Network` | `mqtt_user` / `mqtt_pw` | string | — | MQTT credentials (see secrets below) |
| `Serial` | `device` | string | `/dev/ttyACM0` | Motor-controller serial device |
| `Serial` | `baud` | int | `115200` | Serial baud rate |
| `Camera` | `id_Cam1`..`id_Cam4` | string | — | Camera IDs (GigE IP or USB `DEV_...`); non-empty ones used in order |
| `Paths` | `output_dir` | string | `$XDG_DATA_HOME/fire_gimbal_control/images` | Image output dir; supports `~`/`$ENV` |
| `Features` | `enable_mqtt` | bool | `true` | Use MQTT (vs null channel) |
| `Features` | `enable_camera` | bool | `true` | (reserved) |
| `Features` | `enable_serial` | bool | `true` | (reserved) |
| `Features` | `mock_camera` | bool | `false` | Use the simulated camera |
| `Features` | `mock_serial` | bool | `false` | Use the simulated motor controller |
| `Logging` | `level` | enum | `info` | Linear log level (`--log-level` overrides) |
| `Logging` | `trace` | csv | — | Wire-trace categories, off by default (`--trace` overrides) |
| `UI` | `enable_tui` | bool | `false` | Full-screen terminal dashboard (`--tui`/`--no-tui` override; needs `WITH_TUI=ON`) |
| `Motor` | `yaw_counts_per_deg` / `pitch_counts_per_deg` | float | `983.33` / — | Encoder counts per degree (**calibrate**; may be negative to flip) |
| `Motor` | `yaw_zero_count` / `pitch_zero_count` | int | `500000` / `0` | `xenc` value that = 0° |
| `Motor` | `yaw_min_deg`/`yaw_max_deg`/`pitch_*` | float | `-90`/`90`/… | Soft clamp on commanded degrees |
| `Scan` | `grid_file` | string | — | CSV of `yaw_deg,pitch_deg` waypoints; empty → generate |
| `Scan` | `yaw_intervals` | int | `56` | Generated yaw positions across `[yaw_min_deg, yaw_max_deg]` |
| `Scan` | `yaw_min_deg`/`yaw_max_deg` | float | `-90`/`90` | Generated yaw arc |
| `Scan` | `pitch_levels` | csv | `0` | Generated pitch elevations (deg) |
### `[Motor]` calibration & `[Scan]` grid
The firmware reports only **encoder counts**; `[Motor]` maps them to the heading/elevation degrees
used by MQTT (`target_HDG`) and `CamEvent`. Calibrate `*_counts_per_deg` / `*_zero_count` against
real `xenc` readings after homing (`MOVE` a known angle, read the resulting `xenc`).
The capture **scan grid** is the ordered `(yaw,pitch)` waypoints auto-sweep visits (ping-pong). Set
`[Scan] grid_file` to an editable CSV ([config/scan.csv](../config/scan.csv)) to define exact
coordinates, or leave it blank to generate `yaw_intervals × pitch_levels` points.
Camera index → output subfolder defaults to `RGB`, `ACR`, `NIR` (`CameraConfig::labels`).
### Secrets
`mqtt_user` / `mqtt_pw` are read from the environment variables **`FGC_MQTT_USER` / `FGC_MQTT_PW`** first,
falling back to the config file. Keep credentials out of `config.ini` (which is gitignored anyway) by exporting
them or using a systemd `EnvironmentFile`.
## Command-line flags
Parsed by Boost.Program_options ([main.cpp](../main.cpp)). Flags override `[Features]`.
| Flag | Effect |
|------|--------|
| `-h, --help` | Show help and exit |
| `-c, --config <path>` | Explicit config file path |
| `-i, --init` | Run the endstop-finding init sequence before the loop |
| `-s, --start` | Start capture automatically |
| `-d, --demo` | Demo mode: copy the placeholder image instead of encoding |
| `--no-mqtt` | Disable MQTT (use the null channel) |
| `--mock-camera` | Use the simulated camera |
| `--mock-serial` | Use the simulated motor controller |
| `--tui` | Show the full-screen terminal dashboard (overrides `[UI] enable_tui`) |
| `--no-tui` | Force the headless line console (overrides config; wins over `--tui`) |
| `--log-level <lvl>` | `trace`/`debug`/`info`/`warn`/`error`/`off` |
| `--trace <cats>` | Verbatim wire trace; comma list `serial,mqtt,camera,control,all,none` |
Typical headless dev run: `scripts/run.sh --mock-serial --mock-camera --no-mqtt --start`.
### Init sequence (`--init`)
Sends `ENABLE Y`, `ENABLE P`, `HOME`, then polls telemetry until both axes report `READY`
(firmware `ST` state `A`), bounded by the firmware's 60 s homing timeout, then sets `SPEED Y/P`
([src/core/Application.cpp](../src/core/Application.cpp), `runInitSequence`).
## Interactive console commands
Lines on stdin are parsed by `parseCommand` ([src/core/CommandParser.cpp](../src/core/CommandParser.cpp)) — a
whitespace tokenizer (`<verb> [device] [option] [value]`) that replaced the old fragile Boost.Spirit grammar.
Handled in `Application::Impl::handleCommand`.
| Type this | Meaning |
|-----------|---------|
| `start` | Start camera acquisition + capture |
| `stop` | Stop acquisition |
| `debug` | Toggle debug logging |
| `trace <cat> [on\|off]` | Toggle a wire-trace category (`serial`/`mqtt`/`camera`/`control`) |
| `trace all` / `trace off` | Enable every category / silence all |
| `set camera jxlq <v>` | JPEG XL distance (0 = lossless) |
| `set camera jxle <v>` | JPEG XL effort |
| `set camera display <0\|1>` | Toggle OpenCV preview window |
| `set camera fps <v>` | Camera acquisition frame rate (real camera only) |
| `set fps <v>` | Capture interval rate (images/second) |
| `set motorctl <cmd>` | Forward a raw command to the motor controller (e.g. `set motorctl MOVE Y 20000`) |
| `exit` | Quit (Ctrl-D also works) |
## Terminal dashboard (TUI)
An **optional** full-screen interface (`--tui`, or `[UI] enable_tui = true`) renders the tower
status as sectioned, colored panels updated in place, with a scrolling log pane and a nano-style
key bar. It is built on [FTXUI](https://github.com/ArthurSonzogni/FTXUI) (fetched via
[cmake/Ftxui.cmake](../cmake/Ftxui.cmake) when `WITH_TUI=ON`, the default) and is **fully decoupled
from application logic**: the control loop publishes a plain `UiSnapshot`
([include/fgc/ui/UiSnapshot.h](../include/fgc/ui/UiSnapshot.h)) that the UI renders, and the UI
forwards keystrokes/typed commands back through the same command queue the console uses. Headless
operation is unchanged and remains the default — the same binary runs under systemd/ssh/pipes with
logs on stdout.
Panels (MVP): **Gimbal** (per-axis state, heading, encoder counts, flag badges, target),
**Sensors** (DHT11 + Xsens MTi — shown as *pending integration* until those drivers land),
**Camera** (count, capture state, rate, last capture), **Connectivity** (MQTT state, broker, tower,
control mode, target heading). Adding a panel later (e.g. computer vision) is a struct in
`UiSnapshot.h` plus one node in [src/ui/TuiUi.cpp](../src/ui/TuiUi.cpp).
Keys (shown in the bottom bar): `s` start · `x` stop · `h` home · `r` reset · `:` open a command
line (any console/`set motorctl …` command) · `q` quit. Plain letters are used rather than Ctrl
chords so terminal flow-control (`Ctrl-S`/`Ctrl-Q` XON/XOFF) can't swallow them. In TUI mode all log
output is diverted from stdout into the on-screen log pane via a `Logger` sink, so the screen is
never corrupted.
Build without it (`-DWITH_TUI=OFF`) for a smaller, dependency-free binary; `--tui` then warns and
runs headless.
## Logging: level vs. wire-trace categories
Two **independent** controls, each settable via config (`[Logging]`), CLI, and a console command:
- **Linear level** — `[Logging] level`, `--log-level`, console `debug`. Filters ordinary messages
(`trace<debug<info<warn<error<off`). Default `info`; an active capture is nearly silent at `info`.
- **Wire-trace categories** `[Logging] trace`, `--trace`, console `trace`. Each enabled category
(`serial`, `mqtt`, `camera`, `control`) logs **every** message exchanged with that subsystem,
verbatim. They are **off by default** and **independent of the level**: `--trace serial` prints all
firmware serial traffic even at `info`. Only level `off` silences them. The `camera` category is
high-rate (one line per frame, metadata only no pixel data).
Precedence at startup: config applies first, then CLI overrides (`--trace` replaces the config set,
it does not merge). At runtime the `trace` console command edits categories incrementally.
Trace lines carry a category tag and a `TX`/`RX` direction so one subsystem is easy to follow/grep:
```
[SERIAL] TX MOVE -90,0 (command to firmware: yaw,pitch counts)
[SERIAL] RX ST Y:A,982,969,80084000,0,8,8,Se P:A,... (status from firmware)
[MQTT] PUB GGS/FWT/Tower/StatusCode 0 / [MQTT] RX GGS/FWT/Tower/target_HDG 180
[CAMERA] TX trigger cam0 / [CAMERA] RX frame cam0 1936x1216 7064576B
[CONTROL] sweep -> grid yaw=-90 pitch=0 (scheduler decisions + inbound console commands)
```
## Motor command vocabulary (emitted by the software)
The firmware speaks **full-word, newline-terminated** commands in absolute **encoder counts**
(see `../firmware/docs/protocol.md`). The host converts degreescounts via the `[Motor]` calibration.
| Command | When | Meaning |
|---------|------|---------|
| `ENABLE Y` / `ENABLE P` | init | energize the axis coils |
| `HOME` | init | home all axes (firmware runs it non-blocking; watch `ST` state `R→H→A`) |
| `SPEED Y\|P <vel>` | init | set the production move speed (VMAX, counts/s) |
| `MOVE <yaw>,<pitch>` | each capture point | drive both axes to absolute counts (combined form) |
| `MOVE Y\|P <pos>` | | single-axis absolute move (`set motorctl`) |
| `STOP Y\|P\|ALL` | | ramp to a controlled stop |
Capture is a **move → settle → trigger** cycle: the scheduler issues a `MOVE`, waits until both
axes report standstill at the target, then triggers the cameras. **ControlCode 0** walks the scan
grid (ping-pong); **ControlCode 1** drives yaw to `target_HDG` (pitch held). Telemetry arrives as
firmware `ST` lines (per-axis state + encoder counts), parsed by
[TelemetryParser](../src/core/TelemetryParser.cpp).