Files
silver_pose/v1/tests/test_fall_policy.py
T
ilaandClaude Opus 4.8 b1f0e8aeb4 feat(v1): relax fall sensitivity for low overhead cameras
Adds configurable event.require_rapid_drop (default off), require_lower_body
(default off) and horizontal_angle_threshold_degrees (default 45). With the
lenient defaults a sustained horizontal pose alone enters SUSPECT and confirms
after the confirm window, which is now the main false-positive guard; missing
knees/ankles no longer reject the pose. Thresholds flow into config_version.
End-to-end smoke confirms a lying pose; 70 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 23:29:16 +08:00

68 lines
2.3 KiB
Python

from v1.evidence import PoseEvidence
from v1.fall_policy import FallEvidencePolicy
from v1.fall_state import FallState
def _evidence(horizontal=False, rapid_drop=False, accepted=True):
return PoseEvidence(
accepted=accepted,
horizontal_pose=horizontal,
rapid_vertical_change=rapid_drop,
horizontal_angle_degrees=10.0 if horizontal else 80.0,
hip_center_y=100.0,
torso_length=50.0,
reason="accepted" if accepted else "missing_pose",
)
def test_rapid_drop_followed_by_horizontal_pose_within_suspect_window_starts_candidate():
policy = FallEvidencePolicy(suspect_window_seconds=0.5)
onset = policy.evaluate(
"P-0001", _evidence(rapid_drop=True), now=0.0, state=FallState.NORMAL
)
candidate = policy.evaluate(
"P-0001", _evidence(horizontal=True), now=0.5, state=FallState.NORMAL
)
assert onset.is_fall_candidate is False
assert candidate.is_fall_candidate is True
def test_rejected_pose_clears_pending_drop_before_the_next_horizontal_pose():
policy = FallEvidencePolicy(suspect_window_seconds=0.5)
policy.evaluate("P-0001", _evidence(rapid_drop=True), now=0.0, state=FallState.NORMAL)
policy.evaluate("P-0001", _evidence(accepted=False), now=0.1, state=FallState.NORMAL)
candidate = policy.evaluate(
"P-0001", _evidence(horizontal=True), now=0.2, state=FallState.NORMAL
)
assert candidate.accepted is True
assert candidate.is_fall_candidate is False
def test_horizontal_pose_alone_starts_candidate_when_rapid_drop_not_required():
policy = FallEvidencePolicy(suspect_window_seconds=0.5, require_rapid_drop=False)
candidate = policy.evaluate(
"P-0001", _evidence(horizontal=True), now=0.0, state=FallState.NORMAL
)
upright = policy.evaluate(
"P-0002", _evidence(horizontal=False), now=0.0, state=FallState.NORMAL
)
assert candidate.is_fall_candidate is True
assert upright.is_fall_candidate is False
def test_upright_pose_is_recovery_evidence_only_after_confirmation():
policy = FallEvidencePolicy(suspect_window_seconds=0.5)
recovery = policy.evaluate(
"P-0001", _evidence(horizontal=False), now=4.0, state=FallState.CONFIRMED
)
assert recovery.accepted is True
assert recovery.is_recovery_candidate is True