Testing¶
The firmware logic is written so that it can run on a PC: the flight core, the estimators, the drivers and the protocol have no HAL and no RTOS. These are the tests that run without a board, and what each one proves.
| Suite | Command | Checks | What it covers |
|---|---|---|---|
| FC host tests | make in 00 - FC_FW/Tests/host |
93 | flight core, failsafes, TAKEOFF/LAND, altitude filter, drivers, AppTask protocol, BLE rings |
| ExtTask, end to end | same make (ext_test) |
12 | Qwiic probing and polling, with the real bridge logic of 01 - FLOW_FW |
| Battery | same make (power_test) |
95 | estimator, BattTask signals, battery failsafes, BATTERY calibration, CHECKS item 1, POWER, flash image v1 → v2 |
| Flight log | same make (log_test) |
63 | W25Q driver on a simulated chip, sector ring (wrap, re-mount, power loss), queues, LogTask policy, USB download protocol, BLE last-flight download through the AppTask, SET_TIME and LR_TIME |
| USB ground link | same make (link_test) |
35 | flight commands refused over USB, ARM / TAKEOFF / MOTOR_TEST refused with a USB host (CHECKS item 12), the first link has control of the settings, ACKs to their own link, three producers on one endpoint without cut frames, calibration aborted when USB closes |
| Attitude IEKF, cold start | same make (iekf_test) |
32 | the production estimator pipeline (app_est_config()) re-initialised 30°–179° off, static and moving, with and without the tangential innovation (Cold start) |
| Flow bridge | make test in 01 - FLOW_FW |
22 | register map against the FC driver, I²C target protocol, PMW3901 sequences |
| Frames vs app decoders | frames.bin and power_frames.bin from the FC host tests, decoded by the app (src/protocol) |
every v2 frame | byte-for-byte layout of CAPS, FLIGHT, POWER, NAV, MOTORS, SYSTEM, EVENT, PARAM, CALIB (battery too), ACK, CHECKS |
| Python tools | pytest in 02 - Tools/01 - Python |
33 | parity of the Python filters and parsers with MATLAB; flight-log decoder on a log written by the firmware code (log_test → flightlog_sample.bin), download client against an emulated drone |
| Firmware builds | CubeIDE / arm-none-eabi-gcc |
– | the FC firmware (include paths and defines from .cproject) and the Flow firmware (make) |
The C suites build with AddressSanitizer and UBSan, so an out-of-bounds access or an undefined shift fails the test, not only a wrong value.
FC host tests¶
flowchart LR
T["test_main.c"] --> FC["fc_core"]
T --> AT["app_task (HOST_TEST)"]
AT --> FC
AT --> P["params · cfg_store (RAM pages)"]
AT --> CAL["calib_mgr · checks"]
T --> ALT["alt_est · alt_ctrl"]
T --> DRV["vl53l1x · flow_bridge"]
AT --> BLE["tlm_ble rings"] --> BIN[("frames.bin")]
PL["vertical plant<br/>thrust → h, vz"] <--> FC
They cover:
- the ACK cache;
- TAKEOFF rules: no altitude sensor, not armed, no lift-off within 6 s, a second TAKEOFF;
- TAKEOFF → hold 1 m → LAND → auto-disarm, against a simple vertical plant.
This checks the climb rate, the overshoot and the
autoDisarmtiming; - the indoor model:
- POS_HOLD at the target, and the slow landing below 1 m;
- flow lost, and the ceiling;
- no FLOW_LOST and no position hold below 80 mm, at take-off and landing;
- the app-link failsafe timings (short at 1 s, long at 3 s, then landing and disarm), with BLE silent, and the GAP disconnect;
- the altitude filter:
- baro only;
- baro + ToF with a baro drift;
- re-anchoring on a step;
- the "on the ground" reading;
- the vertical acceleration at rest and tilted;
- the VL53L1X driver against a register model, and the flow-bridge decoder;
- the AppTask protocol:
- the subscribe order CAPS → INIT → CHECKS, and the rates;
- PARAM_SET repeated with the same seq, executed once;
- PARAM_SAVE and a reload from flash;
- ARM blocked by CHECKS;
- TAKEOFF with the saved
takeoffAlt; - KILL with EVENT 70 sent 3 times;
- REQUEST, SET_NAME, a Flow board plugged in late, a flash error reported;
- the CHECKS flow item on the ground and in the air;
- HI/LO ring priority: an ACK right after a half-sent STATE frame, and HI frames accepted while LO is full.
ExtTask end to end¶
ext_test runs app/ext_sens.c with a VL53L1X register model and, for the
flow, the real bridge firmware logic (01 - FLOW_FW/Core/Src/bridge.c).
The bridge sits behind a simulated I²C bus, reached through the FC
flow_bridge driver. The test covers:
- the probe at 0 s and 5 s, with nothing plugged;
- a board plugged in at 7 s and found at 10 s;
- ToF samples on data ready;
- flow frames every cycle, with motion only when new;
- status-only frames when the sensor is below its minimum height;
- a ToF lost after 50 errors and found again.
If the FC driver and the bridge firmware ever disagree on the register map or the latch semantics, this test fails.
Cross-check with the app¶
The FC host test writes every frame the AppTask sends to frames.bin. To
check the layouts against the app:
- parse the stream with the app's
StreamParser(02 - Tools/02 - App/src/protocol/frame.ts); - decode each frame with
decodeFrame(); - re-encode it with the matching
encode*().
The bytes must be identical, and there must be no CRC error. Last result:
143 frames from frames.bin and 507 from power_frames.bin (POWER, battery
CALIB, CHECKS with the battery item), 0 CRC errors, 0 mismatches.
Attitude IEKF cold start¶
iekf_test runs est_pipeline.c (calibration, multi-rate IEKF, stillness
detector, mag latency) with the configuration the firmware uses,
app_est_config(), on simulated sensors. After the static calibration it
calls iekf_reset_attitude() with an error of 30°, 90°, 150° and 179° about 6
axes, on a still and on a rotating drone, and prints a table: how many runs
converge below 5° for good within 10 s, the worst time, the lockouts and the
final error. It repeats the runs with the full innovation, with the production
\(P_0\) and without the grace period. The table is in
IEKF on the microcontroller.
It takes about 20 s with the sanitizers. Tests/host/stubs/arm_math.h
replaces CMSIS-DSP on the PC.
The C filter, the Python port (acwing_tools.filters.embedded) and the MATLAB
reference (iekfCore*.m) were checked to give the same state to 6 digits, with
the tangential innovation on and off and the NIS inflation on.
Flight log across languages¶
log_test writes one downloaded log, produced by the firmware's own store,
queues and LogTask, to log_sample.bin. The same file is
02 - Tools/01 - Python/tests/data/flightlog_sample.bin: the Python decoder
test and the MATLAB decoder (acwingLogDecode, checked with Octave) read it
and must find the same records. When the format changes, regenerate the file
with make and copy it.
On the board¶
The tests above do not replace the board. The acceptance tests of
HANDOVER_FW.md §16 (with the app), the hand test of TAKEOFF/LAND
(Flight core) and the first checks of
the Flow board (ACwing Flow bridge) are
still to be run.