uvt 0.1.1

Utilities for interacting with Uncrewed Vehicle Trajectory (UVT) files.
Documentation
use std::io::{Error, ErrorKind};

use crate::deserialization::{BufferReader, MessageDataBuffer};
use crate::pointcloud::{PointCloud2Deserializer, PointField};
use crate::pose;
use crate::trajectory::TrajectoryDeserializer;

pub struct BagDeserializer {
    buf: MessageDataBuffer,
}

impl BagDeserializer {
    pub fn new(data: Vec<u8>) -> Self {
        Self {
            buf: MessageDataBuffer::new(data),
        }
    }
}

impl BufferReader for BagDeserializer {
    fn read_u32_le(&mut self) -> Result<u32, std::io::Error> {
        self.buf.read_u32_le()
    }

    fn read_f64_le(&mut self) -> Result<f64, std::io::Error> {
        self.buf.read_f64_le()
    }

    fn read_byte(&mut self) -> Result<u8, std::io::Error> {
        self.buf.read_byte()
    }

    fn slice(&mut self, length: usize) -> Option<&[u8]> {
        self.buf.slice(length)
    }

    fn read_lp_string(&mut self) -> Result<String, std::io::Error> {
        self.buf.read_lp_string()
    }

    fn read_null_terminated_string(&mut self) -> Result<String, std::io::Error> {
        self.buf.read_null_terminated_string()
    }

    fn read_header(&mut self) -> Result<pose::Header, std::io::Error> {
        Ok(pose::Header {
            seq: self.buf.read_u32_le()?.clone(),
            stamp: pose::Time {
                sec: self.buf.read_i32_le()?.clone(),
                nanosec: self.buf.read_u32_le()?.clone(),
            },
            frame_id: self.read_lp_string()?,
        })
    }
}

impl PointCloud2Deserializer for BagDeserializer {
    fn read_point_field(&mut self) -> Result<PointField, std::io::Error> {
        Ok(PointField {
            name: self.read_lp_string()?,
            offset: self.buf.read_u32_le()?,
            datatype: self.buf.read_byte()?.into(),
            count: self.buf.read_u32_le()?,
        })
    }

    fn read_point_fields(&mut self) -> Result<Vec<PointField>, std::io::Error> {
        let n_fields = self.buf.read_u32_le()?;
        let fields = (0..n_fields)
            .into_iter()
            .map(|_| self.read_point_field())
            .into_iter()
            .collect();
        fields
    }

    fn read_data(&mut self) -> Result<Vec<u8>, std::io::Error> {
        // TODO: Rely on fields
        // Point cloud data, size is (row_step*height)
        let data_len = self.buf.read_u32_le()?;
        let data: Vec<u8> = self
            .buf
            .slice(data_len as usize)
            .ok_or_else(|| {
                Error::new(
                    ErrorKind::UnexpectedEof,
                    "Not enough bytes to read the pointcloud data with the specified length",
                )
            })?
            .to_vec();
        Ok(data)
    }
}

impl TrajectoryDeserializer for BagDeserializer {
    fn read_position(&mut self) -> Result<pose::Point, std::io::Error> {
        Ok(pose::Point {
            x: self.buf.read_f64_le()?,
            y: self.buf.read_f64_le()?,
            z: self.buf.read_f64_le()?,
        })
    }
    fn read_orientation(&mut self) -> Result<pose::Quaternion, std::io::Error> {
        Ok(pose::Quaternion {
            x: self.buf.read_f64_le()?,
            y: self.buf.read_f64_le()?,
            z: self.buf.read_f64_le()?,
            w: self.buf.read_f64_le()?,
        })
    }

    /// Read covariance values
    /// 6 x 6 covariance matrix = 36 covariance values
    /// https://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/PoseWithCovarianceStamped.html
    fn read_covariance(&mut self) -> Result<Vec<f64>, std::io::Error> {
        (0..36)
            .into_iter()
            .map(|_| self.buf.read_f64_le())
            .into_iter()
            .collect()
    }
    fn read_vector(&mut self) -> Result<pose::Vector3, std::io::Error> {
        Ok(pose::Vector3::new(
            self.buf.read_f64_le()?,
            self.buf.read_f64_le()?,
            self.buf.read_f64_le()?,
        ))
    }
}