Skip to main content

rplidar_drv/
lib.rs

1//! # Rplidar Driver
2//!
3//! `rplidar_drv` is driver for Slamtec Rplidar series
4
5extern crate byteorder;
6extern crate crc;
7
8mod answers;
9mod capsuled_parser;
10mod checksum;
11mod cmds;
12mod errors;
13mod internals;
14mod prelude;
15mod protocol;
16pub mod rpos_drv;
17mod ultra_capsuled_parser;
18pub mod utils;
19
20pub use self::errors::*;
21pub use self::prelude::*;
22
23pub use self::answers::RplidarResponseDeviceInfo;
24
25use self::answers::*;
26use self::capsuled_parser::parse_capsuled;
27use self::checksum::Checksum;
28use self::cmds::*;
29use self::internals::*;
30pub use self::protocol::RplidarHostProtocol;
31use self::ultra_capsuled_parser::parse_ultra_capsuled;
32use byteorder::{ByteOrder, LittleEndian};
33use crc::crc32;
34pub use rpos_drv::{Channel, Message, Result};
35use std::collections::VecDeque;
36use std::io::{Read, Write};
37use std::mem::transmute_copy;
38use std::time::{Duration, Instant};
39
40const RPLIDAR_GET_LIDAR_CONF_START_VERSION: u16 = ((1 << 8) | (24)) as u16;
41
42/// Rplidar device driver
43#[derive(Debug)]
44pub struct RplidarDevice<T: ?Sized> {
45    channel: Channel<RplidarHostProtocol, T>,
46    cached_measurement_nodes: VecDeque<ScanPoint>,
47    cached_prev_capsule: CachedPrevCapsule,
48}
49
50macro_rules! parse_resp_data {
51    ($x:expr, $t:ty) => {{
52        const SIZE: usize = std::mem::size_of::<$t>();
53        if $x.len() != SIZE {
54            Err(Error::from(RposError::OperationFail {
55                description: "answer type mismatch".to_owned(),
56            }))
57        } else {
58            let mut slice = [0u8; SIZE];
59            slice.clone_from_slice(&$x[..]);
60            Ok(unsafe { transmute_copy::<[u8; SIZE], $t>(&slice) })
61        }
62    }};
63}
64
65macro_rules! parse_resp {
66    ($x:expr, $t:ty) => {
67        parse_resp_data!($x.data, $t)
68    };
69}
70
71macro_rules! handle_resp {
72    ($ans:expr, $x:expr, $t:ty) => {
73        if $x.cmd != $ans {
74            Err(RposError::OperationFail {
75                description: "answer type mismatch".to_owned(),
76            }
77            .into())
78        } else {
79            parse_resp!($x, $t)
80        }
81    };
82}
83
84impl From<RplidarResponseMeasurementNodeHq> for ScanPoint {
85    fn from(p: RplidarResponseMeasurementNodeHq) -> ScanPoint {
86        ScanPoint {
87            angle_z_q14: p.angle_z_q14,
88            dist_mm_q2: p.dist_mm_q2,
89            quality: p.quality,
90            flag: p.flag,
91        }
92    }
93}
94
95impl<T: ?Sized> RplidarDevice<T>
96where
97    T: Read + Write,
98{
99    /// Construct a new RplidarDevice with channel
100    ///
101    /// # Example
102    /// ```ignore
103    /// let mut serial_port = serialport::open(serial_port_name)?;
104    /// let channel = Channel::new(RplidarHostProtocol::new(), serial_port);
105    /// let rplidar_device = RplidarDevice::new(channel);
106    /// ```
107    pub fn new(channel: Channel<RplidarHostProtocol, T>) -> RplidarDevice<T> {
108        RplidarDevice {
109            channel,
110            cached_measurement_nodes: VecDeque::with_capacity(RPLIDAR_DEFAULT_CACHE_DEPTH),
111            cached_prev_capsule: CachedPrevCapsule::None,
112        }
113    }
114
115    /// Construct a new RplidarDevice with stream
116    ///
117    /// # Example
118    /// ```ignore
119    /// let mut serial_port = serialport::open(serial_port_name)?;
120    /// let rplidar_device = RplidarDevice::with_stream(serial_port);
121    /// ```
122    pub fn with_stream(stream: Box<T>) -> RplidarDevice<T> {
123        RplidarDevice::<T>::new(rpos_drv::Channel::new(RplidarHostProtocol::new(), stream))
124    }
125
126    /// get device info of the RPLIDAR
127    pub fn get_device_info(&mut self) -> Result<RplidarResponseDeviceInfo> {
128        self.get_device_info_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
129    }
130
131    /// get device info of the RPLIDAR with timeout
132    pub fn get_device_info_with_timeout(
133        &mut self,
134        timeout: Duration,
135    ) -> Result<RplidarResponseDeviceInfo> {
136        if let Some(msg) = self
137            .channel
138            .invoke(&Message::new(RPLIDAR_CMD_GET_DEVICE_INFO), timeout)?
139        {
140            return handle_resp!(RPLIDAR_ANS_TYPE_DEVINFO, msg, RplidarResponseDeviceInfo);
141        }
142
143        Err(RposError::OperationTimeout.into())
144    }
145
146    /// Stop lidar
147    pub fn stop(&mut self) -> Result<()> {
148        self.channel.write(&Message::new(RPLIDAR_CMD_STOP))?;
149        Ok(())
150    }
151
152    /// Reset RPLIDAR core
153    pub fn core_reset(&mut self) -> Result<()> {
154        self.channel.write(&Message::new(RPLIDAR_CMD_RESET))?;
155        Ok(())
156    }
157
158    /// Set motor PWM (via accessory board)
159    pub fn set_motor_pwm(&mut self, pwm: u16) -> Result<()> {
160        let mut payload = [0; 2];
161        LittleEndian::write_u16(&mut payload, pwm);
162
163        self.channel
164            .write(&Message::with_data(RPLIDAR_CMD_SET_MOTOR_PWM, &payload))?;
165
166        Ok(())
167    }
168
169    /// Stop motor
170    pub fn stop_motor(&mut self) -> Result<()> {
171        self.set_motor_pwm(0)
172    }
173
174    /// Start motor
175    pub fn start_motor(&mut self) -> Result<()> {
176        self.set_motor_pwm(RPLIDAR_DEFAULT_MOTOR_PWM)
177    }
178
179    /*
180    /// Get LIDAR config
181    fn get_lidar_conf(&mut self, config_type: u32) -> Result<Vec<u8>> {
182        self.get_lidar_conf_with_timeout(config_type, RPLIDAR_DEFAULT_TIMEOUT)
183    }
184
185    /// get lidar config with parameter
186    fn get_lidar_conf_with_param(&mut self, config_type: u32, param: &[u8]) -> Result<Vec<u8>> {
187        self.get_lidar_conf_with_param_and_timeout(config_type, param, RPLIDAR_DEFAULT_TIMEOUT)
188    }
189    */
190
191    /// get lidar config with timeout
192    fn get_lidar_conf_with_timeout(
193        &mut self,
194        config_type: u32,
195        timeout: Duration,
196    ) -> Result<Vec<u8>> {
197        self.get_lidar_conf_with_param_and_timeout(config_type, &[], timeout)
198    }
199
200    /// get lidar config with parameter and timeout
201    fn get_lidar_conf_with_param_and_timeout(
202        &mut self,
203        config_type: u32,
204        param: &[u8],
205        timeout: Duration,
206    ) -> Result<Vec<u8>> {
207        let mut msg = Message::with_data(RPLIDAR_CMD_GET_LIDAR_CONF, &[0; 4]);
208
209        LittleEndian::write_u32(&mut msg.data, config_type);
210        msg.data.extend_from_slice(param);
211
212        let response = self.channel.invoke(&msg, timeout)?;
213
214        if let Some(mut response_msg) = response {
215            if response_msg.cmd != RPLIDAR_ANS_TYPE_GET_LIDAR_CONF {
216                Err(RposError::OperationFail {
217                    description: "answer type mismatch".to_owned(),
218                }
219                .into())
220            } else if response_msg.data.len() < 4
221                || LittleEndian::read_u32(&response_msg.data[0..4]) != config_type
222            {
223                return Err(RposError::OperationFail {
224                    description: "answer config type mismatch".to_owned(),
225                }
226                .into());
227            } else {
228                return Ok(response_msg.data.split_off(4));
229            }
230        } else {
231            Err(RposError::OperationTimeout.into())
232        }
233    }
234
235    /// get typical scan mode of target LIDAR
236    pub fn get_typical_scan_mode(&mut self) -> Result<u16> {
237        self.get_typical_scan_mode_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
238    }
239
240    /// get typical scan mode of target LIDAR with timeout
241    pub fn get_typical_scan_mode_with_timeout(&mut self, timeout: Duration) -> Result<u16> {
242        let device_info = self.get_device_info_with_timeout(timeout)?;
243
244        if device_info.firmware_version < RPLIDAR_GET_LIDAR_CONF_START_VERSION {
245            return Ok(if device_info.model >= 0x20u8 {
246                1u16
247            } else {
248                0u16
249            });
250        }
251
252        let scan_mode_data =
253            self.get_lidar_conf_with_timeout(RPLIDAR_CONF_SCAN_MODE_TYPICAL, timeout)?;
254
255        parse_resp_data!(scan_mode_data, u16)
256    }
257
258    /// get lidar sample duration
259    fn get_scan_mode_us_per_sample_with_timeout(
260        &mut self,
261        scan_mode: u16,
262        timeout: Duration,
263    ) -> Result<f32> {
264        let mut param = [0; 2];
265        LittleEndian::write_u16(&mut param, scan_mode);
266        let us_per_sample_data = self.get_lidar_conf_with_param_and_timeout(
267            RPLIDAR_CONF_SCAN_MODE_US_PER_SAMPLE,
268            &param,
269            timeout,
270        )?;
271        let us_per_sample = (parse_resp_data!(us_per_sample_data, u32)? as f32) / 256f32;
272        Ok(us_per_sample)
273    }
274
275    /// get lidar scan mode max distance
276    fn get_scan_mode_max_distance_with_timeout(
277        &mut self,
278        scan_mode: u16,
279        timeout: Duration,
280    ) -> Result<f32> {
281        let mut param = [0; 2];
282        LittleEndian::write_u16(&mut param, scan_mode);
283        let max_distance_data = self.get_lidar_conf_with_param_and_timeout(
284            RPLIDAR_CONF_SCAN_MODE_MAX_DISTANCE,
285            &param,
286            timeout,
287        )?;
288        let max_distance = (parse_resp_data!(max_distance_data, u32)? as f32) / 256f32;
289        Ok(max_distance)
290    }
291
292    /// get scan mode answer type
293    fn get_scan_mode_ans_type_with_timeout(
294        &mut self,
295        scan_mode: u16,
296        timeout: Duration,
297    ) -> Result<u8> {
298        let mut param = [0; 2];
299        LittleEndian::write_u16(&mut param, scan_mode);
300        let ans_type_data = self.get_lidar_conf_with_param_and_timeout(
301            RPLIDAR_CONF_SCAN_MODE_ANS_TYPE,
302            &param,
303            timeout,
304        )?;
305        parse_resp_data!(ans_type_data, u8)
306    }
307
308    /// get scan mode name
309    fn get_scan_mode_name_with_timeout(
310        &mut self,
311        scan_mode: u16,
312        timeout: Duration,
313    ) -> Result<String> {
314        let mut param = [0; 2];
315        LittleEndian::write_u16(&mut param, scan_mode);
316        let ans_type_data = self.get_lidar_conf_with_param_and_timeout(
317            RPLIDAR_CONF_SCAN_MODE_NAME,
318            &param,
319            timeout,
320        )?;
321
322        if let Ok(name) = std::str::from_utf8(&ans_type_data) {
323            Ok(name.to_owned().trim_matches('\0').to_owned())
324        } else {
325            Err(RposError::ProtocolError {
326                description: "invalid scan mode name".to_owned(),
327            }
328            .into())
329        }
330    }
331
332    /// get scan mode count
333    fn get_scan_mode_count_with_timeout(&mut self, timeout: Duration) -> Result<u16> {
334        let scan_mode_count_data =
335            self.get_lidar_conf_with_timeout(RPLIDAR_CONF_SCAN_MODE_COUNT, timeout)?;
336        parse_resp_data!(scan_mode_count_data, u16)
337    }
338
339    /// get scan mode of specific scan mode id
340    fn get_scan_mode_with_timeout(
341        &mut self,
342        scan_mode: u16,
343        timeout: Duration,
344    ) -> Result<ScanMode> {
345        Ok(ScanMode {
346            id: scan_mode,
347            us_per_sample: self.get_scan_mode_us_per_sample_with_timeout(scan_mode, timeout)?,
348            max_distance: self.get_scan_mode_max_distance_with_timeout(scan_mode, timeout)?,
349            ans_type: self.get_scan_mode_ans_type_with_timeout(scan_mode, timeout)?,
350            name: self.get_scan_mode_name_with_timeout(scan_mode, timeout)?,
351        })
352    }
353
354    /// get all supported scan modes supported by the LIDAR
355    pub fn get_all_supported_scan_modes(&mut self) -> Result<Vec<ScanMode>> {
356        self.get_all_supported_scan_modes_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
357    }
358
359    /// get all supported scan modes supported by the LIDAR with timeout
360    pub fn get_all_supported_scan_modes_with_timeout(
361        &mut self,
362        timeout: Duration,
363    ) -> Result<Vec<ScanMode>> {
364        let device_info = self.get_device_info_with_timeout(timeout)?;
365
366        if device_info.firmware_version < RPLIDAR_GET_LIDAR_CONF_START_VERSION {
367            let mut output: Vec<ScanMode> = Vec::with_capacity(2);
368
369            output.push(ScanMode {
370                id: 0u16,
371                us_per_sample: 1000000f32 / 2000f32,
372                max_distance: 8000f32,
373                ans_type: RPLIDAR_ANS_TYPE_MEASUREMENT,
374                name: "Standard".to_owned(),
375            });
376
377            if device_info.model >= 0x20u8 {
378                output.push(ScanMode {
379                    id: 1u16,
380                    us_per_sample: 1000000f32 / 4000f32,
381                    max_distance: 16000f32,
382                    ans_type: RPLIDAR_ANS_TYPE_MEASUREMENT_CAPSULED,
383                    name: "Express".to_owned(),
384                });
385            }
386
387            Ok(output)
388        } else {
389            let scan_mode_count = self.get_scan_mode_count_with_timeout(timeout)?;
390            let mut output: Vec<ScanMode> = Vec::with_capacity(scan_mode_count as usize);
391
392            for i in 0..scan_mode_count {
393                output.push(self.get_scan_mode_with_timeout(i, timeout)?);
394            }
395
396            Ok(output)
397        }
398    }
399
400    /// start scan
401    pub fn start_scan(&mut self) -> Result<ScanMode> {
402        self.start_scan_with_options(&ScanOptions::default())
403    }
404
405    /// start scan with timeout
406    pub fn start_scan_with_timeout(&mut self, timeout: Duration) -> Result<ScanMode> {
407        self.start_scan_with_options_and_timeout(&ScanOptions::default(), timeout)
408    }
409
410    /// start scan with options
411    pub fn start_scan_with_options(&mut self, options: &ScanOptions) -> Result<ScanMode> {
412        self.start_scan_with_options_and_timeout(options, RPLIDAR_DEFAULT_TIMEOUT)
413    }
414
415    /// start scan with options and non-default timeout
416    pub fn start_scan_with_options_and_timeout(
417        &mut self,
418        options: &ScanOptions,
419        timeout: Duration,
420    ) -> Result<ScanMode> {
421        self.cached_prev_capsule = CachedPrevCapsule::None;
422
423        let scan_mode = match options.scan_mode {
424            Some(mode) => mode,
425            None => self.get_typical_scan_mode_with_timeout(timeout)?,
426        };
427
428        let scan_mode_info = self.get_scan_mode_with_timeout(scan_mode, timeout)?;
429
430        match scan_mode {
431            0 => self.legacy_start_scan(options.force_scan)?,
432            _ => {
433                let payload = RplidarPayloadExpressScan {
434                    work_mode: scan_mode as u8,
435                    work_flags: options.options as u16,
436                    param: 0,
437                };
438                self.start_express_scan(&payload)?;
439            }
440        }
441
442        Ok(scan_mode_info)
443    }
444
445    /// use legacy command to start scan
446    fn legacy_start_scan(&mut self, force_scan: bool) -> Result<()> {
447        self.channel.write(&Message::new(if force_scan {
448            RPLIDAR_CMD_FORCE_SCAN
449        } else {
450            RPLIDAR_CMD_SCAN
451        }))?;
452        Ok(())
453    }
454
455    /// start express scan with options
456    fn start_express_scan(&mut self, options: &RplidarPayloadExpressScan) -> Result<()> {
457        let data = unsafe {
458            transmute_copy::<
459                RplidarPayloadExpressScan,
460                [u8; std::mem::size_of::<RplidarPayloadExpressScan>()],
461            >(options)
462        };
463        self.channel
464            .write(&Message::with_data(RPLIDAR_CMD_EXPRESS_SCAN, &data))?;
465        Ok(())
466    }
467
468    /// when hq measurement node received
469    fn on_measurement_node_hq(&mut self, node: RplidarResponseMeasurementNodeHq) {
470        self.cached_measurement_nodes
471            .push_back(ScanPoint::from(node));
472    }
473
474    /// when measurement node received
475    fn on_measurement_node(&mut self, node: RplidarResponseMeasurementNode) {
476        self.on_measurement_node_hq(RplidarResponseMeasurementNodeHq {
477            angle_z_q14: ((((node.angle_q6_checkbit as u32)
478                >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT as u32)
479                << 8)
480                / 90) as u16,
481            dist_mm_q2: node.distance_q2 as u32,
482            flag: node.sync_quality & RPLIDAR_RESP_MEASUREMENT_SYNCBIT,
483            quality: (node.sync_quality >> RPLIDAR_RESP_MEASUREMENT_QUALITY_SHIFT as u8)
484                << RPLIDAR_RESP_MEASUREMENT_QUALITY_SHIFT as u8,
485        });
486    }
487
488    /// when capsuled measurement msg received
489    fn on_measurement_capsuled_msg(&mut self, msg: &Message) -> Result<()> {
490        check_sync_and_checksum(msg)?;
491        self.on_measurement_capsuled(parse_resp!(msg, RplidarResponseCapsuleMeasurementNodes)?);
492        Ok(())
493    }
494
495    /// when capsuled measurement response received
496    fn on_measurement_capsuled(&mut self, nodes: RplidarResponseCapsuleMeasurementNodes) {
497        let (parsed_nodes, new_cached_capsuled) = parse_capsuled(&self.cached_prev_capsule, nodes);
498        self.cached_prev_capsule = new_cached_capsuled;
499
500        for node in parsed_nodes {
501            self.on_measurement_node_hq(node);
502        }
503    }
504
505    /// when ultra capsuled measurement msg received
506    fn on_measurement_ultra_capsuled_msg(&mut self, msg: &Message) -> Result<()> {
507        check_sync_and_checksum(msg)?;
508        self.on_measurement_ultra_capsuled(parse_resp!(
509            msg,
510            RplidarResponseUltraCapsuleMeasurementNodes
511        )?);
512        Ok(())
513    }
514
515    /// when ultra capsuled measurement response received
516    fn on_measurement_ultra_capsuled(
517        &mut self,
518        nodes: RplidarResponseUltraCapsuleMeasurementNodes,
519    ) {
520        let (parsed_nodes, new_cached_capsuled) =
521            parse_ultra_capsuled(&self.cached_prev_capsule, nodes);
522        self.cached_prev_capsule = new_cached_capsuled;
523
524        for node in parsed_nodes {
525            self.on_measurement_node_hq(node);
526        }
527    }
528
529    /// when hq capsuled measurement msg received
530    fn on_measurement_hq_capsuled_msg(&mut self, msg: &Message) -> Result<()> {
531        check_sync_and_checksum_hq(msg)?;
532        self.on_measurement_hq_capsuled(parse_resp!(
533            msg,
534            RplidarResponseHqCapsuledMeasurementNodes
535        )?);
536        Ok(())
537    }
538
539    /// when hq capsuled measurement response received
540    fn on_measurement_hq_capsuled(&mut self, nodes: RplidarResponseHqCapsuledMeasurementNodes) {
541        for node in nodes.nodes.iter() {
542            self.on_measurement_node_hq(*node);
543        }
544    }
545
546    /// wait for next section of scan data
547    fn wait_scan_data_with_timeout(&mut self, timeout: Duration) -> Result<()> {
548        let opt_msg = self.channel.read_until(timeout)?;
549
550        if let Some(msg) = opt_msg {
551            match msg.cmd {
552                RPLIDAR_ANS_TYPE_MEASUREMENT => {
553                    self.on_measurement_node(parse_resp!(msg, RplidarResponseMeasurementNode)?)
554                }
555                RPLIDAR_ANS_TYPE_MEASUREMENT_CAPSULED => self.on_measurement_capsuled_msg(&msg)?,
556                RPLIDAR_ANS_TYPE_MEASUREMENT_CAPSULED_ULTRA => {
557                    self.on_measurement_ultra_capsuled_msg(&msg)?
558                }
559                RPLIDAR_ANS_TYPE_MEASUREMENT_HQ => self.on_measurement_hq_capsuled_msg(&msg)?,
560                _ => {
561                    return Err(RposError::ProtocolError {
562                        description: "unexpected response".to_owned(),
563                    }
564                    .into());
565                }
566            }
567            Ok(())
568        } else {
569            Ok(())
570        }
571    }
572
573    /// read scan point
574    pub fn grab_scan_point(&mut self) -> Result<ScanPoint> {
575        self.grab_scan_point_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
576    }
577
578    /// read scan point with timeout
579    pub fn grab_scan_point_with_timeout(&mut self, timeout: Duration) -> Result<ScanPoint> {
580        if self.cached_measurement_nodes.is_empty() {
581            self.wait_scan_data_with_timeout(timeout)?;
582
583            if self.cached_measurement_nodes.is_empty() {
584                return Err(RposError::OperationTimeout.into());
585            }
586        }
587
588        Ok(self.cached_measurement_nodes.pop_front().unwrap())
589    }
590
591    /// read scan frame
592    pub fn grab_scan(&mut self) -> Result<Vec<ScanPoint>> {
593        self.grab_scan_with_timeout(RPLIDAR_DEFAULT_TIMEOUT * 5)
594    }
595
596    /// read scan frame
597    pub fn grab_scan_with_timeout(&mut self, timeout: Duration) -> Result<Vec<ScanPoint>> {
598        let deadline = Instant::now() + timeout;
599        let mut end = 1;
600
601        'outer_loop: loop {
602            if Instant::now() > deadline {
603                return Err(RposError::OperationTimeout.into());
604            }
605
606            if self.cached_measurement_nodes.len() <= end {
607                self.wait_scan_data_with_timeout(std::cmp::min(
608                    deadline - Instant::now(),
609                    RPLIDAR_DEFAULT_TIMEOUT,
610                ))?;
611            }
612
613            for i in end..self.cached_measurement_nodes.len() {
614                if self.cached_measurement_nodes[i].is_sync() {
615                    end = i;
616                    break 'outer_loop;
617                }
618            }
619
620            end = self.cached_measurement_nodes.len();
621        }
622
623        let mut out = Vec::<ScanPoint>::with_capacity(end);
624        for _ in 0..end {
625            if let Some(point) = self.cached_measurement_nodes.pop_front() {
626                out.push(point);
627            }
628        }
629
630        Ok(out)
631    }
632
633    /// Get LIDAR health information
634    pub fn get_device_health(&mut self) -> Result<Health> {
635        self.get_device_health_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
636    }
637
638    /// Get LIDAR health information
639    pub fn get_device_health_with_timeout(&mut self, timeout: Duration) -> Result<Health> {
640        if let Some(msg) = self
641            .channel
642            .invoke(&Message::new(RPLIDAR_CMD_GET_DEVICE_HEALTH), timeout)?
643        {
644            let resp = handle_resp!(RPLIDAR_ANS_TYPE_DEVHEALTH, msg, RplidarResponseDeviceHealth)?;
645
646            return Ok(match resp.status {
647                RPLIDAR_HEALTH_STATUS_OK => Health::Healthy,
648                RPLIDAR_HEALTH_STATUS_WARNING => Health::Warning(resp.error_code),
649                RPLIDAR_HEALTH_STATUS_ERROR => Health::Error(resp.error_code),
650                _ => Health::Healthy,
651            });
652        }
653
654        Err(RposError::OperationTimeout.into())
655    }
656
657    /// Check if the connected LIDAR supports motor control
658    pub fn check_motor_ctrl_support(&mut self) -> Result<bool> {
659        self.check_motor_ctrl_support_with_timeout(RPLIDAR_DEFAULT_TIMEOUT)
660    }
661
662    /// Check if the connected LIDAR supports motor control with timeout
663    pub fn check_motor_ctrl_support_with_timeout(&mut self, timeout: Duration) -> Result<bool> {
664        let mut data = [0u8; 4];
665        LittleEndian::write_u32(&mut data, 0u32);
666
667        let resp_msg = self.channel.invoke(
668            &Message::with_data(RPLIDAR_CMD_GET_ACC_BOARD_FLAG, &data),
669            timeout,
670        )?;
671
672        if let Some(msg) = resp_msg {
673            let support_flag = handle_resp!(RPLIDAR_ANS_TYPE_ACC_BOARD_FLAG, msg, u32)?;
674
675            Ok(
676                (support_flag & RPLIDAR_RESP_ACC_BOARD_FLAG_MOTOR_CTRL_SUPPORT_MASK)
677                    == RPLIDAR_RESP_ACC_BOARD_FLAG_MOTOR_CTRL_SUPPORT_MASK,
678            )
679        } else {
680            Err(RposError::OperationTimeout.into())
681        }
682    }
683}
684
685fn check_sync_and_checksum(msg: &Message) -> Result<()> {
686    if msg.data.len() < 2 {
687        return Err(RposError::ProtocolError {
688            description: "data too short".to_owned(),
689        }
690        .into());
691    }
692
693    if (msg.data[0] >> 4) != RPLIDAR_RESP_MEASUREMENT_EXP_SYNC_1 {
694        return Err(RposError::ProtocolError {
695            description: "miss sync 1".to_owned(),
696        }
697        .into());
698    }
699
700    if (msg.data[1] >> 4) != RPLIDAR_RESP_MEASUREMENT_EXP_SYNC_2 {
701        return Err(RposError::ProtocolError {
702            description: "miss sync 2".to_owned(),
703        }
704        .into());
705    }
706
707    let recv_checksum = (msg.data[0] & 0xf) | (msg.data[1] << 4);
708    let mut checksum = Checksum::new();
709    checksum.push_slice(&msg.data[2..]);
710
711    if checksum.checksum() != recv_checksum {
712        Err(RposError::ProtocolError {
713            description: "checksum mismatch".to_owned(),
714        }
715        .into())
716    } else {
717        Ok(())
718    }
719}
720
721fn check_sync_and_checksum_hq(msg: &Message) -> Result<()> {
722    if msg.data.len() != std::mem::size_of::<RplidarResponseHqCapsuledMeasurementNodes>() {
723        return Err(RposError::ProtocolError {
724            description: "data length mismatch".to_owned(),
725        }
726        .into());
727    }
728
729    if msg.data[0] != RPLIDAR_RESP_MEASUREMENT_HQ_SYNC {
730        return Err(RposError::ProtocolError {
731            description: "sync mismatch".to_owned(),
732        }
733        .into());
734    }
735
736    let checksum = crc32::checksum_ieee(&msg.data[0..msg.data.len() - 4]);
737    let recv_checksum = LittleEndian::read_u32(&msg.data[msg.data.len() - 4..msg.data.len()]);
738
739    if checksum != recv_checksum {
740        Err(RposError::ProtocolError {
741            description: "checksum mismatch".to_owned(),
742        }
743        .into())
744    } else {
745        Ok(())
746    }
747}