fwt_software/docs/test-command.md

7.0 KiB

The test command — hardware self-test suite

test runs modular hardware self-tests on the real gimbal, writes a plain-text report to disk, and compares each run against a pinned baseline and the previous run so both outright failures and slow drift are visible. It replaces the old firmware-only gimbal diag (the firmware motor self-test is now one leaf inside test gimbal encoder).

Implementation: TestRunner (worker thread, mirrors PitchSetupRoutine) + the pure TestReport model (formatTestReport/parseTestReport/applyComparison) and HostMetrics host reads.

Usage

test [<subsystem> [<leaf>]] [<profile>]
test list            # list configured profiles
test baseline        # pin the most recent report from this session as the baseline

The command is a three-level hierarchy; omitting a level widens scope. A trailing token that names a configured profile selects the profile at any position.

Command Runs
test everything
test gimbal all gimbal leaves
test gimbal homing just homing
test imu drift just IMU yaw drift
test host host thermal/disk/memory/load (no hardware needed)
test hardware is this rig the one hardware.ini declares? (no homing needed)
test gimbal encoder strict one leaf, strict profile
test strict everything, strict profile

While a test runs, interfering commands (moves, homing, a second test, capture, refresh) are ignored; Esc (or stop) cancels. Pure-UI keys (p, i, c, g, ?) keep working.

Subsystems and leaves

Subsystem Leaf Checks
gimbal homing endstop-count + travel + homing-time reproducibility
gimbal encoder firmware DIAG tracking quality (repeated) + hold-corrector drift
gimbal friction full-range traverse time + motor load per direction
gimbal backlash reversal dead-band (encoder error) at small steps
gimbal balance pitch up-vs-down per-step peak current (center-of-mass symmetry)
imu config device identity, output mode, sample rate, active XKF profile
imu health rate, dropped samples, angle noise, accel-norm, temperature
imu drift yaw drift rate (deg/min) over a long static window
host thermal hottest CPU thermal zone (/sys/class/thermal)
host disk free space on the image partition (statvfs)
host memory available RAM / swap (/proc/meminfo)
host load load average vs CPU count
hardware axes the axes [Hardware] axes declares appear in telemetry, and no others
hardware motor DUMP echoes the drivetrain current hardware.ini asked for — catches a stale flash or a tower left on limp-home defaults
hardware imu a declared IMU is connected and a sample arrives
hardware env a declared ambient sensor sits on a real I2C adapter (not SMBus-only) and returns a CRC-valid reading
hardware camera every configured camera opened; usbfs_memory_mb and USB3 LPM hold

Gimbal leaves require a homed gimbal (home first); IMU leaves require the IMU ([Features] enable_imu). host needs neither, so test host doubles as a fast non-intrusive smoke check.

hardware deliberately does not need a homed gimbal either — it reads telemetry and DUMP and moves nothing, because it is what you run on a tower that has just been installed and before you trust it enough to home it.

Some metrics are most accurate with firmware support (a homing-report HM line and a load-capture LP primitive); until those land the modules fall back to sampling the ST telemetry stream and note it in the report. See test-roadmap.md.

What test hardware is for

The other subsystems measure things and trend them. hardware asks a different question — is this rig the one hardware.ini says it is? — so its metrics are 1/0 verdicts and the sentence that matters lives in the notes:

[hw_motor]  FAIL  0 ms  # firmware adopted the declared drivetrain
  yaw_current = 0  FAIL
  ; yaw_current: yaw: firmware did not echo gs/irun/irun_home -- it predates the
    CURRENT command, so whatever hardware.ini asks for is NOT being applied. Re-flash it
  ; pitch_current: not checked -- no [Motor.pitch] current declared

Three rules hold across every leaf:

  • Nothing declared reports SKIP, never PASS. A check that did not happen must not look like one that succeeded — that is how a precondition regression survives a green run. An un-migrated tower with no hardware.ini skips the lot and still reports all_pass.
  • Unreadable is a failure. An empty or unparseable usbfs_memory_mb fails rather than passing by default, for the same reason.
  • The detail names the fix, not the symptom. "SMBus-only, point [Env] i2c_device at a real adapter" rather than "sensor not responding" — the two look identical from the sensor's side, and the distinction is what known-issues #2 cost to learn.

The motor leaf is the one worth running after every flash: GLOBALSCALER and IRUN are write-only on the TMC5160, so the DUMP echo is the only evidence a CURRENT command landed. A homing current that silently did not take is the case that destroys an endstop.

Key Default For
[Test] usbfs_min_mb 1000 What usbfs_memory_mb must reach. It reverts to 16 on every reboot unless it is on the kernel cmdline
[Test] usb3_lpm_path (empty) The camera port's usb3_lpm_permit node. Differs per board, so it cannot be guessed; empty means the LPM check reports "not checked" rather than passing

Profiles

A profile is a named bundle of repetition counts, sampling windows and pass thresholds, configured in config.ini under [Test] and [TestProfile.<name>] (see configuration.md and the example config). The default is standard (5 reps). Unknown keys are ignored, so new tests add knobs without code changes. Ship profiles like quick (fewer reps) and strict (more reps, tighter drift gate).

Pass / fail and comparison

Each metric can carry an absolute threshold from the profile. Independently, every metric is compared to the pinned baseline and the previous run; a move worse than drift_warn_pct is flagged, and if drift_gates_pass = true a flagged metric also fails. A section fails if any of its metrics fail; the run fails if any section fails.

Reports

Each run writes logs/test_YYYYMMDD-HHMMSS.log (under the per-user log dir). The format is human-readable and re-parseable:

[host_disk]  PASS  0 ms  # image partition free space
  free_gb = 3.65 GB  PASS  base=3.66  prev=3.66  drift=-0.001%
  used_pct = 2.65 %  PASS

test baseline copies the most recent report to the baseline file ([Test] baseline_file, default <logdir>/test_baseline.txt); subsequent runs show base= columns against it.