Replay Tabs:
- Add stability detection and threshold classification features with corresponding tests
This commit is contained in:
@@ -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}"
|
||||
|
||||
|
||||
@@ -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])
|
||||
@@ -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
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user