package wshub import ( "math" "testing" ) // A short window at a high sample rate fits in a ring's initial capacity, so the // retune sweep used to leave it there — and a ring holding exactly the window has // already rolled past the front of a capture by the time that capture is read, // which happens a post-window plus captureMarginSec after the trigger fires. // // 1 MSps over a 200 ms window: 200 k points fit in the 250 k initial ring, and // every shot came back missing its first 123 ms. func TestCaptureWholeAtHighRateShortWindow(t *testing.T) { const ( key = "s1:Ch1" rate = 1e6 window = 0.2 prePct = 20.0 batchSec = 1.0 / 30.0 simSec = 6.0 ) h := NewHub() h.SetRingBudget(defaultRingPts) h.rings[key] = newSigRing(ringCapInitial) h.trigger.SetConfig(trigConfig{signalKey: key, edge: "rising", threshold: 0, windowSec: window, prePercent: prePct, mode: "normal", holdoffSec: 0.2}) rateHz := float64(rate) nBatch := int(rateHz * batchSec) ts := make([]float64, nBatch) vs := make([]float64, nBatch) armed, shots := false, 0 for now := 0.0; now < simSec; now += batchSec { for i := range ts { ts[i] = now + float64(i)/rateHz vs[i] = math.Sin(2 * math.Pi * 5 * ts[i]) // a rising crossing every 200 ms } h.ingest(key, 1, ts, vs) h.retuneRings(now) h.refreshTriggerFill() if !armed && now > 2 { h.trigger.Arm() armed = true } trigTime, pre, post, ok := h.trigger.dueCapture(now + batchSec) if !ok { if h.trigger.dueRearm(now + batchSec) { h.trigger.Arm() } continue } t0 := trigTime - pre buf := h.buildTriggerCapture(trigTime, pre, post) if buf == nil { t.Fatalf("shot at t=%.4f produced no frame at all", trigTime) } first, last, n := decodeCaptureSpan(t, buf, key) shots++ if lost := first - t0; lost > shortCaptureTol*window { _, span := h.rings[key].stats() t.Errorf("shot at t=%.4f is missing %.0f ms at the front of its %.0f ms window "+ "(got [%.4f,%.4f], %d pts; ring holds %.4f s in %d points)", trigTime, 1e3*lost, 1e3*window, first, last, n, span, h.rings[key].capacity()) } h.trigger.markTriggered(now + batchSec) } if shots < 3 { t.Fatalf("only %d shots in %.0f s", shots, simSec) } }