use crate::constants::{
NUMBER_OF_POINTS_PER_MEASUREMENT, NUMBER_OF_POINTS_PER_SCAN, PAYLOAD_SIZE_IN_BYTES,
};
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error<E> {
UART(E),
ChecksumMismatch(u32, u32),
Other,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RawDistance {
value: u16,
quality: u8,
flag: bool,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RawMeasurement {
pub speed: u16,
pub start_angle: u16,
pub end_angle: u16,
pub distances: [RawDistance; NUMBER_OF_POINTS_PER_MEASUREMENT],
pub checksum: u16,
}
#[inline]
pub fn check_lidar_checksum(data: &[u8; PAYLOAD_SIZE_IN_BYTES]) -> Result<(), Error<()>> {
let mut accumulator: u32 = 0;
let num_data_words = data.len() / 2 - 1;
for i in 0..num_data_words {
let word = u16::from_le_bytes([data[2 * i], data[2 * i + 1]]);
accumulator = (accumulator << 1) + word as u32;
}
let computed_checksum = ((accumulator & 0x7FFF) + (accumulator >> 15)) & 0x7FFF;
let expected_checksum = u16::from_le_bytes([data[data.len() - 2], data[data.len() - 1]]) as u32;
if computed_checksum == expected_checksum {
Ok(())
} else {
Err(Error::ChecksumMismatch(
expected_checksum,
computed_checksum,
))
}
}
impl TryFrom<[u8; 36]> for RawMeasurement {
type Error = Error<()>; fn try_from(data: [u8; PAYLOAD_SIZE_IN_BYTES]) -> Result<Self, Self::Error> {
check_lidar_checksum(&data)?;
let speed = u16::from_le_bytes([data[4], data[5]]);
let start_angle = u16::from_le_bytes([data[6], data[7]]);
let end_angle = u16::from_le_bytes([data[32], data[33]]);
let checksum = u16::from_le_bytes([data[34], data[35]]);
let mut distances = [RawDistance {
value: 0,
quality: 0,
flag: false,
}; 8];
for i in 0..8 {
let distance_bytes = [data[8 + i * 3], data[9 + i * 3], data[10 + i * 3]];
let value_bytes = [distance_bytes[0], distance_bytes[1] & 0x3F];
let value = u16::from_le_bytes(value_bytes);
let quality = distance_bytes[2];
let flag = (distance_bytes[1] >> 7) & 0x01 != 0;
let distance = RawDistance {
value,
quality,
flag,
};
distances[i] = distance;
}
Ok(Self {
speed,
start_angle,
end_angle,
distances,
checksum,
})
}
}
#[derive(Clone, Copy, Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Point {
pub distance: u16,
pub angle: f32,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PartialScan {
pub frequency: f32,
pub start_angle: f32,
pub end_angle: f32,
pub points: [Option<Point>; NUMBER_OF_POINTS_PER_MEASUREMENT],
}
impl From<(RawMeasurement, f32)> for PartialScan {
fn from((raw, angle_offset): (RawMeasurement, f32)) -> Self {
let frequency = raw.speed as f32 / 3840.0;
let start_angle = raw.start_angle as f32 / 64.0 - 640.0;
let end_angle = raw.end_angle as f32 / 64.0 - 640.0;
let step = if end_angle > start_angle {
(end_angle - start_angle) / 7.0
} else {
(end_angle - (start_angle - 360.0)) / 7.0
};
let mut points = [None; NUMBER_OF_POINTS_PER_MEASUREMENT];
for (i, raw_distance) in raw.distances.iter().enumerate() {
if raw_distance.flag || raw_distance.quality == 0 || raw_distance.value == 0 {
continue;
}
let angle = (start_angle + step * i as f32 + angle_offset) % 360.0;
points[i] = Some(Point {
distance: raw_distance.value,
angle,
});
}
Self {
frequency,
start_angle,
end_angle,
points,
}
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Scan {
pub points: [Option<Point>; NUMBER_OF_POINTS_PER_SCAN],
}