"""Display-only force baseline / deadband (value cards).""" from __future__ import annotations import numpy as np from orisys_app_shared.force.presentation import ForcePresentation, ZeroEstimate def test_fit_zero_rejects_insufficient_samples() -> None: presentation = ForcePresentation(enabled=True, min_samples=30) estimate = presentation.fit_zero([(1.0, 2.0, 3.0), (1.1, 2.1, 3.1)]) assert estimate.quality == "insufficient" def test_fit_zero_and_accept_deadband() -> None: rng = np.random.default_rng(0) bias = np.array([1262.0, -11.0, -353.0]) noise = rng.normal(0.0, 20.0, size=(80, 3)) samples = (bias + noise).tolist() presentation = ForcePresentation( enabled=True, min_samples=30, baseline_seconds=0.0, min_deadband=50.0, deadband_percentile=99.0, ) estimate = presentation.fit_zero(samples) assert estimate.quality == "ok" presentation.apply_estimate(estimate) idle = presentation.accept(tuple(bias), unit="raw") assert idle.zero_zone assert idle.values == (0.0, 0.0, 0.0) # Curves / CSV keep the measurement: accept never mutates the input tuple. measured = (1262.0, -11.0, -353.0) presentation.accept(measured, unit="raw") assert measured == (1262.0, -11.0, -353.0) def test_observe_skips_contact_contamination() -> None: presentation = ForcePresentation( enabled=True, min_samples=5, baseline_seconds=0.1, require_no_contact=True, ) for i in range(10): out = presentation.observe( (1000.0, 0.0, 0.0), t_rel_s=i * 0.05, contact_found=True, flow_p95=0.1, ) assert out is None assert not presentation.ready def test_observe_freezes_after_quiet_window() -> None: presentation = ForcePresentation( enabled=True, min_samples=10, baseline_seconds=0.5, min_deadband=10.0, ) estimate: ZeroEstimate | None = None for i in range(40): estimate = presentation.observe( (100.0 + (i % 3), -5.0, 2.0), t_rel_s=i * 0.02, flow_p95=0.1, contact_found=False, ) if estimate is not None: break assert estimate is not None assert estimate.quality == "ok" assert presentation.ready # Further observes do not chase a new zero once frozen. later = presentation.observe((5000.0, 0.0, 0.0), t_rel_s=10.0, flow_p95=0.0) assert later is None def test_format_force_stable_digits() -> None: presentation = ForcePresentation(enabled=True) assert presentation.format_force(0.0, unit="raw") == "0" assert presentation.format_force(1262.4, unit="raw") == "1262" assert presentation.format_force(1.2345, unit="N") == "1.234"