ydlidar_rust_driver/types.rs
1use crate::cmd::TMiniPlusCmd;
2
3/// # Example
4///
5/// ```
6/// use ydlidar::types::{LidarCommands, TMiniPlus};
7///
8/// let start_cmd = TMiniPlus::start_scan_cmd();
9/// assert_eq!(start_cmd, &[0xA5, 0x60]);
10/// ```
11pub trait LidarCommands {
12 /// The header structure for this LIDAR model's data packets.
13 type Header;
14
15 /// Get the command to start scanning.
16 ///
17 /// Returns a reference to the command byte array.
18 fn start_scan_cmd() -> &'static [u8; 2];
19
20 /// Get the command to stop scanning.
21 ///
22 /// Returns a reference to the command byte array.
23 fn stop_scan_cmd() -> &'static [u8; 2];
24
25 /// Get the command to retrieve device information.
26 ///
27 /// Returns a reference to the command byte array.
28 fn get_info_cmd() -> &'static [u8; 2];
29}
30
31/// YDLidar T-mini Plus model implementation.
32///
33/// This struct represents the T-mini Plus LIDAR model and implements
34/// the necessary traits for device communication.
35#[derive(Debug, Clone, Copy)]
36pub struct TMiniPlus;
37
38impl LidarCommands for TMiniPlus {
39 type Header = TMiniHeader;
40
41 fn start_scan_cmd() -> &'static [u8; 2] {
42 TMiniPlusCmd::START_SCAN.as_cmd()
43 }
44
45 fn stop_scan_cmd() -> &'static [u8; 2] {
46 TMiniPlusCmd::STOP_SCAN.as_cmd()
47 }
48
49 fn get_info_cmd() -> &'static [u8; 2] {
50 TMiniPlusCmd::GET_INFO.as_cmd()
51 }
52}
53
54/// Data packet header for T-mini Plus.
55///
56/// # Fields
57///
58/// - `header`: Start marker (0x55AA)
59/// - `ct`: Control byte containing scan frequency and package type
60/// - `lsn`: Sample count in this packet
61/// - `fsa`: First sample angle (raw value)
62/// - `lsa`: Last sample angle (raw value)
63/// - `cs`: Checksum for data integrity
64#[derive(Debug, Clone, Copy)]
65#[repr(C)]
66pub struct TMiniHeader {
67 /// Start marker (0x55AA).
68 pub header: u16,
69 /// Control byte: scan frequency and package type.
70 pub ct: u8,
71 /// Sample count in this packet.
72 pub lsn: u8,
73 /// First sample angle (raw value).
74 pub fsa: u16,
75 /// Last sample angle (raw value).
76 pub lsa: u16,
77 /// Checksum for data integrity.
78 pub cs: u16,
79}
80
81impl TMiniHeader {
82 /// Create a header from raw bytes.
83 ///
84 /// # Arguments
85 ///
86 /// * `data` - Raw byte array containing header data
87 ///
88 /// # Returns
89 ///
90 /// A new `TMiniHeader` instance parsed from the bytes.
91 pub fn from_bytes(data: &[u8]) -> Option<Self> {
92 if data.len() < 10 {
93 return None;
94 }
95
96 Some(TMiniHeader {
97 header: u16::from_le_bytes([data[0], data[1]]),
98 ct: data[2],
99 lsn: data[3],
100 fsa: u16::from_le_bytes([data[4], data[5]]),
101 lsa: u16::from_le_bytes([data[6], data[7]]),
102 cs: u16::from_le_bytes([data[8], data[9]]),
103 })
104 }
105
106 /// Check if the header is valid.
107 ///
108 /// Verifies that the start marker is correct (0x55AA).
109 pub fn is_valid(&self) -> bool {
110 self.header == 0x55AA // Little endian: bytes are AA 55, value is 0x55AA
111 }
112
113 /// Get the scan frequency from the control byte.
114 ///
115 /// # Returns
116 ///
117 /// The scan frequency in Hz.
118 pub fn scan_frequency(&self) -> f32 {
119 match self.ct & 0x03 {
120 0 => 5.0,
121 1 => 8.0,
122 2 => 10.0,
123 _ => 0.0,
124 }
125 }
126}
127
128/// Each sample in a scan packet contains distance and intensity information.
129#[derive(Debug, Clone, Copy)]
130#[repr(C)]
131pub struct SampleNode {
132 /// Signal intensity (0-255).
133 pub intensity: u8,
134 /// Low byte of distance value.
135 pub distance_low: u8,
136 /// High byte of distance and flag bits.
137 pub distance_high_and_flag: u8,
138}
139
140impl SampleNode {
141 /// # Returns
142 ///
143 /// Distance in millimeters.
144 pub fn distance(&self) -> u16 {
145 // where s1=intensity, s2=distance_low, s3=distance_high_and_flag
146 ((self.distance_high_and_flag as u16) << 6) | ((self.distance_low as u16) >> 2)
147 }
148
149 /// # Returns
150 ///
151 /// True if the sample should be discarded.
152 pub fn is_invalid(&self) -> bool {
153 (self.distance_high_and_flag & 0x80) != 0
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_tmini_header_parsing() {
163 let data = [0x55, 0xAA, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
164 let header = TMiniHeader::from_bytes(&data).unwrap();
165 assert!(header.is_valid());
166 assert_eq!(header.lsn, 1);
167 }
168
169 #[test]
170 fn test_sample_node_distance() {
171 let sample = SampleNode {
172 intensity: 100,
173 distance_low: 0x34,
174 distance_high_and_flag: 0x12,
175 };
176 assert_eq!(sample.distance(), 0x1234);
177 assert!(!sample.is_invalid());
178 }
179}