9.7 KiB
Module Reference
Per-file reference for the refactored tree, plus the shared data structures.
Core (fgc_core — SDK-independent, unit-tested)
| File | Contents |
|---|---|
| include/fgc/Config.h, src/core/Config.cpp | Typed AppConfig (General/Network/Serial/Camera/Paths/Features/Logging/UI/Motor/Scan/IMU) + ConfigLoader (INI parse, env overrides, validation) |
| include/fgc/Paths.h, src/core/Paths.cpp | ~/$ENV expansion, executable dir, config search order, default output dir |
| include/fgc/Logger.h, src/core/Logger.cpp | Leveled, thread-safe logger + per-category wire trace; LOG_TRACE..LOG_ERROR, LOG_TRACE_CAT |
| include/fgc/Geometry.h, src/core/Geometry.cpp | Per-axis degrees↔encoder-counts affine map ([Motor] calibration) |
| include/fgc/ScanGrid.h, src/core/ScanGrid.cpp | Capture waypoints (CSV or generated) + ping-pong cursor ([Scan]) |
| include/fgc/TelemetryParser.h, src/core/TelemetryParser.cpp | parseTelemetryLine (firmware ST line) → std::optional<MotorTelemetry> |
| include/fgc/CommandParser.h, src/core/CommandParser.cpp | parseCommand whitespace tokenizer → Command |
| include/fgc/HelpText.h, src/core/HelpText.cpp | Operator command catalog (helpCatalog) + renderHelp (console & TUI help) |
| include/fgc/DumpParser.h, src/core/DumpParser.cpp | parseDump firmware DUMP block → DumpData (per-axis state + decoded TMC registers/flags); formatDump |
| include/fgc/DiagParser.h, src/core/DiagParser.cpp | parseDiag firmware DG self-test stream → DiagResult (per-axis tests, PASS/FAIL); formatDiag |
| include/fgc/Calibration.h, src/core/Calibration.cpp | linearFit (least-squares, R²) + circularMeanDeg for the IMU-referenced calibration |
| include/fgc/CalibrationRoutine.h, src/core/CalibrationRoutine.cpp | gimbal calib worker thread: sweeps each axis, dwells reading the IMU, fits degrees↔counts; exposes progress()/report()/takeResult() |
| include/fgc/MtiProtocol.h, src/core/MtiProtocol.cpp | Xsens MTi binary protocol: MtiFramer (checksum framing), config-message builders, parseMTData → ImuSample (temp/acc/gyr/mag/euler), config-readback query builders + applyImuConfigAck/finalizeImuConfig → ImuDeviceConfig (product/firmware/device-id/output mode+settings/sample rate/XKF scenario) |
| include/fgc/CaptureScheduler.h, src/core/CaptureScheduler.cpp | Capture state machine over the interfaces; injectable clock; setGeometry adopts a recalibration |
| include/fgc/Application.h, src/core/Application.cpp | Factory (real vs mock, headless vs TUI), wiring, control loop, gimbal … commands, background-result polling, buildSnapshot() |
| include/fgc/ui/UiSnapshot.h, src/ui/UiSnapshot.cpp | Plain-data view model (incl. ImuView, ActivityView, CalibResultView, DumpView) + pure formatting helpers (state label/colour, degrees, time-ago, pending-sensors) |
| include/fgc/ui/HeadlessUi.h, src/ui/HeadlessUi.cpp | Default line console: stdin → command sink; logs via the default stdout/stderr writer |
| ini.c, ini.h | Bundled third-party inih INI parser |
Interfaces
| File | Interface | Shared structs |
|---|---|---|
| include/fgc/IMotorController.h | IMotorController |
MotorTelemetry |
| include/fgc/IControlChannel.h | IControlChannel |
ControlCommand, CamEvent |
| include/fgc/ICameraSource.h | ICameraSource |
Frame |
| include/fgc/IImuSource.h | IImuSource |
ImuSample (from MtiProtocol.h) |
| include/fgc/ui/IUserInterface.h | IUserInterface |
UiSnapshot |
Real implementations (SDK-gated)
| File | Implements | Built when |
|---|---|---|
| src/serial/SerialMotorController.cpp | IMotorController over Boost.Asio serial (pImpl) |
always |
| src/serial/MtiImuSource.cpp | IImuSource over Boost.Asio serial: configures the MTi to Euler+calibrated, reads back its config (config() → ImuDeviceConfig), then frames the MTData stream |
always |
| src/mqtt/MqttControlChannel.cpp | IControlChannel over Eclipse Paho |
WITH_MQTT |
| src/ui/TuiUi.cpp | IUserInterface over FTXUI: panels, key bar, log pane |
WITH_TUI |
| src/camera/VimbaCameraSource.cpp | ICameraSource over Vimba X (pImpl) |
WITH_VIMBA |
| src/camera/JpegXlEncoder.cpp | libjxl encode-to-file | always |
| src/camera/ImagePipeline.cpp | frame → rotate → encode → write → CamEvent (worker thread) | always |
Mock / null implementations
| File | Implements |
|---|---|
| include/fgc/mock/MockMotorController.h | Simulated sweeping gimbal (incl. canned DUMP) |
| include/fgc/mock/NullControlChannel.h | No-op channel; auto-sweep |
| include/fgc/mock/MockCameraSource.h | Synthetic gradient frames |
| include/fgc/mock/MockImuSource.h | Synthetic IMU (sinusoidal orientation, gravity on accZ) |
Entry point & scripts
| File | Role |
|---|---|
| main.cpp | CLI parsing → AppConfig + RuntimeOptions → Application::run() |
| scripts/run.sh | Path-independent launcher |
| scripts/fire-gimbal-control.service | systemd unit template |
Data structures
MotorTelemetry / AxisTelemetry (IMotorController.h)
Per-axis yaw and pitch segments (pitch_present flag), each an AxisTelemetry: state
(AxisState B/R/H/A/E), xactual, xenc (encoder counts), drv_status, sg/cs/pwm, and flags
standstill/stall/overtemp/endstop_l/endstop_r, with moving()/ready() helpers. Parsed from the
firmware ST Y:...[ P:...] line; degrees are derived via Geometry.
ControlCommand (IControlChannel.h)
control_code (0 = scan-grid sweep, 1 = directed to target_HDG) + target_heading, each with an
*_available flag.
CamEvent (IControlChannel.h)
tower, camera (RGB/ACR/NIR), heading_decideg (yaw×10), pitch_decideg (pitch×10), timestamp_ms.
Serialized to the CamEvent JSON payload (see mqtt-api.md).
Frame (ICameraSource.h)
Owned pixel buffer + width, height, channels (1 or 3), timestamp_ms, cam_id.
ImuSample (MtiProtocol.h)
One decoded Xsens MTi reading: temp_c (°C), acc[3] (m/s², incl. gravity), gyr[3] (rad/s),
mag[3] (a.u., earth-normalized), roll_deg (−180..180), pitch_deg (−90..90), yaw_deg
(0..360 heading — the MTi's native −180..180 is shifted by parseMTData), sample_counter.
gimbal calib phase-unwraps the swept yaw (unwrapNear) so a sweep crossing 0/360 still fits a clean
line.
ImuDeviceConfig (MtiProtocol.h)
The device configuration read back during the Config-state handshake (each has_* flag marks whether
the device actually answered): product_code, device_id, firmware, output mode flags
(temp/calibrated/orientation/aux/status), output settings (orientation_mode, timestamp_mode,
per-channel acc/gyr/mag_enabled, data_format), period/sample_rate_hz, and the XKF scenario
(scenario_type/version, resolved to scenario_label against available_profiles). Built by
applyImuConfigAck (one ack frame at a time) + finalizeImuConfig (derive rate, resolve label).
Surfaced read-only in the TUI's i view (IMU CONFIG section); the legacy "scenario" is the modern
"filter profile" — same MIDs (0x62/0x64/0x65).
On-disk artifacts
| Artifact | Path | Format |
|---|---|---|
| Captured images | <output_dir>/<RGB|ACR|NIR>/<unix_ms>.jxl |
JPEG XL, rotated 90° CCW |
| Demo placeholder | bin/x64/Release/test_smoke.jxl |
copied verbatim in demo mode |
| Diagnostics log | <data_dir>/fire_gimbal_control/logs/diag_<ts>.log |
raw DG stream + parsed summary |
| Calibration log | <data_dir>/fire_gimbal_control/logs/calib_<ts>.log |
per-axis samples + the least-squares fit |
Diagnostics/calibration results are also surfaced live in the TUI (activity strip + gimbal g view)
and applied to the running session's geometry; other state lives in memory.