Replay Tabs:

- Add stability detection and threshold classification features with corresponding tests
This commit is contained in:
jack
2026-06-04 13:25:11 -07:00
parent 9779baa468
commit 9cd3170e8a
8 changed files with 207 additions and 8 deletions
+5 -5
View File
@@ -1,7 +1,7 @@
"""Tests for serialcomm/data_parser.py binary parsing pipeline."""
import pytest
from serialcomm.data_parser import (
from pygui.serialcomm.data_parser import (
csv_column_count,
parse_binary_data,
convert_to_temperatures,
@@ -12,7 +12,6 @@ from serialcomm.data_parser import (
MAXDUT_X,
)
# ── csv_column_count ──────────────────────────────────────────────────────────
@@ -203,6 +202,7 @@ class TestConvertToTemperatures:
def test_p_family_single_block(self):
data = _make_p_block(1, value=0x0100)
hex_data = parse_binary_data(data, "P")
assert hex_data is not None
result = convert_to_temperatures(hex_data, "P")
assert len(result) == 1
assert all(isinstance(v, str) for v in result[0])
@@ -270,9 +270,9 @@ class TestSaveToCsv:
result = save_to_csv(data, family, f"{family}00001", str(tmp_path))
assert result is not None, f"save_to_csv returned None for {family}"
headers = open(result).readline().strip().split(",")
assert len(headers) == expected_cols, (
f"{family}: expected {expected_cols} headers, got {len(headers)}"
)
assert (
len(headers) == expected_cols
), f"{family}: expected {expected_cols} headers, got {len(headers)}"
assert headers[0] == "Sensor1"
assert headers[-1] == f"Sensor{expected_cols}"
+22
View File
@@ -0,0 +1,22 @@
import math
import pytest
from pygui.backend.frame_stats import compute_stats, Stats
def test_basic_stat():
s = compute_stats([148.0, 150.0, 149.0])
assert s.min == 148.0 and s.min_index == 0
assert s.max == 150.0 and s.max_index == 1
assert s.diff == pytest.approx(2.0)
assert s.avg == pytest.approx(149.0)
assert s.sigma == pytest.approx(math.sqrt(2 / 3))
assert s.three_sigma == pytest.approx(3 * math.sqrt(2 / 3))
def test_empty_values_returns_zeros():
s = compute_stats([])
assert s == Stats(0.0, -1, 0.0, -1, 0.0, 0.0, 0.0, 0.0)
def test_ignores_nan():
s = compute_stats([149.0, float("nan"), 151.0])
+35
View File
@@ -0,0 +1,35 @@
from pygui.backend.stability_detector import (
StabilityDetector, STATE_IDLE, STATE_RAMP, STATE_SET,
)
SET_POINT = 149.0
def make():
return StabilityDetector(idle_below = 50.0, tolerance=1.0, settle_seconds=2.0)
def test_idle_when_cold():
d = make()
assert d.update(avg=25.0, time=0.0, set_point=SET_POINT) == STATE_IDLE
def test_ramp_while_far_from_setpoint():
d = make()
d.update(avg=100.0, time=0.0, set_point=SET_POINT)
def test_ramp_until_settle_time_elapses():
d = make()
assert d.update(avg=149.2, time=0.0, set_point=SET_POINT) == STATE_RAMP
assert d.update(avg=148.9, time=0.0, set_point=SET_POINT) == STATE_RAMP
def test_set_after_holding_near_setpoint():
d = make()
d.update(avg=149.2, time=0.0, set_point=SET_POINT)
d.update(avg=148.9, time=1.0, set_point=SET_POINT)
assert d.update(avg=149.0, time=2.5, set_point=SET_POINT) == STATE_SET
def test_back_to_ramp_on_disturbance():
d = make()
for t in (0.0, 1.0, 2.5):
d.update(149.0, t, set_point=SET_POINT)
assert d.update(avg=160.0, time=3.0, set_point=SET_POINT) == STATE_RAMP
+29
View File
@@ -0,0 +1,29 @@
import math
from pygui.backend.threshold_classifier import (
ThresholdConfig, classify, classify_all, resolve_bounds,
BAND_IN, BAND_HIGH, BAND_LOW
)
def test_classify_about_target_margin():
assert classify(149.0, target=149.0, margin=1.0) ==BAND_IN # exactly
assert classify(149.9, target=149.0, margin=1.0) ==BAND_IN # within
assert classify(150.5, target=149.0, margin=1.0) ==BAND_HIGH # 1.5 above
assert classify(147.5, target=149.0, margin=1.0) ==BAND_LOW # 1.5 below
def test_manual_bounds_use_set_point_and_margin():
cfg = ThresholdConfig(set_point=149.0, margin=1.0, auto=False)
assert resolve_bounds([200.0, 0.0], cfg) == (149.0, 1.0)
def test_auto_bounds_use_mean_and_sigma():
cfg = ThresholdConfig(auto=True)
target, margin = resolve_bounds([148.0, 150.0, 149.0], cfg)
assert target == 149.0
assert margin == math.sqrt(2 / 3)
def test_classify_all_manual():
cfg = ThresholdConfig(set_point=149.0, margin=1.0, auto=False)
assert classify_all([149.0, 151.0, 147.0], cfg) == [BAND_IN, BAND_HIGH, BAND_LOW]