lidar_utils/ouster/
frame_converter.rs

1//! Provides a set of tools convert raw packets from Ouster sensors.
2
3use super::{
4    config::Config,
5    consts::COLUMNS_PER_PACKET,
6    packet::{Column, Packet},
7    pcd_converter::{Point, PointCloudConverter},
8};
9use crate::common::*;
10
11/// A frame is a collection of points gathered in one
12/// LIDAR rotation.
13#[derive(Debug, Clone)]
14pub struct Frame {
15    /// The ID marked by [FrameConverter](FrameConverter).
16    pub frame_id: u16,
17    /// The IDs of dropped frames before this frame comes in.
18    pub skipped_frame_ids: Range<u16>,
19    /// Pairs of `(measurement_id, timestamp)`.
20    pub timestamps: Vec<(u16, u64)>,
21    /// Point cloud data.
22    pub points: Vec<Point>,
23}
24
25/// It reads [columns](Column) of sensor data, and
26/// gathers points into sequence of frames.
27///
28/// It internally computes point cloud using
29/// [PointCloudConverter](PointCloudConverter).
30/// The columns must be pushed in the same order
31/// of LIDAR output. It keeps track of skipped
32/// columns and dropped frames.
33#[derive(Debug)]
34pub struct FrameConverter {
35    pcd_converter: PointCloudConverter,
36    state: Option<FrameConverterState>,
37}
38
39impl FrameConverter {
40    /// Creates converter from config.
41    pub fn from_config(config: Config) -> Self {
42        Self {
43            pcd_converter: PointCloudConverter::from_config(config),
44            state: None,
45        }
46    }
47
48    /// Returns the resolution in `(width, height)` pair.
49    pub fn resolution(&self) -> (u16, u16) {
50        let width = self.pcd_converter.columns_per_revolution();
51        (width, 64)
52    }
53
54    /// Returns the number of columns per revolution.
55    pub fn columns_per_revolution(&self) -> u16 {
56        self.pcd_converter.columns_per_revolution()
57    }
58
59    /// Pushes new [Column] to converter.
60    pub fn push_column(&mut self, column: &Column) -> Result<Vec<Frame>> {
61        let curr_fid = column.frame_id;
62        let curr_mid = column.measurement_id;
63        let curr_ts = column.timestamp;
64        let curr_points = self.pcd_converter.column_to_points(column)?;
65
66        // If received column is not valid, update last_{fid,mid} only
67        if !column.valid() {
68            let (frame_opt, new_state) = match self.state.take() {
69                Some(mut state) => {
70                    let frame_opt = match state.last_fid.cmp(&curr_fid) {
71                        Ordering::Less => state.frame.take(),
72                        Ordering::Equal => None,
73                        Ordering::Greater => {
74                            bail!(
75                                "Measurement ID of received column is less than that of previous column"
76                            );
77                        }
78                    };
79
80                    state.last_fid = curr_fid;
81                    state.last_mid = curr_mid;
82                    (frame_opt, state)
83                }
84                None => {
85                    let new_state = FrameConverterState {
86                        last_fid: curr_fid,
87                        last_mid: curr_mid,
88                        frame: None,
89                    };
90                    (None, new_state)
91                }
92            };
93
94            self.state = Some(new_state);
95            return Ok(frame_opt.into_iter().collect());
96        }
97
98        let (new_state, output_frames) = match self.state.take() {
99            Some(mut state) => {
100                match state.last_fid.cmp(&curr_fid) {
101                    Ordering::Less => {
102                        // Case: New frame ID
103                        // Pop out saved frame and conditionally save or output second frame
104
105                        let first_frame_opt = state.frame.take();
106                        let second_frame = Frame {
107                            frame_id: curr_fid,
108                            skipped_frame_ids: (state.last_fid + 1)..curr_fid,
109                            timestamps: {
110                                let mut timestamps = Vec::with_capacity(COLUMNS_PER_PACKET);
111                                timestamps.push((curr_mid, curr_ts));
112                                timestamps
113                            },
114                            points: curr_points,
115                        };
116                        let mut new_state = FrameConverterState {
117                            last_mid: curr_mid,
118                            last_fid: curr_fid,
119                            frame: None,
120                        };
121
122                        // Produce frame if measurement ID is exactly the latest ID of frame
123                        let (second_frame_opt, new_state) =
124                            if curr_mid + 1 == self.pcd_converter.columns_per_revolution() {
125                                (Some(second_frame), new_state)
126                            } else {
127                                new_state.frame = Some(second_frame);
128                                (None, new_state)
129                            };
130
131                        let output_frames = first_frame_opt
132                            .into_iter()
133                            .chain(second_frame_opt.into_iter())
134                            .collect();
135
136                        (new_state, output_frames)
137                    }
138                    Ordering::Equal => {
139                        if state.last_mid >= curr_mid {
140                            let error = format_err!(
141                                "Measurement ID of received column is less than that of previous column"
142                            );
143                            return Err(error);
144                        }
145
146                        // Conditionally produce frame if measurement ID is the latest one
147                        let mut new_state = FrameConverterState {
148                            last_mid: curr_mid,
149                            last_fid: curr_fid,
150                            frame: None,
151                        };
152                        let frame = {
153                            let mut frame = state.frame.take().unwrap_or_else(|| {
154                                unreachable!("Please report bug to upstream");
155                            });
156                            frame.timestamps.push((curr_mid, curr_ts));
157                            frame.points.extend(curr_points);
158                            frame
159                        };
160
161                        let (frame_opt, new_state) =
162                            if curr_mid + 1 == self.pcd_converter.columns_per_revolution() {
163                                (Some(frame), new_state)
164                            } else {
165                                new_state.frame = Some(frame);
166                                (None, new_state)
167                            };
168
169                        let output_frames = frame_opt.into_iter().collect();
170                        (new_state, output_frames)
171                    }
172                    Ordering::Greater => {
173                        let error = format_err!(
174                            "Frame ID of received column is less than that of previous column"
175                        );
176                        return Err(error);
177                    }
178                }
179            }
180            None => {
181                let frame = Frame {
182                    frame_id: curr_fid,
183                    skipped_frame_ids: curr_fid..curr_fid,
184                    timestamps: {
185                        let mut timestamps = Vec::with_capacity(COLUMNS_PER_PACKET);
186                        timestamps.push((curr_mid, curr_ts));
187                        timestamps
188                    },
189                    points: curr_points,
190                };
191                let mut new_state = FrameConverterState {
192                    last_mid: curr_mid,
193                    last_fid: curr_fid,
194                    frame: None,
195                };
196
197                let frame_opt = if curr_mid + 1 == self.pcd_converter.columns_per_revolution() {
198                    Some(frame)
199                } else {
200                    new_state.frame = Some(frame);
201                    None
202                };
203
204                (new_state, frame_opt.into_iter().collect())
205            }
206        };
207
208        self.state = Some(new_state);
209        Ok(output_frames)
210    }
211
212    /// Pushes new [Packet] to converter.
213    pub fn push_packet<P>(&mut self, packet: P) -> Result<Vec<Frame>>
214    where
215        P: AsRef<Packet>,
216    {
217        let mut frames = vec![];
218        for column in packet.as_ref().columns.iter() {
219            frames.extend(self.push_column(column)?);
220        }
221        Ok(frames)
222    }
223
224    /// Consumes the instance and outputs last maybe
225    /// incomplete frame.
226    pub fn finish(mut self) -> Option<Frame> {
227        self.state
228            .take()
229            .map(|mut state| state.frame.take())
230            .unwrap_or(None)
231    }
232}
233
234#[derive(Clone, Debug)]
235struct FrameConverterState {
236    last_mid: u16,
237    last_fid: u16,
238    frame: Option<Frame>,
239}