test: make firmware logic bugs catchable without hardware (Workstream F)

The firmware has never been flashed, and a real bug already reached the
repo because of it: RD03E_FRAME_LEN was 5 for a 6-byte frame, so the footer
check collided with the distance high byte and EVERY distance reading was
garbage — always `lo | 0x5500`, about 218 metres, regardless of what the
sensor saw. That was pure logic with no hardware dependency. It should have
been catchable on a laptop, and there was simply no way to run the code.

Extracted the hardware-free logic out of the three drivers — rd03e_parse,
bmp280_compensate, mems_level — as moves rather than rewrites, carrying the
explanatory comments along with the code they explain. The drivers now own
only their bus I/O and call into the pure units, so nothing changes for the
real device.

`./run_tests.sh` builds them with gcc -Wall -Wextra -Werror plus a
dependency-free assert harness: 175 checks, 0 failed, from a clean tree.

Proven to catch the actual bug rather than assumed to: reintroducing
FRAME_LEN 5 fails four checks, including one that reads "a simple-report
frame is 6 bytes, not 5", plus the truncated-frame and 5-byte-window cases.
Restored, green again.

This does NOT make the firmware verified, and the README says so plainly —
it is called a narrow exception and scoped to pure logic. Wiring, timing,
real register behaviour and the reconstructed RD-03E frame format all still
need the physical board.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Indiana
2026-07-31 13:17:35 +00:00
parent 9d42f541e3
commit 0966fa8cfc
20 changed files with 1083 additions and 177 deletions

View File

@@ -0,0 +1,47 @@
// I2S MEMS microphone level maths (RMS -> dBFS) — PURE LOGIC.
//
// ESP-IDF-free by design: no I2S, no esp_err_t, no logging. Only
// <stdint.h>/<stddef.h> and <math.h> in the .c, so it compiles and is
// testable on a host with plain gcc (see ../test/). `mems_mic.c` does the
// I2S read and calls in here for the arithmetic.
//
// The INMP441 family outputs 24-bit signed PCM, MSB-first, left-justified
// in a 32-bit I2S slot (Philips/standard I2S timing). The right-shift-by-8
// used below to recover the 24-bit sample from the 32-bit slot, and the
// dBFS reference level (2^23, a 24-bit signed sample's full-scale
// magnitude), are the commonly-documented values for this exact mic family
// — but "commonly documented" is not "verified against this specific
// board." Host tests prove the arithmetic (full scale reads ~0 dBFS,
// silence reads the floor and never -inf/NaN); they cannot prove the shift
// amount matches this module revision's real bit alignment.
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// dBFS reference: full-scale magnitude of a 24-bit signed sample.
#define MEMS_FULL_SCALE_24BIT (8388608.0) // 2^23
// Level reported for a true-silent (or sub-LSB) input, and the clamp
// applied to anything quieter.
#define MEMS_DBFS_FLOOR (-120.0)
// RMS over a block of raw 32-bit I2S slots. The mic's 24-bit sample is
// left-justified in the 32-bit slot -- shift right 8 to recover it before
// squaring, so the magnitude lines up with MEMS_FULL_SCALE_24BIT.
// Returns 0.0 for an empty block.
double mems_level_rms(const int32_t *samples, size_t n_samples);
// dBFS: 20*log10(rms / full_scale). A true-silent input gives rms=0,
// which is -inf in dB -- clamp to a floor rather than emit a value the
// JSON encoder/backend can't handle.
double mems_level_dbfs(double rms);
#ifdef __cplusplus
}
#endif