laser_dac/protocols/helios/
frame.rs1use bitflags::bitflags;
4
5use crate::point::LaserPoint;
6
7#[derive(Debug, Clone, PartialEq)]
9pub struct Frame {
10 pub pps: u32,
12 pub flags: WriteFrameFlags,
14 pub points: Vec<Point>,
16}
17
18impl Frame {
19 pub fn new(pps: u32, points: Vec<Point>) -> Self {
25 Frame {
26 pps,
27 points,
28 flags: WriteFrameFlags::SINGLE_MODE,
29 }
30 }
31
32 pub fn new_with_flags(pps: u32, points: Vec<Point>, flags: WriteFrameFlags) -> Self {
34 Frame { pps, points, flags }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq)]
40pub struct Point {
41 pub coordinate: Coordinate,
43 pub color: Color,
45 pub intensity: u8,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq)]
53pub struct Coordinate {
54 pub x: u16,
55 pub y: u16,
56}
57
58impl From<(u16, u16)> for Coordinate {
59 fn from((x, y): (u16, u16)) -> Self {
60 Coordinate { x, y }
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq)]
66pub struct Color {
67 pub r: u8,
69 pub g: u8,
71 pub b: u8,
73}
74
75impl Color {
76 pub fn new(r: u8, g: u8, b: u8) -> Self {
78 Color { r, g, b }
79 }
80}
81
82bitflags! {
83 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
85 pub struct WriteFrameFlags: u8 {
86 const START_IMMEDIATELY = 0b0000_0001;
88 const SINGLE_MODE = 0b0000_0010;
90 const DONT_BLOCK = 0b0000_0100;
92 }
93}
94
95impl From<&LaserPoint> for Point {
96 fn from(p: &LaserPoint) -> Self {
101 Point {
102 coordinate: Coordinate {
103 x: LaserPoint::coord_to_u12_inverted(p.x),
104 y: LaserPoint::coord_to_u12_inverted(p.y),
105 },
106 color: Color::new(
107 LaserPoint::color_to_u8(p.r),
108 LaserPoint::color_to_u8(p.g),
109 LaserPoint::color_to_u8(p.b),
110 ),
111 intensity: LaserPoint::color_to_u8(p.intensity),
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
129 fn test_helios_conversion_center() {
130 let laser_point = LaserPoint::new(0.0, 0.0, 128 * 257, 64 * 257, 32 * 257, 200 * 257);
133 let helios_point: Point = (&laser_point).into();
134
135 assert_eq!(helios_point.coordinate.x, 2048);
137 assert_eq!(helios_point.coordinate.y, 2048);
138 assert_eq!(helios_point.color.r, 128);
140 assert_eq!(helios_point.color.g, 64);
141 assert_eq!(helios_point.color.b, 32);
142 assert_eq!(helios_point.intensity, 200);
143 }
144
145 #[test]
146 fn test_helios_conversion_boundaries() {
147 let min = LaserPoint::new(-1.0, -1.0, 0, 0, 0, 0);
149 let min_helios: Point = (&min).into();
150 assert_eq!(min_helios.coordinate.x, 4095);
151 assert_eq!(min_helios.coordinate.y, 4095);
152
153 let max = LaserPoint::new(1.0, 1.0, 0, 0, 0, 0);
155 let max_helios: Point = (&max).into();
156 assert_eq!(max_helios.coordinate.x, 0);
157 assert_eq!(max_helios.coordinate.y, 0);
158 }
159
160 #[test]
161 fn test_helios_conversion_asymmetric() {
162 let laser_point = LaserPoint::new(-0.5, 0.5, 0, 0, 0, 0);
164 let helios_point: Point = (&laser_point).into();
165
166 assert_eq!(helios_point.coordinate.x, 3071);
169 assert_eq!(helios_point.coordinate.y, 1024);
170 }
171
172 #[test]
173 fn test_helios_conversion_clamps_out_of_range() {
174 let positive = LaserPoint::new(2.0, 3.0, 0, 0, 0, 0);
176 let positive_helios: Point = (&positive).into();
177 assert_eq!(positive_helios.coordinate.x, 0);
178 assert_eq!(positive_helios.coordinate.y, 0);
179
180 let negative = LaserPoint::new(-2.0, -3.0, 0, 0, 0, 0);
182 let negative_helios: Point = (&negative).into();
183 assert_eq!(negative_helios.coordinate.x, 4095);
184 assert_eq!(negative_helios.coordinate.y, 4095);
185 }
186
187 #[test]
188 fn test_helios_inversion_symmetry() {
189 let p1 = LaserPoint::new(0.5, 0.0, 0, 0, 0, 0);
192 let p2 = LaserPoint::new(-0.5, 0.0, 0, 0, 0, 0);
193 let h1: Point = (&p1).into();
194 let h2: Point = (&p2).into();
195
196 let sum = h1.coordinate.x as i32 + h2.coordinate.x as i32;
198 assert!((sum - 4095).abs() <= 1, "Sum was {}, expected ~4095", sum);
199 }
200
201 #[test]
202 fn test_helios_conversion_infinity_clamps() {
203 let laser_point = LaserPoint::new(f32::INFINITY, f32::NEG_INFINITY, 0, 0, 0, 0);
204 let helios_point: Point = (&laser_point).into();
205
206 assert_eq!(helios_point.coordinate.x, 0);
208 assert_eq!(helios_point.coordinate.y, 4095);
209 }
210
211 #[test]
212 fn test_helios_conversion_nan_does_not_panic() {
213 let laser_point = LaserPoint::new(
215 f32::NAN,
216 f32::NAN,
217 100 * 257,
218 100 * 257,
219 100 * 257,
220 100 * 257,
221 );
222 let helios_point: Point = (&laser_point).into();
223
224 assert!(helios_point.coordinate.x <= 4095);
226 assert!(helios_point.coordinate.y <= 4095);
227 }
228}