Enhance buzzer detection logic to group intermittent matches with a 500 ms quiet tail, updating relevant configurations and documentation.

This commit is contained in:
2026-10-19 15:28:21 -05:00
parent 7862a09858
commit 63d9995f59
10 changed files with 131 additions and 50 deletions
+37 -14
View File
@@ -24,21 +24,44 @@ int main(void)
CHECK(!features_compute(&c2, z, f2, &r2) && r2 == 0.0f, "silence -> invalid, rms 0");
for (int b = 0; b < NUM_BANDS; b++) CHECK(f2[b] == 0.0f, "silence fp zero");
// detector: 10-of-15 triggers once, cooldown 20 s, then re-arms
// Detector starts after N-of-M matches, groups matches within 500 ms,
// includes intermittent gaps, and excludes the final quiet tail.
detector_t d; detector_init(&d);
detector_cfg_t cfg = { 15, 10, 20000 };
uint32_t t = 0; int trig = 0, first = -1;
for (int i = 0; i < 40; i++, t += 32) if (detector_update(&d, i >= 5 && i < 30, t, &cfg)) { trig++; if (first < 0) first = i; }
CHECK(trig == 1 && first == 14, "single trigger at 10th match (got %d trig, first %d)", trig, first);
CHECK(d.state == DET_COOLDOWN, "cooldown after trigger");
for (int i = 0; i < 100; i++, t += 32) CHECK(!detector_update(&d, true, t, &cfg), "no trigger in cooldown");
t += 20000; detector_update(&d, false, t, &cfg);
CHECK(d.state == DET_IDLE, "back to idle");
int n = 0; for (int i = 0; i < 12; i++, t += 32) n += detector_update(&d, true, t, &cfg);
CHECK(n == 1, "re-arms and triggers again");
// sparse matches never trigger
detector_reset(&d); n = 0;
for (int i = 0; i < 300; i++, t += 32) n += detector_update(&d, i % 3 == 0, t, &cfg);
detector_cfg_t cfg = { 1, 1, 500 };
uint32_t duration = 0;
CHECK(!detector_update(&d, true, 0, &cfg, &duration), "event remains open after first match");
CHECK(d.state == DET_TRIGGERED && d.triggers == 1, "event starts on match");
CHECK(!detector_update(&d, false, 100, &cfg, &duration), "intermittent gap remains in event");
CHECK(!detector_update(&d, true, 400, &cfg, &duration), "match within quiet tail extends event");
CHECK(!detector_update(&d, false, 600, &cfg, &duration), "event remains open before tail expires");
CHECK(detector_update(&d, false, 900, &cfg, &duration), "event closes at 500 ms after last match");
CHECK(duration == 432, "duration includes gap, excludes final quiet tail (got %lu ms)",
(unsigned long)duration);
CHECK(d.state == DET_IDLE, "returns to idle after event closes");
CHECK(!detector_update(&d, true, 932, &cfg, &duration), "next event starts after prior event closes");
CHECK(detector_update(&d, false, 1432, &cfg, &duration), "single-frame event closes");
CHECK(duration == (1000u * HOP_LEN / FS_HZ), "single-frame duration is one hop (got %lu ms)",
(unsigned long)duration);
detector_reset(&d);
cfg = (detector_cfg_t){ 5, 3, 500 };
CHECK(!detector_update(&d, true, 0, &cfg, &duration), "first of three matches");
CHECK(!detector_update(&d, false, 32, &cfg, &duration), "intervening non-match");
CHECK(!detector_update(&d, true, 64, &cfg, &duration), "second match");
CHECK(!detector_update(&d, true, 96, &cfg, &duration), "third match starts event");
CHECK(d.buzz_start_ms == 0, "event start is the earliest qualifying match");
CHECK(detector_update(&d, false, 608, &cfg, &duration), "N-of-M event ends after quiet tail");
CHECK(duration == 128, "N-of-M duration includes one-hop extent (got %lu ms)",
(unsigned long)duration);
// Sparse matches must not satisfy 10-of-15.
cfg = (detector_cfg_t){ 15, 10, 500 };
int n = 0;
detector_reset(&d);
uint32_t t = 0;
for (int i = 0; i < 300; i++, t += 32) {
n += detector_update(&d, i % 3 == 0, t, &cfg, &duration);
}
CHECK(n == 0, "1-in-3 matches must not trigger 10/15");
printf(fails ? "HOST TEST FAILED\n" : "HOST TEST PASSED\n");
return fails != 0;