; Fire Gimbal Control - example configuration. ; ; Copy this file to "config.ini" and edit for your deployment: ; cp config/config.example.ini config.ini ; ; The real config.ini is gitignored because it contains credentials. ; MQTT credentials may instead be supplied via the environment variables ; FGC_MQTT_USER and FGC_MQTT_PW, which take precedence over the values here. [General] ; Tower identity, substituted into all MQTT topics and CamEvent payloads. tower_name = ExampleTower ; Seconds between captures. image_interval = 5 ; 1 = print motor telemetry each loop tick, 0 = quiet. debug = 0 [Network] ; MQTT broker address (the ground-station / ZKMS server). zkms_server_ip = 127.0.0.1 ; Prefer the FGC_MQTT_USER / FGC_MQTT_PW environment variables over these. mqtt_user = CHANGE_ME mqtt_pw = CHANGE_ME [Camera] ; Camera IDs: GigE IP (e.g. 192.168.11.101) or USB device ID (e.g. DEV_1AB22C0AADED). ; Leave entries blank/absent for cameras you do not have. Order maps to RGB, ACR, NIR. id_Cam1 = DEV_0000000000 id_Cam2 = id_Cam3 = id_Cam4 = ; --- On-camera imaging (Alvium; applied in-session at startup) --- ; The LattePanda USB3 host cannot reliably transfer large single frames (~60 MB RGB8 ; full-res stalls); keep frames small and PACE capture. See docs/known-issues.md. ; Digital binning: 1 = full res; 2 = 2x2 (~5 MP, ~15 MB) - the reliable default. binning = 2 ; ROI (0 = sensor maximum / full frame). Set width/height to crop. offset_x = 0 offset_y = 0 width = 0 height = 0 ; Pixel format: RGB8 keeps de-Bayering AND white balance on-camera (colour out, no host debayer). pixel_format = RGB8 ; DeviceLinkThroughputLimit in MByte/s. Do NOT max it (450 caused incomplete frames); ~200-300. throughput_mbytes = 250 ; Paced acquisition rate (fps). Keep LOW: ~2 fps of 15 MB frames stalls this USB3 host; 1 fps ; sustains. Low rate still keeps the on-camera auto exposure/gain/white-balance converged. stream_fps = 1.0 ; On-camera auto adjustments to track dramatically changing outdoor light. *_max caps bound ; motion blur / frame time (exposure) and noise (gain); 0 = leave the camera default. exposure_auto = true exposure_max_us = 0 gain_auto = true gain_max_db = 0 white_balance_auto = true ; JPEG XL encoding. distance: 0 = lossless, ~0.8 = near-lossless (default); effort 1..9. jxl_distance = 0.8 jxl_effort = 4 [Serial] ; Motor-controller serial device and baud rate. device = /dev/ttyACM0 baud = 115200 [IMU] ; Xsens MTi orientation/inertial sensor. Wired to the LattePanda's RS-232 header ; (an onboard hardware UART), so this is a stable /dev/ttyS* node - NOT a USB ; device and NOT /dev/ttyUSB0-3 (those are the Quectel modem). Find it with ; ls /dev/ttyS* ; dmesg | grep -iE 'ttyS|LPSS|HSUART' (often /dev/ttyS4) ; Enable with [Features] enable_imu = true. The host reconfigures the MTi to a ; Euler + calibrated 100 Hz stream at startup. device = baud = 115200 [Motor] ; Degrees<->encoder-counts calibration for each axis. The firmware speaks only ; in absolute encoder counts; these map them to the heading/elevation degrees ; used by MQTT (target_HDG) and CamEvent. CALIBRATE on the rig after homing: ; counts = zero_count + deg * counts_per_deg ; counts_per_deg may be negative to flip direction. min/max_deg clamp commands. ; ; Mechanical travel of this gimbal (between hard endstops, found at homing): ; Yaw ~320 deg (0 .. 320; NOT a full revolution, cannot wrap) ; Pitch 40 deg (-10 .. +30; -10 below horizon to +30 above) ; Keep all scan waypoints inside these ranges or the move clamps short. ; ; Yaw starting point: firmware steps_per_rev=177000 over ~180 deg => ~983.33. yaw_counts_per_deg = 983.33 yaw_zero_count = 500000 yaw_min_deg = -90 yaw_max_deg = 90 ; Re-anchor the yaw zero from homing instead of the fixed yaw_zero_count above: ; off (default) - use yaw_zero_count as written ; low - the lower-count endstop becomes 0 deg, degrees rise toward ; the higher-count endstop (which lands near +360) ; high - the higher-count endstop becomes 0 deg, degrees rise toward ; the lower-count endstop (which lands near +360) ; Keeps yaw_counts_per_deg as the scale; only the zero + direction come from the ; homed endstops, so the yaw origin is fixed by homing (no zero recalibration). ; yaw_min_deg/yaw_max_deg are auto-set to 0..(homed range). Pitch is unaffected. yaw_home_zero = off ; Pitch (physical endstops, -10..+30 deg = 40 deg travel). CALIBRATE counts_per_deg/zero_count. pitch_counts_per_deg = 8333.33 pitch_zero_count = 0 pitch_min_deg = -10 pitch_max_deg = 30 ; Live encoder-tracking WARN: log a warning when an axis's |xactual - xenc| ; exceeds this many counts (positional slip). 0 disables. Default 400 sits above ; the firmware hold deadband (200) and below the DIAG peak limit (500). enc_error_warn_counts = 400 [Scan] ; Capture scan grid (the (yaw,pitch) waypoints auto-sweep steps through). ; If grid_file is set, that CSV is used verbatim (one "yaw_deg,pitch_deg" per ; line, '#' comments allowed) - edit it to define exact scan coordinates. ; If grid_file is blank, a grid is generated: yaw_intervals positions across ; [yaw_min_deg, yaw_max_deg] at each pitch level, traversed ping-pong. grid_file = yaw_intervals = 56 yaw_min_deg = -90 yaw_max_deg = 90 pitch_levels = 10,30,50 [Paths] ; Directory for saved .jxl images. Supports leading ~ and $ENV expansion. ; If blank, defaults to $XDG_DATA_HOME/fire_gimbal_control/images ; (i.e. ~/.local/share/fire_gimbal_control/images). output_dir = [Features] ; Enable/disable subsystems and select mock (simulated) implementations. ; CLI flags (e.g. --no-mqtt, --mock-camera, --mock-serial) override these. enable_mqtt = true enable_camera = true enable_serial = true enable_imu = false mock_camera = false mock_serial = false mock_imu = false [Test] ; Hardware self-test command (`test`). `profile` is the default profile used when ; none is named on the command line. `baseline_file` is where `test baseline` pins ; a golden report (empty => /test_baseline.txt). Each [TestProfile.*] is a ; named bundle of repetition counts, sampling windows and pass thresholds; modules ; read the keys they need (unknown keys are ignored, so new tests can add knobs ; here without code changes). profile = standard baseline_file = [TestProfile.standard] reps = 5 ; repetitions for homing / friction / balance drift_warn_pct = 15 ; flag a metric drifting more than this vs baseline drift_gates_pass = false ; true => a drift flag also fails the metric home_spread_max_counts = 80 ; max endstop-count spread across homings home_ms_min = 4000 ; expected homing-time band (ms) home_ms_max = 20000 encoder_diag_reps = 2 imu_sample_window_ms = 3000 ; static window for IMU health imu_drift_window_ms = 60000 ; long static window for the yaw-drift fit imu_yaw_noise_max_deg = 0.5 imu_yaw_drift_max_deg_min = 1.0 imu_expected_hz = 100 ; imu_expected_profile = VRU_general ; assert the active XKF profile, if set backlash_step_counts = 200,500,1000 ; small steps probing reversal dead-band balance_steps = 10 ; pitch up/down current-profile resolution balance_asym_max = 4 ; max |up-down| current (CS) before "imbalanced" cpu_temp_max_c = 85 disk_free_min_gb = 5 ram_avail_min_mb = 256 [TestProfile.quick] reps = 3 imu_sample_window_ms = 1000 imu_drift_window_ms = 10000 [TestProfile.strict] reps = 10 drift_warn_pct = 8 drift_gates_pass = true [UI] ; Full-screen terminal dashboard (sectioned, colored, live status + log pane). ; false => headless line console (default; stdin commands, stdout logs). ; CLI --tui / --no-tui override this. Requires a build with WITH_TUI=ON. enable_tui = false [Logging] ; level: trace|debug|info|warn|error|off (default info). --log-level overrides. level = info ; Verbatim per-category wire tracing, independent of the level above. Comma list ; of: serial, mqtt, camera, control, all, none. OFF by default. --trace overrides. ; Lines are tagged [SERIAL]/[MQTT]/[CAMERA]/[CONTROL] with TX/RX direction, e.g. ; [SERIAL] TX kd180 / [SERIAL] RX $;1234;0;... trace =