mirror of
https://github.com/ruvnet/RuView
synced 2026-08-11 20:41:44 +00:00
49c594822f
The layer that turns the certificate spine into a modality-agnostic perception substrate. Four crates, all deterministic and green independently (43 tests). ruview-hal (ADR-317): one abstraction mapping any modality (CSI/802.11bf/BLE/ UWB/mmWave/acoustic/camera/lidar/IMU/custom) to a canonical ontology Observation. SensorHal trait + two SYNTHETIC/L0 reference adapters; malformed input yields a degraded UNKNOWN observation, never a panic; synthetic can never alias measured. 8 tests. ruview-groundtruth (ADR-300): reference sensors as a formal VALIDATION plane (never an estimator input, enforced by the type boundary); modality-agnostic ReferenceSeries, deterministic cross-correlation alignment, AgreementReport with mandatory SessionScope, emitting per-context ruview-evidence records; Measured requires reference + coverage + reproducer. 15 tests. ruview-track (ADR-304): privacy-preserving persistent tracks (opaque person ids, coarse non-reversible features, no civil-identity binding); ambiguous detections stay tentative rather than misassigned; cross-zone hand-off. 8 tests. ruview-fusion (ADR-308): multiple HalObservations -> one probabilistic WorldState, uncertainty-aware (confidence-weighted, not naive averaging); irreconcilable conflict or insufficient coverage yields UNKNOWN, not a confident average. 9+ tests incl. irreconcilable_conflict_yields_unknown. Flips ADR-300/304/308/317 to implemented; registers the four crates as workspace members. SYNTHETIC/L0 throughout; no hardware/MEASURED claims. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_015TcKegTS7QqhWPC2L2SzaS
94 lines
2.9 KiB
Rust
94 lines
2.9 KiB
Rust
//! Mandatory session scope (ADR-300 §3, mirroring ADR-290).
|
||
//!
|
||
//! An agreement report without scope cannot be constructed: WiFi-sensing
|
||
//! numbers without stated scope (subject count, motion, line-of-sight,
|
||
//! distance) are systematically misleading (ADR-290 Context). [`SessionScope`]
|
||
//! is a required argument to [`crate::AgreementReport::build`], so the type
|
||
//! system enforces the rule.
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
use crate::error::GroundTruthError;
|
||
|
||
/// The largest subject count accepted, bounding untrusted input.
|
||
pub const MAX_SUBJECTS: u16 = 4096;
|
||
|
||
/// Whether subjects were static or moving during the session.
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||
#[serde(rename_all = "snake_case")]
|
||
pub enum MotionState {
|
||
/// Subject(s) static / at rest.
|
||
Static,
|
||
/// Subject(s) moving.
|
||
Moving,
|
||
/// A mix of static and moving intervals.
|
||
Mixed,
|
||
}
|
||
|
||
/// The propagation condition between sensor and subject.
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||
#[serde(rename_all = "snake_case")]
|
||
pub enum LineOfSight {
|
||
/// Line-of-sight.
|
||
Los,
|
||
/// Non-line-of-sight (obstructed, same room).
|
||
Nlos,
|
||
/// Through-wall.
|
||
ThroughWall,
|
||
}
|
||
|
||
/// A coarse distance band between sensor and subject.
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||
#[serde(rename_all = "snake_case")]
|
||
pub enum DistanceBand {
|
||
/// Near (roughly < 2 m).
|
||
Near,
|
||
/// Mid (roughly 2–5 m).
|
||
Mid,
|
||
/// Far (roughly > 5 m).
|
||
Far,
|
||
}
|
||
|
||
/// Mandatory metadata attached to every [`crate::AgreementReport`]. A report
|
||
/// cannot exist without it, so an agreement number always states the conditions
|
||
/// it was measured under.
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||
pub struct SessionScope {
|
||
/// Number of subjects present (0 is valid for an empty-room session).
|
||
pub subject_count: u16,
|
||
/// Motion state during the session.
|
||
pub motion: MotionState,
|
||
/// Line-of-sight condition.
|
||
pub line_of_sight: LineOfSight,
|
||
/// Distance band.
|
||
pub distance: DistanceBand,
|
||
}
|
||
|
||
impl SessionScope {
|
||
/// Construct a validated session scope. `subject_count` is bounded to
|
||
/// [`MAX_SUBJECTS`] so untrusted metadata cannot claim an absurd count.
|
||
///
|
||
/// # Errors
|
||
/// [`GroundTruthError::SubjectCountTooLarge`] if `subject_count` exceeds
|
||
/// [`MAX_SUBJECTS`].
|
||
pub fn new(
|
||
subject_count: u16,
|
||
motion: MotionState,
|
||
line_of_sight: LineOfSight,
|
||
distance: DistanceBand,
|
||
) -> Result<Self, GroundTruthError> {
|
||
if subject_count > MAX_SUBJECTS {
|
||
return Err(GroundTruthError::SubjectCountTooLarge {
|
||
count: u32::from(subject_count),
|
||
max: u32::from(MAX_SUBJECTS),
|
||
});
|
||
}
|
||
Ok(Self {
|
||
subject_count,
|
||
motion,
|
||
line_of_sight,
|
||
distance,
|
||
})
|
||
}
|
||
}
|