nv_view/ptz.rs
1//! PTZ telemetry types and discrete control events.
2
3use nv_core::MonotonicTs;
4
5/// Raw PTZ (Pan-Tilt-Zoom) telemetry from the camera.
6///
7/// Provided by `ViewStateProvider` implementations that have access
8/// to PTZ control systems (ONVIF, serial protocols, etc.).
9#[derive(Clone, Debug, PartialEq)]
10pub struct PtzTelemetry {
11 /// Pan angle in degrees.
12 pub pan: f32,
13 /// Tilt angle in degrees.
14 pub tilt: f32,
15 /// Zoom level, normalized to `[0, 1]` where `1` = maximum zoom.
16 pub zoom: f32,
17 /// Timestamp of this telemetry reading.
18 pub ts: MonotonicTs,
19}
20
21/// A discrete PTZ control event.
22///
23/// Models push-based PTZ control commands that the camera may receive
24/// externally (from an operator, a tour scheduler, or an API call).
25/// These supplement the polling-based [`PtzTelemetry`] with explicit
26/// causal information about **why** the camera moved.
27///
28/// Providers include these in [`MotionReport::ptz_events`](crate::MotionReport::ptz_events)
29/// when they have access to a PTZ command stream (e.g., ONVIF Events).
30#[derive(Clone, Debug, PartialEq)]
31pub enum PtzEvent {
32 /// A continuous move started (pan/tilt/zoom speeds set).
33 MoveStart {
34 /// Timestamp of the command.
35 ts: MonotonicTs,
36 },
37 /// A continuous move stopped.
38 MoveStop {
39 /// Timestamp of the command.
40 ts: MonotonicTs,
41 },
42 /// Camera moved to a preset position.
43 PresetRecall {
44 /// Numeric preset identifier.
45 preset_id: u32,
46 /// Timestamp of the command.
47 ts: MonotonicTs,
48 },
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn ptz_telemetry_clone_eq() {
57 let t = PtzTelemetry {
58 pan: 45.0,
59 tilt: -10.0,
60 zoom: 0.5,
61 ts: MonotonicTs::from_nanos(1_000_000),
62 };
63 let t2 = t.clone();
64 assert_eq!(t, t2);
65 }
66
67 #[test]
68 fn ptz_event_variants() {
69 let ts = MonotonicTs::from_nanos(100);
70
71 let start = PtzEvent::MoveStart { ts };
72 let stop = PtzEvent::MoveStop { ts };
73 let preset = PtzEvent::PresetRecall { preset_id: 3, ts };
74
75 // Each variant is distinct.
76 assert_ne!(start, stop);
77 assert_ne!(stop, preset.clone());
78
79 // Clone round-trips.
80 assert_eq!(preset.clone(), preset);
81 }
82}