|
| 1 | +/** |
| 2 | + * @file test_mmwave_detect.c |
| 3 | + * @brief Host-side unit tests for the LD2410 frame-validation predicate (#1135). |
| 4 | + * |
| 5 | + * Proves the phantom-detection fix: a floating UART can emit the 4-byte head |
| 6 | + * 0xF4F3F2F1, but the predicate rejects it unless a sane length + matching tail |
| 7 | + * 0xF8F7F6F5 are also present. Tests the REAL predicate from mmwave_detect.h |
| 8 | + * (the same code the firmware's probe_at_baud calls). |
| 9 | + * |
| 10 | + * cc -std=c99 -Wall -I../main -o test_mmwave_detect test_mmwave_detect.c && ./test_mmwave_detect |
| 11 | + * |
| 12 | + * Exits 0 on all-pass; prints the failing case otherwise. |
| 13 | + */ |
| 14 | +#include <stdint.h> |
| 15 | +#include <stdio.h> |
| 16 | +#include <string.h> |
| 17 | +#include "mmwave_detect.h" |
| 18 | + |
| 19 | +static int failures = 0; |
| 20 | +#define CHECK(cond, msg) do { \ |
| 21 | + if (!(cond)) { printf("FAIL: %s\n", msg); failures++; } \ |
| 22 | + else { printf("ok: %s\n", msg); } \ |
| 23 | +} while (0) |
| 24 | + |
| 25 | +/* Build a valid LD2410 report frame: F4F3F2F1 | len(LE) | data[len] | F8F7F6F5 */ |
| 26 | +static int make_frame(uint8_t *out, uint16_t dlen) |
| 27 | +{ |
| 28 | + int n = 0; |
| 29 | + out[n++] = 0xF4; out[n++] = 0xF3; out[n++] = 0xF2; out[n++] = 0xF1; |
| 30 | + out[n++] = (uint8_t)(dlen & 0xFF); out[n++] = (uint8_t)(dlen >> 8); |
| 31 | + for (uint16_t k = 0; k < dlen; k++) out[n++] = (uint8_t)(0xAA ^ k); |
| 32 | + out[n++] = 0xF8; out[n++] = 0xF7; out[n++] = 0xF6; out[n++] = 0xF5; |
| 33 | + return n; |
| 34 | +} |
| 35 | + |
| 36 | +int main(void) |
| 37 | +{ |
| 38 | + uint8_t buf[256]; |
| 39 | + |
| 40 | + /* 1. A real basic-report frame (data len 13) validates. */ |
| 41 | + int n = make_frame(buf, 13); |
| 42 | + CHECK(mmwave_ld2410_valid_at(buf, 0, n), "valid basic frame (len=13) accepted"); |
| 43 | + |
| 44 | + /* 2. A real engineering-report frame (data len 35) validates. */ |
| 45 | + n = make_frame(buf, 35); |
| 46 | + CHECK(mmwave_ld2410_valid_at(buf, 0, n), "valid engineering frame (len=35) accepted"); |
| 47 | + |
| 48 | + /* 3. Head magic present but NO valid tail — the #1135 phantom case. */ |
| 49 | + memset(buf, 0x00, sizeof(buf)); |
| 50 | + buf[0]=0xF4; buf[1]=0xF3; buf[2]=0xF2; buf[3]=0xF1; buf[4]=13; buf[5]=0; |
| 51 | + /* data present but tail is zeros, not F8F7F6F5 */ |
| 52 | + CHECK(!mmwave_ld2410_valid_at(buf, 0, 64), "head magic without valid tail REJECTED (#1135)"); |
| 53 | + |
| 54 | + /* 4. Head magic with insane length is rejected. */ |
| 55 | + memset(buf, 0xFF, sizeof(buf)); |
| 56 | + buf[0]=0xF4; buf[1]=0xF3; buf[2]=0xF2; buf[3]=0xF1; buf[4]=0xFF; buf[5]=0xFF; /* len=65535 */ |
| 57 | + CHECK(!mmwave_ld2410_valid_at(buf, 0, 200), "head magic with oversized length REJECTED"); |
| 58 | + |
| 59 | + /* 5. Pure noise (no head) is rejected. */ |
| 60 | + for (int k = 0; k < 64; k++) buf[k] = (uint8_t)(0x5A + k); |
| 61 | + CHECK(!mmwave_ld2410_valid_at(buf, 0, 64), "non-header noise REJECTED"); |
| 62 | + |
| 63 | + /* 6. Truncated frame (tail would run past the buffer) is rejected. */ |
| 64 | + n = make_frame(buf, 13); |
| 65 | + CHECK(!mmwave_ld2410_valid_at(buf, 0, n - 2), "truncated frame (tail past buffer) REJECTED"); |
| 66 | + |
| 67 | + /* 7. Valid frame at a non-zero offset still validates. */ |
| 68 | + memset(buf, 0x00, sizeof(buf)); |
| 69 | + n = make_frame(buf + 7, 13); |
| 70 | + CHECK(mmwave_ld2410_valid_at(buf, 7, 7 + n), "valid frame at offset 7 accepted"); |
| 71 | + |
| 72 | + /* 8. Repeated head bytes without a frame (worst-case noise) rejected. */ |
| 73 | + for (int k = 0; k + 3 < 64; k += 4) { |
| 74 | + buf[k]=0xF4; buf[k+1]=0xF3; buf[k+2]=0xF2; buf[k+3]=0xF1; |
| 75 | + } |
| 76 | + CHECK(!mmwave_ld2410_valid_at(buf, 0, 64), "repeated bare head bytes REJECTED"); |
| 77 | + |
| 78 | + printf("\n%s (%d failures)\n", failures ? "FAILED" : "ALL PASS", failures); |
| 79 | + return failures ? 1 : 0; |
| 80 | +} |
0 commit comments