14 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) + one-way wrapping 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: home-if-needed → pitch sweep at the first yaw position → pitch 0° → switch IMU to a no-mag XKF profile → IMU no-rotation + heading reset → yaw sweep, fitting degrees↔counts; tunable via CalibParams; 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); orientation-control builders msgSetNoRotation (gyro-bias update) + msgResetOrientation (heading reset / store) + msgSetFilterProfile (select XKF profile) + pickNoMagProfile (choose a magnetometer-free profile from the available list) |
| include/fgc/sensors/Sht41Protocol.h, src/sensors/Sht41Protocol.cpp | Sensirion SHT4x wire protocol, pure: command bytes, sht41Crc8 (poly 0x31/init 0xFF), decodeSht41Measurement (6-byte response → Sht41Reading, CRC-checked, raw→°C/%RH with RH clamped 0..100) |
| include/fgc/CameraProfile.h, src/core/CameraProfile.cpp | loadCameraProfile / loadCameraProfiles: one VimbaX settings XML per profile. Hashes the XML, regenerates the .txt record when it changes, and reports which settings moved. A missing XML throws — the record is never a fallback |
| include/fgc/CameraSettingsXml.h, src/core/CameraSettingsXml.cpp | Parses the Viewer export's <RemoteDevice> features (ordered, selector-aware), renders the human-readable .txt record, reads one back, and diffs two settings lists. Used for display and change detection only — never to configure a camera |
| include/fgc/Hash.h, src/core/Hash.cpp | FNV-1a 64 over a string or a file, with an algorithm-prefixed label. Change detection for the profile records, deliberately not a crypto hash |
| 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, EnvView, 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, EnvEvent |
| include/fgc/ICameraSource.h | ICameraSource |
Frame |
| include/fgc/IImuSource.h | IImuSource (incl. config(), refreshConfig(), noRotation(), headingReset(), setFilterProfile()) |
ImuSample (from MtiProtocol.h) |
| include/fgc/IEnvSensor.h | IEnvSensor (connected(), sample(), name()) |
EnvSample |
| 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/sensors/I2cBus.cpp | Thin /dev/i2c-N wrapper (open/close, write, writeRead via I2C_SLAVE ioctl) |
always |
| src/sensors/Sht41EnvSensor.cpp | IEnvSensor over I2C (pImpl): soft-reset, poll thread at [Env] period_ms, decode via Sht41Protocol, staleness = no valid sample within 2× the period |
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): free-running stream + keep-latest, restart-on-stall, and per-camera profiles applied with the SDK's LoadSettings on the Viewer's settings XML |
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) |
| include/fgc/mock/MockEnvSensor.h | Synthetic ambient sensor (slow sinusoidal temp/humidity, never stale); --mock-env |
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/deviation/overtemp/endstop_l/endstop_r, with moving()/ready() helpers.
Parsed from the firmware ST Y:...[ P:...] line; degrees are derived via Geometry.
deviation (flag d) is the TMC5160's hardware encoder-deviation latch — set when |xactual − xenc|
exceeds ENC_DEVIATION, i.e. the shaft is not where it was commanded to be. It is the usable stall signal
on this rig and is what the TUI's STALL badge shows. stall (flag s) is StallGuard2, which is inert
because production runs StealthChop; it is still parsed in case that changes. The latch is cleared by
homing or gimbal reset, so a lit badge means "position untrustworthy since the last home".
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) and
timestamp_ms (which matches the image filename). Serialized to the CamEvent JSON payload (see mqtt-api.md).
EnvEvent (IControlChannel.h)
tower, temp_c, humidity_pct, timestamp_ms. Published once per fresh sample (deduplicated
on timestamp_ms, not per control-loop tick) to the Env topic — see mqtt-api.md.
EnvSample (IEnvSensor.h)
One ambient reading: valid, temp_c (°C), humidity_pct (0..100), timestamp_ms (epoch ms).
IEnvSensor::sample() returns nullopt rather than a stale sample once nothing valid has arrived
within 2× the configured poll period, so a silently dead sensor reads as absent, not as a frozen
value. Surfaced as EnvView in the TUI (i view) and as SensorsView::env in the compact panel.
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 per Camera.rotation_deg |
| 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.