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
18 KiB
18 KiB
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:
BreathingPatternHeartbeatSignatureMovementProfileConfidenceScore
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:
Coordinates3DDepthEstimateLocationUncertaintyDebrisProfile
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:
TriageStatusPriorityAlertPayload
Core Domain Entities
Survivor (Entity)
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)
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)
pub struct ScanZone {
id: ScanZoneId,
bounds: ZoneBounds,
sensor_positions: Vec<SensorPosition>,
scan_parameters: ScanParameters,
status: ZoneStatus,
last_scan: DateTime<Utc>,
}
Value Objects
VitalSignsReading
pub struct VitalSignsReading {
breathing: Option<BreathingPattern>,
heartbeat: Option<HeartbeatSignature>,
movement: MovementProfile,
timestamp: DateTime<Utc>,
confidence: ConfidenceScore,
}
TriageStatus (Enumeration)
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
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
pub struct HeartbeatSignature {
rate_bpm: f32, // Beats per minute (normal: 60-100)
variability: f32, // Heart rate variability
strength: SignalStrength,
}
Coordinates3D
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
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
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:
pub trait TriageService {
fn calculate_triage(&self, vitals: &VitalSignsReading) -> TriageStatus;
fn should_upgrade_priority(&self, history: &VitalSignsHistory) -> bool;
}
Rules:
- No breathing detected → Check for movement
- Movement but no breathing → Immediate (airway issue)
- Breathing > 30/min → Immediate
- Breathing < 10/min → Immediate
- No radial pulse equivalent (weak heartbeat) → Immediate
- Cannot follow commands (no responsive movement) → Immediate
- Otherwise → Delayed or Minor based on severity
LocalizationService
Fuses multiple localization techniques:
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:
/// 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
#[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>;
}