feat: Add wifi-densepose-mat disaster detection module

Implements WiFi-Mat (Mass Casualty Assessment Tool) for detecting and
localizing survivors trapped in rubble, earthquakes, and natural disasters.

Architecture:
- Domain-Driven Design with bounded contexts (Detection, Localization, Alerting)
- Modular Rust crate integrating with existing wifi-densepose-* crates
- Event-driven architecture for audit trails and distributed deployments

Features:
- Breathing pattern detection from CSI amplitude variations
- Heartbeat detection using micro-Doppler analysis
- Movement classification (gross, fine, tremor, periodic)
- START protocol-compatible triage classification
- 3D position estimation via triangulation and depth estimation
- Real-time alert generation with priority escalation

Documentation:
- ADR-001: Architecture Decision Record for wifi-Mat
- DDD domain model specification
This commit is contained in:
Claude
2026-01-13 17:24:50 +00:00
parent 0fa9a0b882
commit a17b630c02
31 changed files with 9042 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
# ADR-001: WiFi-Mat Disaster Detection Architecture
## Status
Accepted
## Date
2026-01-13
## Context
Natural disasters such as earthquakes, building collapses, avalanches, and floods trap victims under rubble or debris. Traditional search and rescue methods using visual inspection, thermal cameras, or acoustic devices have significant limitations:
- **Visual/Optical**: Cannot penetrate rubble, debris, or collapsed structures
- **Thermal**: Limited penetration depth, affected by ambient temperature
- **Acoustic**: Requires victim to make sounds, high false positive rate
- **K9 Units**: Limited availability, fatigue, environmental hazards
WiFi-based sensing offers a unique advantage: **RF signals can penetrate non-metallic debris** (concrete, wood, drywall) and detect subtle human movements including breathing patterns and heartbeats through Channel State Information (CSI) analysis.
### Problem Statement
We need a modular extension to the WiFi-DensePose Rust implementation that:
1. Detects human presence in disaster scenarios with high sensitivity
2. Localizes survivors within rubble/debris fields
3. Classifies victim status (conscious movement, breathing only, critical)
4. Provides real-time alerts to rescue teams
5. Operates in degraded/field conditions with portable hardware
## Decision
We will create a new crate `wifi-densepose-mat` (Mass Casualty Assessment Tool) as a modular addition to the existing Rust workspace with the following architecture:
### 1. Domain-Driven Design (DDD) Approach
The module follows DDD principles with clear bounded contexts:
```
wifi-densepose-mat/
├── src/
│ ├── domain/ # Core domain entities and value objects
│ │ ├── survivor.rs # Survivor entity with status tracking
│ │ ├── disaster_event.rs # Disaster event aggregate root
│ │ ├── scan_zone.rs # Geographic zone being scanned
│ │ └── alert.rs # Alert value objects
│ ├── detection/ # Life sign detection bounded context
│ │ ├── breathing.rs # Breathing pattern detection
│ │ ├── heartbeat.rs # Micro-doppler heartbeat detection
│ │ ├── movement.rs # Gross/fine movement classification
│ │ └── classifier.rs # Multi-modal victim classifier
│ ├── localization/ # Position estimation bounded context
│ │ ├── triangulation.rs # Multi-AP triangulation
│ │ ├── fingerprinting.rs # CSI fingerprint matching
│ │ └── depth.rs # Depth/layer estimation in rubble
│ ├── alerting/ # Notification bounded context
│ │ ├── priority.rs # Triage priority calculation
│ │ ├── dispatcher.rs # Alert routing and dispatch
│ │ └── protocols.rs # Emergency protocol integration
│ └── integration/ # Anti-corruption layer
│ ├── signal_adapter.rs # Adapts wifi-densepose-signal
│ └── nn_adapter.rs # Adapts wifi-densepose-nn
```
### 2. Core Architectural Decisions
#### 2.1 Event-Driven Architecture
- All survivor detections emit domain events
- Events enable audit trails and replay for post-incident analysis
- Supports distributed deployments with multiple scan teams
#### 2.2 Configurable Detection Pipeline
```rust
pub struct DetectionPipeline {
breathing_detector: BreathingDetector,
heartbeat_detector: HeartbeatDetector,
movement_classifier: MovementClassifier,
ensemble_classifier: EnsembleClassifier,
}
```
#### 2.3 Triage Classification (START Protocol Compatible)
| Status | Detection Criteria | Priority |
|--------|-------------------|----------|
| Immediate (Red) | Breathing detected, no movement | P1 |
| Delayed (Yellow) | Movement + breathing, stable vitals | P2 |
| Minor (Green) | Strong movement, responsive patterns | P3 |
| Deceased (Black) | No vitals for >30 minutes continuous scan | P4 |
#### 2.4 Hardware Abstraction
Supports multiple deployment scenarios:
- **Portable**: Single TX/RX with handheld device
- **Distributed**: Multiple APs deployed around collapse site
- **Drone-mounted**: UAV-based scanning for large areas
- **Vehicle-mounted**: Mobile command post with array
### 3. Integration Strategy
The module integrates with existing crates through adapters:
```
┌─────────────────────────────────────────────────────────────┐
│ wifi-densepose-mat │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Detection │ │ Localization│ │ Alerting │ │
│ │ Context │ │ Context │ │ Context │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ └────────────────┼─────────────────────┘ │
│ │ │
│ ┌───────────▼───────────┐ │
│ │ Integration Layer │ │
│ │ (Anti-Corruption) │ │
│ └───────────┬───────────┘ │
└──────────────────────────┼───────────────────────────────────┘
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│wifi-densepose │ │wifi-densepose │ │wifi-densepose │
│ -signal │ │ -nn │ │ -hardware │
└───────────────┘ └───────────────┘ └───────────────┘
```
### 4. Performance Requirements
| Metric | Target | Rationale |
|--------|--------|-----------|
| Detection Latency | <500ms | Real-time feedback for rescuers |
| False Positive Rate | <5% | Minimize wasted rescue efforts |
| False Negative Rate | <1% | Cannot miss survivors |
| Penetration Depth | 3-5m | Typical rubble pile depth |
| Battery Life (portable) | >8 hours | Full shift operation |
| Concurrent Zones | 16+ | Large disaster site coverage |
### 5. Safety and Reliability
- **Fail-safe defaults**: Always assume life present on ambiguous signals
- **Redundant detection**: Multiple algorithms vote on presence
- **Continuous monitoring**: Re-scan zones periodically
- **Offline operation**: Full functionality without network
- **Audit logging**: Complete trace of all detections
## Consequences
### Positive
- Modular design allows independent development and testing
- DDD ensures domain experts can validate logic
- Event-driven enables distributed deployments
- Adapters isolate from upstream changes
- Compatible with existing WiFi-DensePose infrastructure
### Negative
- Additional complexity from event system
- Learning curve for rescue teams
- Requires calibration for different debris types
- RF interference in disaster zones
### Risks and Mitigations
| Risk | Mitigation |
|------|------------|
| Metal debris blocking signals | Multi-angle scanning, adaptive frequency |
| Environmental RF interference | Spectral sensing, frequency hopping |
| False positives from animals | Size/pattern classification |
| Power constraints in field | Low-power modes, solar charging |
## References
- [WiFi-based Vital Signs Monitoring](https://dl.acm.org/doi/10.1145/3130944)
- [Through-Wall Human Sensing](https://ieeexplore.ieee.org/document/8645344)
- [START Triage Protocol](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3088332/)
- [CSI-based Human Activity Recognition](https://arxiv.org/abs/2004.03661)

View File

@@ -0,0 +1,497 @@
# WiFi-Mat Domain Model
## Domain-Driven Design Specification
### Ubiquitous Language
| Term | Definition |
|------|------------|
| **Survivor** | A human detected within a scan zone, potentially trapped |
| **Vital Signs** | Detectable life indicators: breathing, heartbeat, movement |
| **Scan Zone** | A defined geographic area being actively monitored |
| **Detection Event** | An occurrence of vital signs being detected |
| **Triage Status** | Medical priority classification (Immediate/Delayed/Minor/Deceased) |
| **Confidence Score** | Statistical certainty of detection (0.0-1.0) |
| **Penetration Depth** | Estimated distance through debris to survivor |
| **Debris Field** | Collection of materials between sensor and survivor |
---
## Bounded Contexts
### 1. Detection Context
**Responsibility**: Analyze CSI data to detect and classify human vital signs
```
┌─────────────────────────────────────────────────────────┐
│ Detection Context │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Breathing │ │ Heartbeat │ │
│ │ Detector │ │ Detector │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ └─────────┬─────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Movement │ │
│ │ Classifier │ │
│ └────────┬────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Ensemble │──▶ VitalSignsReading │
│ │ Classifier │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
```
**Aggregates**:
- `VitalSignsReading` (Aggregate Root)
**Value Objects**:
- `BreathingPattern`
- `HeartbeatSignature`
- `MovementProfile`
- `ConfidenceScore`
### 2. Localization Context
**Responsibility**: Estimate survivor position within debris field
```
┌─────────────────────────────────────────────────────────┐
│ Localization Context │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │Triangulation │ │Fingerprinting│ │
│ │ Engine │ │ Matcher │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ └─────────┬─────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Depth │ │
│ │ Estimator │ │
│ └────────┬────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Position │──▶ SurvivorLocation │
│ │ Fuser │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
```
**Aggregates**:
- `SurvivorLocation` (Aggregate Root)
**Value Objects**:
- `Coordinates3D`
- `DepthEstimate`
- `LocationUncertainty`
- `DebrisProfile`
### 3. Alerting Context
**Responsibility**: Generate and dispatch alerts based on detections
```
┌─────────────────────────────────────────────────────────┐
│ Alerting Context │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Triage │ │ Alert │ │
│ │ Calculator │ │ Generator │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ └─────────┬─────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Dispatcher │──▶ Alert │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
```
**Aggregates**:
- `Alert` (Aggregate Root)
**Value Objects**:
- `TriageStatus`
- `Priority`
- `AlertPayload`
---
## Core Domain Entities
### Survivor (Entity)
```rust
pub struct Survivor {
id: SurvivorId,
detection_time: DateTime<Utc>,
location: Option<SurvivorLocation>,
vital_signs: VitalSignsHistory,
triage_status: TriageStatus,
confidence: ConfidenceScore,
metadata: SurvivorMetadata,
}
```
**Invariants**:
- Must have at least one vital sign detection to exist
- Triage status must be recalculated on each vital sign update
- Confidence must be >= 0.3 to be considered valid detection
### DisasterEvent (Aggregate Root)
```rust
pub struct DisasterEvent {
id: DisasterEventId,
event_type: DisasterType,
start_time: DateTime<Utc>,
location: GeoLocation,
scan_zones: Vec<ScanZone>,
survivors: Vec<Survivor>,
status: EventStatus,
}
```
**Invariants**:
- Must have at least one scan zone
- All survivors must be within a scan zone
- Cannot add survivors after event is closed
### ScanZone (Entity)
```rust
pub struct ScanZone {
id: ScanZoneId,
bounds: ZoneBounds,
sensor_positions: Vec<SensorPosition>,
scan_parameters: ScanParameters,
status: ZoneStatus,
last_scan: DateTime<Utc>,
}
```
---
## Value Objects
### VitalSignsReading
```rust
pub struct VitalSignsReading {
breathing: Option<BreathingPattern>,
heartbeat: Option<HeartbeatSignature>,
movement: MovementProfile,
timestamp: DateTime<Utc>,
confidence: ConfidenceScore,
}
```
### TriageStatus (Enumeration)
```rust
pub enum TriageStatus {
/// Immediate - Life-threatening, requires immediate intervention
Immediate, // Red tag
/// Delayed - Serious but can wait for treatment
Delayed, // Yellow tag
/// Minor - Walking wounded, minimal treatment needed
Minor, // Green tag
/// Deceased - No vital signs detected over threshold period
Deceased, // Black tag
/// Unknown - Insufficient data for classification
Unknown,
}
```
### BreathingPattern
```rust
pub struct BreathingPattern {
rate_bpm: f32, // Breaths per minute (normal: 12-20)
amplitude: f32, // Signal strength
regularity: f32, // 0.0-1.0, consistency of pattern
pattern_type: BreathingType,
}
pub enum BreathingType {
Normal,
Shallow,
Labored,
Irregular,
Agonal,
}
```
### HeartbeatSignature
```rust
pub struct HeartbeatSignature {
rate_bpm: f32, // Beats per minute (normal: 60-100)
variability: f32, // Heart rate variability
strength: SignalStrength,
}
```
### Coordinates3D
```rust
pub struct Coordinates3D {
x: f64, // East-West offset from reference (meters)
y: f64, // North-South offset from reference (meters)
z: f64, // Depth below surface (meters, negative = below)
uncertainty: LocationUncertainty,
}
pub struct LocationUncertainty {
horizontal_error: f64, // meters (95% confidence)
vertical_error: f64, // meters (95% confidence)
}
```
---
## Domain Events
### Detection Events
```rust
pub enum DetectionEvent {
/// New survivor detected
SurvivorDetected {
survivor_id: SurvivorId,
zone_id: ScanZoneId,
vital_signs: VitalSignsReading,
location: Option<Coordinates3D>,
timestamp: DateTime<Utc>,
},
/// Survivor vital signs updated
VitalsUpdated {
survivor_id: SurvivorId,
previous: VitalSignsReading,
current: VitalSignsReading,
timestamp: DateTime<Utc>,
},
/// Survivor triage status changed
TriageStatusChanged {
survivor_id: SurvivorId,
previous: TriageStatus,
current: TriageStatus,
reason: String,
timestamp: DateTime<Utc>,
},
/// Survivor location refined
LocationRefined {
survivor_id: SurvivorId,
previous: Coordinates3D,
current: Coordinates3D,
timestamp: DateTime<Utc>,
},
/// Survivor no longer detected (may have been rescued or false positive)
SurvivorLost {
survivor_id: SurvivorId,
last_detection: DateTime<Utc>,
reason: LostReason,
},
}
pub enum LostReason {
Rescued,
FalsePositive,
SignalLost,
ZoneDeactivated,
}
```
### Alert Events
```rust
pub enum AlertEvent {
/// New alert generated
AlertGenerated {
alert_id: AlertId,
survivor_id: SurvivorId,
priority: Priority,
payload: AlertPayload,
},
/// Alert acknowledged by rescue team
AlertAcknowledged {
alert_id: AlertId,
acknowledged_by: TeamId,
timestamp: DateTime<Utc>,
},
/// Alert resolved
AlertResolved {
alert_id: AlertId,
resolution: AlertResolution,
timestamp: DateTime<Utc>,
},
}
```
---
## Domain Services
### TriageService
Calculates triage status based on vital signs using START protocol:
```rust
pub trait TriageService {
fn calculate_triage(&self, vitals: &VitalSignsReading) -> TriageStatus;
fn should_upgrade_priority(&self, history: &VitalSignsHistory) -> bool;
}
```
**Rules**:
1. No breathing detected → Check for movement
2. Movement but no breathing → Immediate (airway issue)
3. Breathing > 30/min → Immediate
4. Breathing < 10/min → Immediate
5. No radial pulse equivalent (weak heartbeat) → Immediate
6. Cannot follow commands (no responsive movement) → Immediate
7. Otherwise → Delayed or Minor based on severity
### LocalizationService
Fuses multiple localization techniques:
```rust
pub trait LocalizationService {
fn estimate_position(
&self,
csi_data: &[CsiReading],
sensor_positions: &[SensorPosition],
) -> Result<Coordinates3D, LocalizationError>;
fn estimate_depth(
&self,
signal_attenuation: f64,
debris_profile: &DebrisProfile,
) -> Result<DepthEstimate, LocalizationError>;
}
```
---
## Context Map
```
┌────────────────────────────────────────────────────────────────┐
│ WiFi-Mat System │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Detection │◄───────►│ Localization│ │
│ │ Context │ Partner │ Context │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ │ Publishes │ Publishes │
│ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Event Bus (Domain Events) │ │
│ └─────────────────┬───────────────────┘ │
│ │ │
│ │ Subscribes │
│ ▼ │
│ ┌─────────────┐ │
│ │ Alerting │ │
│ │ Context │ │
│ └─────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────┤
│ UPSTREAM (Conformist) │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │wifi-densepose │ │wifi-densepose │ │wifi-densepose │ │
│ │ -signal │ │ -nn │ │ -hardware │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
**Relationship Types**:
- Detection ↔ Localization: **Partnership** (tight collaboration)
- Detection → Alerting: **Customer/Supplier** (Detection publishes, Alerting consumes)
- WiFi-Mat → Upstream crates: **Conformist** (adapts to their models)
---
## Anti-Corruption Layer
The integration module provides adapters to translate between upstream crate models and WiFi-Mat domain:
```rust
/// Adapts wifi-densepose-signal types to Detection context
pub struct SignalAdapter {
processor: CsiProcessor,
feature_extractor: FeatureExtractor,
}
impl SignalAdapter {
pub fn extract_vital_features(
&self,
raw_csi: &[Complex<f64>],
) -> Result<VitalFeatures, AdapterError>;
}
/// Adapts wifi-densepose-nn for specialized detection models
pub struct NeuralAdapter {
breathing_model: OnnxModel,
heartbeat_model: OnnxModel,
}
impl NeuralAdapter {
pub fn classify_breathing(
&self,
features: &VitalFeatures,
) -> Result<BreathingPattern, AdapterError>;
}
```
---
## Repository Interfaces
```rust
#[async_trait]
pub trait SurvivorRepository {
async fn save(&self, survivor: &Survivor) -> Result<(), RepositoryError>;
async fn find_by_id(&self, id: &SurvivorId) -> Result<Option<Survivor>, RepositoryError>;
async fn find_by_zone(&self, zone_id: &ScanZoneId) -> Result<Vec<Survivor>, RepositoryError>;
async fn find_active(&self) -> Result<Vec<Survivor>, RepositoryError>;
}
#[async_trait]
pub trait DisasterEventRepository {
async fn save(&self, event: &DisasterEvent) -> Result<(), RepositoryError>;
async fn find_active(&self) -> Result<Vec<DisasterEvent>, RepositoryError>;
async fn find_by_location(&self, location: &GeoLocation, radius_km: f64) -> Result<Vec<DisasterEvent>, RepositoryError>;
}
#[async_trait]
pub trait AlertRepository {
async fn save(&self, alert: &Alert) -> Result<(), RepositoryError>;
async fn find_pending(&self) -> Result<Vec<Alert>, RepositoryError>;
async fn find_by_survivor(&self, survivor_id: &SurvivorId) -> Result<Vec<Alert>, RepositoryError>;
}
```