nv_view/camera_motion.rs
1//! Camera motion state and motion source types.
2
3/// Whether the camera is stable, moving, or in an unknown state.
4///
5/// Central to the view system's decision-making. Stages receive this
6/// through [`ViewSnapshot`](crate::ViewSnapshot).
7#[derive(Clone, Debug, PartialEq)]
8pub enum CameraMotionState {
9 /// Camera is not moving. Coordinates are stable across frames.
10 Stable,
11
12 /// Camera is actively moving.
13 Moving {
14 /// Angular velocity in degrees/second, if known.
15 angular_velocity: Option<f32>,
16 /// Estimated frame-to-frame displacement magnitude (normalized coordinates).
17 displacement: Option<f32>,
18 },
19
20 /// Camera motion state is unknown.
21 ///
22 /// Occurs when no telemetry is available, no estimator is configured,
23 /// or the estimator's confidence is below threshold. Treated as potentially
24 /// moving for safety — never assumed stable.
25 Unknown,
26}
27
28/// How the current motion state was determined.
29///
30/// Critical for downstream trust decisions — telemetry-sourced motion
31/// is more trustworthy than inferred motion from noisy optical flow.
32#[derive(Clone, Debug, PartialEq)]
33pub enum MotionSource {
34 /// Determined from PTZ telemetry (ONVIF, serial, etc.).
35 Telemetry,
36
37 /// Inferred from video analysis (optical flow, feature matching, homography).
38 Inferred {
39 /// Confidence score in `[0.0, 1.0]`.
40 confidence: f32,
41 },
42
43 /// From a user-supplied external system.
44 External,
45
46 /// No motion information available for this frame.
47 None,
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn stable_is_not_moving() {
56 assert!(!matches!(
57 CameraMotionState::Stable,
58 CameraMotionState::Moving { .. }
59 ));
60 }
61
62 #[test]
63 fn moving_carries_optional_fields() {
64 let m = CameraMotionState::Moving {
65 angular_velocity: Some(30.0),
66 displacement: Some(0.15),
67 };
68 if let CameraMotionState::Moving {
69 angular_velocity,
70 displacement,
71 } = m
72 {
73 assert_eq!(angular_velocity, Some(30.0));
74 assert_eq!(displacement, Some(0.15));
75 } else {
76 panic!("expected Moving");
77 }
78 }
79
80 #[test]
81 fn unknown_treated_as_potentially_moving() {
82 // Verify Unknown is distinct from both Stable and Moving.
83 let u = CameraMotionState::Unknown;
84 assert_ne!(u, CameraMotionState::Stable);
85 assert!(!matches!(u, CameraMotionState::Moving { .. }));
86 }
87
88 #[test]
89 fn motion_source_variants() {
90 assert_ne!(MotionSource::Telemetry, MotionSource::None);
91 let inferred = MotionSource::Inferred { confidence: 0.7 };
92 assert!(matches!(inferred, MotionSource::Inferred { confidence } if confidence > 0.5));
93 }
94}