diff options
| author | Dawid Rycerz <dawid@rycerz.xyz> | 2026-02-08 12:44:10 +0100 |
|---|---|---|
| committer | Dawid Rycerz <dawid@rycerz.xyz> | 2026-02-08 12:44:10 +0100 |
| commit | 0c20fb86633104744dbccf30ad732296694fff1b (patch) | |
| tree | 02ffb8494086960b4a84decf3bdc2c8c61bfc4f6 /src/protocol/frame.rs | |
Initial pipewiremain
Diffstat (limited to 'src/protocol/frame.rs')
| -rw-r--r-- | src/protocol/frame.rs | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/src/protocol/frame.rs b/src/protocol/frame.rs new file mode 100644 index 0000000..32475e2 --- /dev/null +++ b/src/protocol/frame.rs @@ -0,0 +1,259 @@ +//! UPP protocol frame structures + +use serde::{Deserialize, Serialize}; +use std::mem; + +/// UPP USB frame header (5 bytes) +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[repr(C, packed)] +pub struct UPPUsbFrame { + pub magic: u16, // 0xBBAA + pub cid: u8, // Camera ID + pub length: u16, // Data length (excluding header) +} + +/// UPP camera frame header (7 bytes) +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] +#[repr(C, packed)] +pub struct UPPFrameHeader { + pub frame_id: u8, // Frame ID + pub camera_number: u8, // Camera number + pub flags: UPPFlags, // Various flags + pub g_sensor: u32, // G-sensor data +} + +/// UPP frame flags +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)] +#[repr(C, packed)] +pub struct UPPFlags { + pub has_g: bool, // Has G-sensor data (bit 0) + pub button_press: bool, // Button press detected (bit 1) + pub other: u8, // Other flags (bits 2-7, 6 bits total) +} + +/// Complete UPP frame +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct UPPFrame { + pub header: UPPFrameHeader, + pub data: Vec<u8>, +} + +impl UPPUsbFrame { + /// Create a new UPP USB frame + pub fn new(cid: u8, length: u16) -> Self { + Self { + magic: super::UPP_USB_MAGIC, + cid, + length, + } + } + + /// Get the total frame size including header + /// Based on C++ POC: length field includes camera header + data, but not USB header + pub fn total_size(&self) -> usize { + mem::size_of::<Self>() + self.length as usize + } + + /// Validate the frame magic number + pub fn is_valid(&self) -> bool { + self.magic == super::UPP_USB_MAGIC + } + + /// Get the expected payload size (camera header + data) + /// Based on C++ POC: this is what the length field represents + pub fn expected_payload_size(&self) -> usize { + self.length as usize + } + + /// Get the expected data size (excluding camera header) + /// Camera header is 7 bytes, so data size is payload - 7 + pub fn expected_data_size(&self) -> usize { + if self.length >= 7 { + self.length as usize - 7 + } else { + 0 + } + } +} + +impl UPPFrameHeader { + /// Create a new UPP frame header + pub fn new( + frame_id: u8, + camera_number: u8, + has_g: bool, + button_press: bool, + g_sensor: u32, + ) -> Self { + Self { + frame_id, + camera_number, + flags: UPPFlags { + has_g, + button_press, + other: 0, + }, + g_sensor, + } + } + + /// Check if this frame has G-sensor data + pub fn has_g_sensor(&self) -> bool { + self.flags.has_g + } + + /// Check if button press was detected + pub fn button_pressed(&self) -> bool { + self.flags.button_press + } + + /// Get other flags + pub fn other_flags(&self) -> u8 { + self.flags.other + } + + /// Set other flags + pub fn set_other_flags(&mut self, flags: u8) { + self.flags.other = flags & 0x3F; // Only 6 bits + } + + /// Get G-sensor data + pub fn g_sensor_data(&self) -> Option<u32> { + if self.has_g_sensor() { + Some(self.g_sensor) + } else { + None + } + } +} + +impl UPPFrame { + /// Create a new UPP frame + pub fn new(header: UPPFrameHeader, data: Vec<u8>) -> Self { + Self { header, data } + } + + /// Get the total frame size + pub fn total_size(&self) -> usize { + mem::size_of::<UPPFrameHeader>() + self.data.len() + } + + /// Get the frame ID + pub fn frame_id(&self) -> u8 { + self.header.frame_id + } + + /// Get the camera number + pub fn camera_number(&self) -> u8 { + self.header.camera_number + } + + /// Check if button was pressed + pub fn button_pressed(&self) -> bool { + self.header.button_pressed() + } + + /// Get G-sensor data if available + pub fn g_sensor_data(&self) -> Option<u32> { + if self.header.has_g_sensor() { + Some(self.header.g_sensor) + } else { + None + } + } +} + +impl Default for UPPUsbFrame { + fn default() -> Self { + Self { + magic: super::UPP_USB_MAGIC, + cid: super::UPP_CAMID_7, + length: 0, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_upp_usb_frame_creation() { + let frame = UPPUsbFrame::new(7, 1024); + let magic = frame.magic; + let cid = frame.cid; + let length = frame.length; + assert_eq!(magic, super::super::UPP_USB_MAGIC); + assert_eq!(cid, 7); + assert_eq!(length, 1024); + assert!(frame.is_valid()); + } + + #[test] + fn test_upp_usb_frame_validation() { + let mut frame = UPPUsbFrame::new(7, 1024); + assert!(frame.is_valid()); + + frame.magic = 0x1234; + assert!(!frame.is_valid()); + } + + #[test] + fn test_upp_frame_header_creation() { + let header = UPPFrameHeader::new(1, 0, true, false, 12345); + assert_eq!(header.frame_id, 1); + assert_eq!(header.camera_number, 0); + assert!(header.has_g_sensor()); + assert!(!header.button_pressed()); + assert_eq!(header.g_sensor_data(), Some(12345)); + } + + #[test] + fn test_upp_frame_header_flags() { + let mut header = UPPFrameHeader::default(); + assert!(!header.has_g_sensor()); + assert!(!header.button_pressed()); + + header.flags.has_g = true; + header.flags.button_press = true; + assert!(header.has_g_sensor()); + assert!(header.button_pressed()); + } + + #[test] + fn test_upp_frame_creation() { + let header = UPPFrameHeader::new(1, 0, false, false, 0); + let data = vec![1, 2, 3, 4, 5]; + let frame = UPPFrame::new(header, data.clone()); + + assert_eq!(frame.frame_id(), 1); + assert_eq!(frame.camera_number(), 0); + assert_eq!(frame.data, data); + assert_eq!(frame.total_size(), mem::size_of::<UPPFrameHeader>() + 5); + } + + #[test] + fn test_upp_frame_defaults() { + let frame = UPPFrame::default(); + assert_eq!(frame.frame_id(), 0); + assert_eq!(frame.camera_number(), 0); + assert!(frame.data.is_empty()); + assert!(!frame.button_pressed()); + assert!(frame.g_sensor_data().is_none()); + } + + #[test] + fn test_upp_flags_other_bits() { + let mut header = UPPFrameHeader::default(); + header.set_other_flags(0xFF); + assert_eq!(header.other_flags(), 0x3F); // Only 6 bits should be set + } + + #[test] + fn test_memory_layout() { + // Ensure packed structs have correct sizes + assert_eq!(mem::size_of::<UPPUsbFrame>(), 5); + // UPPFrameHeader: frame_id(1) + camera_number(1) + flags(3) + g_sensor(4) = 9 bytes + assert_eq!(mem::size_of::<UPPFrameHeader>(), 9); + } +} |
