Files
ruvnet--RuView/python/wifi_densepose/mat.py
T
ruv 1c9727f9cf feat(adr-185): P3 MAT bindings (wifi_densepose.mat) + parity harness
Bind the ADR-024 MAT (Mass Casualty Assessment Tool) disaster-survivor
detection + START triage surface into the wheel behind a gated [mat]
extra / Cargo `mat` feature, mirroring the upstream disaster/ML gating.
Also adds the [sota] superset extra (aether+meridian+mat).

Surface (bound against the REAL code at HEAD, not the ADR wishlist):
- DisasterType (9 variants) / TriageStatus (5, START) enums
- DisasterConfig (builder-backed, continuous_monitoring forced off)
- DisasterResponse: initialize_event / add_zone / push_csi_data /
  scan_once / survivors / survivors_by_triage
- Survivor (id, triage_status, confidence, location, latest_vitals)
- VitalSignsReading (breathing/heartbeat rate, movement, confidence)
- ScanZone.rectangle / ScanZone.circle
push_csi_data + scan_once are GIL-released.

Honest deviations from ADR section 3.4 (documented in module header):
- ADR proposed adding a Rust-side sync scan_once() (section 11.3). That was
  UNNECESSARY: the public async start_scanning() runs exactly one
  scan_cycle and returns when continuous_monitoring == false. The binding
  forces that flag off and drives one cycle on a private current-thread
  tokio runtime -- NO change to wifi-densepose-mat.
- scan_cycle requires an active event + Active zone, which the ADR surface
  omitted; initialize_event + add_zone are bound as required additions.
- Survivor.vital_signs is a *history* in the real code; bound as
  Survivor.latest_vitals -> Optional[VitalSignsReading].
- DisasterType has 9 variants at HEAD (adds Landslide/MineCollapse/
  Industrial/TunnelCollapse); all bound.

Parity (section 4.1, release-blocking): committed fixture mat_input.json
(synthetic breathing-modulated CSI stream) -> native Rust reference
(tests/mat_parity.rs, drives DisasterResponse directly) locks
tests/golden/mat_result.sha256 over a canonical
`count=<K>;triage_priorities=<sorted>` string (survivor UUIDs/timestamps
excluded as non-deterministic); pytest (tests/test_mat.py) runs the same
stream through the binding and asserts the identical hash. Both detect
exactly 1 survivor, triage Delayed. Honest: synthetic fixture proves
binding==native path equality, NOT live detection accuracy.

Verified:
  cargo test --features mat --test mat_parity -> 2/2 pass
  maturin develop --features mat + pytest tests/test_mat.py -> 7/7 pass
  default cargo build clean, 0 mat/tokio refs in the default dep graph.

WHEEL-SIZE FINDING (ADR-185 section 9): default-features=false drops MAT's
`api` (axum) and `ruvector` features, but MAT still carries NON-optional
tokio (rt/sync/time), wifi-densepose-nn (ort/ONNX + reqwest/hyper),
rustfft, geo, ndarray. So a [mat] wheel exceeds the ADR-117 section 5.4
<=5 MB budget -- same leaf-crate-hoist follow-up as AETHER/MERIDIAN. The
default wheel is untouched (feature-gated).
2026-07-21 16:49:49 -07:00

60 lines
2.1 KiB
Python

"""MAT — Mass Casualty Assessment Tool (ADR-024 crate, ADR-185 P3).
WiFi-based disaster-survivor detection and START-protocol triage from CSI:
ingest CSI frames, run a scan cycle, and query detected survivors by triage.
Available **only** when the wheel was built with the ``[mat]`` extra::
pip install wifi-densepose[mat]
Quick start::
from wifi_densepose.mat import DisasterConfig, DisasterResponse, DisasterType, ScanZone
cfg = DisasterConfig(DisasterType.Earthquake, sensitivity=0.9, confidence_threshold=0.1)
resp = DisasterResponse(cfg)
resp.initialize_event(0.0, 0.0, "Building A") # required before scanning
resp.add_zone(ScanZone.rectangle("North Wing", 0.0, 0.0, 50.0, 30.0))
for amp, phase in csi_stream:
resp.push_csi_data(amp, phase)
resp.scan_once() # one detection cycle
for s in resp.survivors():
print(s.id, s.triage_status, s.confidence, s.location)
Honest scope (ADR-185 §3.4): the ADR's Rust-side `scan_once()` wrapper was
unnecessary — this binding drives one cycle of the public async
`start_scanning()` (with `continuous_monitoring` forced off) on an internal
runtime. `initialize_event` + `add_zone` are required before `scan_once`.
`Survivor.latest_vitals` returns the latest reading (the Rust accessor is a
history). The detection pipeline is real but unvalidated on live rubble.
"""
from __future__ import annotations
from wifi_densepose import _native
# MAT symbols are compiled into `_native` only under the Rust `mat` feature.
if not hasattr(_native, "DisasterResponse"):
raise ImportError(
"wifi_densepose.mat is not available in this wheel. "
"It requires the 'mat' extra: pip install wifi-densepose[mat]"
)
DisasterType = _native.DisasterType
TriageStatus = _native.TriageStatus
DisasterConfig = _native.DisasterConfig
DisasterResponse = _native.DisasterResponse
ScanZone = _native.ScanZone
Survivor = _native.Survivor
VitalSignsReading = _native.VitalSignsReading
__all__ = [
"DisasterType",
"TriageStatus",
"DisasterConfig",
"DisasterResponse",
"ScanZone",
"Survivor",
"VitalSignsReading",
]