fwt_software/docs/configuration.md

11 KiB
Raw Blame History

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):

  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:

cp config/config.example.ini config.ini

config.ini keys

Parsed and validated by ConfigLoader (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) 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). 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, runInitSequence).

Interactive console commands

Lines on stdin are parsed by parseCommand (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 (fetched via 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) 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.

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 degrees↔counts 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.