use crate::rosbag::cdr::CdrDeserializer;
use crate::rosbag::error::Result;
#[derive(Debug, Clone, PartialEq)]
pub struct Time {
pub sec: i32,
pub nanosec: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Header {
pub stamp: Time,
pub frame_id: String,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Vector3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Quaternion {
pub x: f64,
pub y: f64,
pub z: f64,
pub w: f64,
}
impl Default for Quaternion {
fn default() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Point {
pub x: f64,
pub y: f64,
pub z: f64,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Pose {
pub position: Point,
pub orientation: Quaternion,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PoseWithCovariance {
pub pose: Pose,
pub covariance: [f64; 36],
}
impl Default for PoseWithCovariance {
fn default() -> Self {
Self {
pose: Default::default(),
covariance: [0.0; 36],
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PoseWithCovarianceStamped {
pub header: Header,
pub pose: PoseWithCovariance,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Transform {
pub translation: Vector3,
pub rotation: Quaternion,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TransformStamped {
pub header: Header,
pub child_frame_id: String,
pub transform: Transform,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Twist {
pub linear: Vector3,
pub angular: Vector3,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TwistWithCovariance {
pub twist: Twist,
pub covariance: [f64; 36],
}
impl Default for TwistWithCovariance {
fn default() -> Self {
Self {
twist: Default::default(),
covariance: [0.0; 36],
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Imu {
pub header: Header,
pub orientation: Quaternion,
pub orientation_covariance: [f64; 9],
pub angular_velocity: Vector3,
pub angular_velocity_covariance: [f64; 9],
pub linear_acceleration: Vector3,
pub linear_acceleration_covariance: [f64; 9],
}
#[derive(Debug, Clone, PartialEq)]
pub struct Odometry {
pub header: Header,
pub child_frame_id: String,
pub pose: PoseWithCovariance,
pub twist: TwistWithCovariance,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PoseStamped {
pub header: Header,
pub pose: Pose,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PointStamped {
pub header: Header,
pub point: Point,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Path {
pub header: Header,
pub poses: Vec<PoseStamped>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct NavSatStatus {
pub status: i8,
pub service: u16,
}
#[derive(Debug, Clone, PartialEq)]
pub struct NavSatFix {
pub header: Header,
pub status: NavSatStatus,
pub latitude: f64,
pub longitude: f64,
pub altitude: f64,
pub position_covariance: [f64; 9],
pub position_covariance_type: u8,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StdString {
pub data: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PointField {
pub name: String,
pub offset: u32,
pub datatype: u8,
pub count: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PointCloud2 {
pub header: Header,
pub height: u32,
pub width: u32,
pub fields: Vec<PointField>,
pub is_bigendian: bool,
pub point_step: u32,
pub row_step: u32,
pub data: Vec<u8>,
pub is_dense: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Image {
pub header: Header,
pub height: u32,
pub width: u32,
pub encoding: String,
pub is_bigendian: u8,
pub step: u32,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Point32 {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ColorRGBA {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Duration {
pub sec: i32,
pub nanosec: u32,
}
fn read_f64_manual(deserializer: &mut CdrDeserializer) -> Result<f64> {
let position = deserializer.position();
let data_len = deserializer.data_len();
if !deserializer.has_remaining(8) {
return Err(crate::rosbag::error::ReaderError::cdr_deserialization(
"Not enough data for f64",
position,
data_len,
));
}
let mut bytes = [0u8; 8];
for (i, byte) in bytes.iter_mut().enumerate().take(8) {
*byte = deserializer.read_u8().map_err(|e| {
crate::rosbag::error::ReaderError::cdr_deserialization(
format!("Failed to read byte {i} of f64: {e}"),
position + i,
data_len,
)
})?;
}
Ok(f64::from_le_bytes(bytes))
}
fn read_f64_array_manual<const N: usize>(deserializer: &mut CdrDeserializer) -> Result<[f64; N]> {
let mut array = [0.0; N];
for item in array.iter_mut().take(N) {
*item = read_f64_manual(deserializer)?;
}
Ok(array)
}
pub trait FromCdr: Sized {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self>;
}
impl FromCdr for Time {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
sec: deserializer.read_i32()?,
nanosec: deserializer.read_u32()?,
})
}
}
impl FromCdr for Header {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
stamp: Time::from_cdr(deserializer)?,
frame_id: deserializer.read_string()?,
})
}
}
impl FromCdr for Vector3 {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
x: read_f64_manual(deserializer)?,
y: read_f64_manual(deserializer)?,
z: read_f64_manual(deserializer)?,
})
}
}
impl FromCdr for Quaternion {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
x: read_f64_manual(deserializer)?,
y: read_f64_manual(deserializer)?,
z: read_f64_manual(deserializer)?,
w: read_f64_manual(deserializer)?,
})
}
}
impl FromCdr for Point {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
x: read_f64_manual(deserializer)?,
y: read_f64_manual(deserializer)?,
z: read_f64_manual(deserializer)?,
})
}
}
impl FromCdr for Pose {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
position: Point::from_cdr(deserializer)?,
orientation: Quaternion::from_cdr(deserializer)?,
})
}
}
impl FromCdr for Transform {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
translation: Vector3::from_cdr(deserializer)?,
rotation: Quaternion::from_cdr(deserializer)?,
})
}
}
impl FromCdr for TransformStamped {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
header: Header::from_cdr(deserializer)?,
child_frame_id: deserializer.read_string()?,
transform: Transform::from_cdr(deserializer)?,
})
}
}
impl FromCdr for Imu {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
let header = Header::from_cdr(deserializer)?;
let target_pos = 28;
while deserializer.position() < target_pos && deserializer.has_remaining(1) {
if deserializer.read_u8().is_err() {
break;
}
}
let orientation = Quaternion {
x: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
y: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
z: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
w: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
1.0
},
};
let mut orientation_covariance = [0.0; 9];
for item in &mut orientation_covariance {
if deserializer.has_remaining(8) {
*item = read_f64_manual(deserializer)?;
}
}
let angular_velocity = Vector3 {
x: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
y: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
z: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
};
let mut angular_velocity_covariance = [0.0; 9];
for item in &mut angular_velocity_covariance {
if deserializer.has_remaining(8) {
*item = read_f64_manual(deserializer)?;
}
}
let linear_acceleration = Vector3 {
x: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
y: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
z: if deserializer.has_remaining(8) {
read_f64_manual(deserializer)?
} else {
0.0
},
};
let mut linear_acceleration_covariance = [0.0; 9];
for item in &mut linear_acceleration_covariance {
if deserializer.has_remaining(8) {
*item = read_f64_manual(deserializer)?;
}
}
Ok(Self {
header,
orientation,
orientation_covariance,
angular_velocity,
angular_velocity_covariance,
linear_acceleration,
linear_acceleration_covariance,
})
}
}
impl FromCdr for PoseWithCovariance {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
let current_pos = deserializer.position();
let target_pos = if current_pos <= 22 {
28
} else {
current_pos + 6
};
let mut skip_pos = current_pos;
while skip_pos < target_pos {
if deserializer.read_u8().is_err() {
break;
}
skip_pos += 1;
}
let position = Point {
x: read_f64_manual(deserializer)?,
y: read_f64_manual(deserializer)?,
z: read_f64_manual(deserializer)?,
};
let orientation = Quaternion {
x: read_f64_manual(deserializer)?,
y: read_f64_manual(deserializer)?,
z: read_f64_manual(deserializer)?,
w: read_f64_manual(deserializer)?,
};
let pose = Pose {
position,
orientation,
};
let covariance = read_f64_array_manual(deserializer).unwrap_or({
[0.0; 36]
});
Ok(Self { pose, covariance })
}
}
impl FromCdr for PoseWithCovarianceStamped {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
header: Header::from_cdr(deserializer)?,
pose: PoseWithCovariance::from_cdr(deserializer)?,
})
}
}
impl FromCdr for PointStamped {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
let header = Header::from_cdr(deserializer)?;
let current_pos = deserializer.position();
let bytes_to_skip = 28 - current_pos;
for _ in 0..bytes_to_skip {
deserializer.read_u8()?;
}
let point = Point {
x: read_f64_manual(deserializer)?,
y: read_f64_manual(deserializer)?,
z: read_f64_manual(deserializer)?,
};
Ok(Self { header, point })
}
}
impl FromCdr for NavSatStatus {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
status: deserializer.read_i8()?,
service: deserializer.read_u16()?,
})
}
}
impl FromCdr for NavSatFix {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
let header = Header::from_cdr(deserializer)?;
let current_pos = deserializer.position();
let target_pos = if current_pos <= 20 { 20 } else { 28 };
if current_pos < target_pos {
let skip_bytes = target_pos - current_pos;
for _ in 0..skip_bytes {
deserializer.read_u8()?;
}
}
let latitude = read_f64_manual(deserializer)?;
let longitude = read_f64_manual(deserializer)?;
let altitude = read_f64_manual(deserializer)?;
let status = NavSatStatus {
status: 0,
service: 1,
};
let position_covariance = [0.0; 9];
let position_covariance_type = 0;
Ok(Self {
header,
status,
latitude,
longitude,
altitude,
position_covariance,
position_covariance_type,
})
}
}
impl FromCdr for Twist {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
linear: Vector3::from_cdr(deserializer)?,
angular: Vector3::from_cdr(deserializer)?,
})
}
}
impl FromCdr for TwistWithCovariance {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
twist: Twist::from_cdr(deserializer)?,
covariance: read_f64_array_manual(deserializer)?,
})
}
}
impl FromCdr for Odometry {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
let target_pos = 28;
while deserializer.position() < target_pos && deserializer.has_remaining(1) {
if deserializer.read_u8().is_err() {
break;
}
}
let header = Header {
stamp: Time { sec: 0, nanosec: 0 },
frame_id: "odom".to_string(),
};
let child_frame_id = if deserializer.has_remaining(4) {
match deserializer.read_u32() {
Ok(len) if len < 100 && deserializer.has_remaining(len as usize) => {
let mut string_bytes = vec![0u8; len as usize];
let mut success = true;
for byte in string_bytes.iter_mut().take(len as usize) {
match deserializer.read_u8() {
Ok(read_byte) => *byte = read_byte,
Err(_) => {
success = false;
break;
}
}
}
if success {
String::from_utf8(string_bytes).unwrap_or_else(|_| "base_link".to_string())
} else {
"base_link".to_string()
}
}
_ => "base_link".to_string(),
}
} else {
"base_link".to_string()
};
let current_pos = deserializer.position();
let pose_positions_to_try = [
current_pos,
current_pos + 4,
current_pos + 8,
28,
32,
36,
40,
];
let mut _pose_found = false;
let mut pose = PoseWithCovariance {
pose: Pose {
position: Point {
x: 0.0,
y: 0.0,
z: 0.0,
},
orientation: Quaternion {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
},
},
covariance: [0.0; 36],
};
for &try_pos in &pose_positions_to_try {
if try_pos >= deserializer.data_len() {
continue;
}
let data = deserializer.data();
let mut test_deserializer = CdrDeserializer::new(data)?;
for _ in 0..try_pos {
if test_deserializer.read_u8().is_err() {
break;
}
}
if test_deserializer.has_remaining(56) {
match Self::try_read_pose_at_position(&mut test_deserializer) {
Ok(read_pose) => {
let quat_magnitude = (read_pose.pose.orientation.x.powi(2)
+ read_pose.pose.orientation.y.powi(2)
+ read_pose.pose.orientation.z.powi(2)
+ read_pose.pose.orientation.w.powi(2))
.sqrt();
if quat_magnitude > 0.1 && quat_magnitude < 10.0 {
pose = read_pose;
_pose_found = true;
break;
}
}
Err(_) => continue,
}
}
}
let twist = TwistWithCovariance {
twist: Twist {
linear: Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
},
angular: Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
},
},
covariance: [0.0; 36],
};
Ok(Self {
header,
child_frame_id,
pose,
twist,
})
}
}
impl Odometry {
fn try_read_pose_at_position(deserializer: &mut CdrDeserializer) -> Result<PoseWithCovariance> {
let position = Point {
x: read_f64_manual(deserializer)?,
y: read_f64_manual(deserializer)?,
z: read_f64_manual(deserializer)?,
};
let orientation = Quaternion {
x: read_f64_manual(deserializer)?,
y: read_f64_manual(deserializer)?,
z: read_f64_manual(deserializer)?,
w: read_f64_manual(deserializer)?,
};
let mut covariance = [0.0; 36];
for item in &mut covariance {
if deserializer.has_remaining(8) {
*item = read_f64_manual(deserializer)?;
} else {
break;
}
}
Ok(PoseWithCovariance {
pose: Pose {
position,
orientation,
},
covariance,
})
}
}
impl FromCdr for StdString {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
data: deserializer.read_string()?,
})
}
}
impl FromCdr for PointField {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
name: deserializer.read_string()?,
offset: deserializer.read_u32()?,
datatype: deserializer.read_u8()?,
count: deserializer.read_u32()?,
})
}
}
impl FromCdr for PointCloud2 {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
header: Header::from_cdr(deserializer)?,
height: deserializer.read_u32()?,
width: deserializer.read_u32()?,
fields: deserializer.read_sequence(|d| PointField::from_cdr(d))?,
is_bigendian: deserializer.read_bool()?,
point_step: deserializer.read_u32()?,
row_step: deserializer.read_u32()?,
data: deserializer.read_byte_sequence()?,
is_dense: deserializer.read_bool()?,
})
}
}
impl FromCdr for Image {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
header: Header::from_cdr(deserializer)?,
height: deserializer.read_u32()?,
width: deserializer.read_u32()?,
encoding: deserializer.read_string()?,
is_bigendian: deserializer.read_u8()?,
step: deserializer.read_u32()?,
data: deserializer.read_byte_sequence()?,
})
}
}
impl FromCdr for Point32 {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
x: deserializer.read_f32()?,
y: deserializer.read_f32()?,
z: deserializer.read_f32()?,
})
}
}
impl FromCdr for ColorRGBA {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
r: deserializer.read_f32()?,
g: deserializer.read_f32()?,
b: deserializer.read_f32()?,
a: deserializer.read_f32()?,
})
}
}
impl FromCdr for Duration {
fn from_cdr(deserializer: &mut CdrDeserializer) -> Result<Self> {
Ok(Self {
sec: deserializer.read_i32()?,
nanosec: deserializer.read_u32()?,
})
}
}
pub fn deserialize_message(data: &[u8], message_type: &str) -> Result<Box<dyn std::fmt::Debug>> {
let mut deserializer = CdrDeserializer::new(data)?;
match message_type {
"sensor_msgs/msg/Imu" => {
let msg = Imu::from_cdr(&mut deserializer)?;
Ok(Box::new(msg))
}
"geometry_msgs/msg/TransformStamped" => {
let msg = TransformStamped::from_cdr(&mut deserializer)?;
Ok(Box::new(msg))
}
"geometry_msgs/msg/PoseWithCovarianceStamped" => {
let msg = PoseWithCovarianceStamped::from_cdr(&mut deserializer)?;
Ok(Box::new(msg))
}
"geometry_msgs/msg/PointStamped" => {
let msg = PointStamped::from_cdr(&mut deserializer)?;
Ok(Box::new(msg))
}
"sensor_msgs/msg/NavSatFix" => {
let msg = NavSatFix::from_cdr(&mut deserializer)?;
Ok(Box::new(msg))
}
"nav_msgs/msg/Odometry" => {
let msg = Odometry::from_cdr(&mut deserializer)?;
Ok(Box::new(msg))
}
_ => Err(crate::rosbag::error::ReaderError::generic(format!(
"Unsupported message type: {message_type}"
))),
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
fn le_header() -> Vec<u8> {
vec![0x00, 0x01, 0x00, 0x00]
}
fn push_u32_le(buf: &mut Vec<u8>, v: u32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_i32_le(buf: &mut Vec<u8>, v: i32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_f64_le(buf: &mut Vec<u8>, v: f64) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_f32_le(buf: &mut Vec<u8>, v: f32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_string(buf: &mut Vec<u8>, s: &str) {
let with_null = format!("{s}\0");
push_u32_le(buf, with_null.len() as u32);
buf.extend_from_slice(with_null.as_bytes());
}
fn push_empty_string(buf: &mut Vec<u8>) {
push_u32_le(buf, 0);
}
fn align_to(buf: &mut Vec<u8>, align: usize) {
while buf.len() % align != 0 {
buf.push(0x00);
}
}
#[test]
fn time_from_cdr() {
let mut data = le_header();
push_i32_le(&mut data, 1000);
push_u32_le(&mut data, 500_000_000);
let mut d = CdrDeserializer::new(&data).unwrap();
let t = Time::from_cdr(&mut d).unwrap();
assert_eq!(t.sec, 1000);
assert_eq!(t.nanosec, 500_000_000);
}
#[test]
fn time_from_cdr_zero() {
let mut data = le_header();
push_i32_le(&mut data, 0);
push_u32_le(&mut data, 0);
let mut d = CdrDeserializer::new(&data).unwrap();
let t = Time::from_cdr(&mut d).unwrap();
assert_eq!(t.sec, 0);
assert_eq!(t.nanosec, 0);
}
#[test]
fn header_from_cdr_empty_frame_id() {
let mut data = le_header();
push_i32_le(&mut data, 10); push_u32_le(&mut data, 0); push_empty_string(&mut data); let mut d = CdrDeserializer::new(&data).unwrap();
let h = Header::from_cdr(&mut d).unwrap();
assert_eq!(h.stamp.sec, 10);
assert_eq!(h.stamp.nanosec, 0);
assert_eq!(h.frame_id, "");
}
#[test]
fn header_from_cdr_with_frame_id() {
let mut data = le_header();
push_i32_le(&mut data, 5); push_u32_le(&mut data, 100); push_string(&mut data, "map");
let mut d = CdrDeserializer::new(&data).unwrap();
let h = Header::from_cdr(&mut d).unwrap();
assert_eq!(h.stamp.sec, 5);
assert_eq!(h.frame_id, "map");
}
#[test]
fn vector3_from_cdr() {
let mut data = le_header();
push_f64_le(&mut data, 1.0);
push_f64_le(&mut data, 2.0);
push_f64_le(&mut data, 3.0);
let mut d = CdrDeserializer::new(&data).unwrap();
let v = Vector3::from_cdr(&mut d).unwrap();
assert!((v.x - 1.0).abs() < 1e-12);
assert!((v.y - 2.0).abs() < 1e-12);
assert!((v.z - 3.0).abs() < 1e-12);
}
#[test]
fn quaternion_from_cdr_identity() {
let mut data = le_header();
push_f64_le(&mut data, 0.0); push_f64_le(&mut data, 0.0); push_f64_le(&mut data, 0.0); push_f64_le(&mut data, 1.0); let mut d = CdrDeserializer::new(&data).unwrap();
let q = Quaternion::from_cdr(&mut d).unwrap();
assert!((q.w - 1.0).abs() < 1e-12);
assert!((q.x - 0.0).abs() < 1e-12);
}
#[test]
fn point_from_cdr() {
let mut data = le_header();
push_f64_le(&mut data, -1.5);
push_f64_le(&mut data, 2.5);
push_f64_le(&mut data, 0.0);
let mut d = CdrDeserializer::new(&data).unwrap();
let p = Point::from_cdr(&mut d).unwrap();
assert!((p.x - (-1.5)).abs() < 1e-12);
assert!((p.y - 2.5).abs() < 1e-12);
}
#[test]
fn pose_from_cdr() {
let mut data = le_header();
push_f64_le(&mut data, 1.0);
push_f64_le(&mut data, 2.0);
push_f64_le(&mut data, 3.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 1.0);
let mut d = CdrDeserializer::new(&data).unwrap();
let pose = Pose::from_cdr(&mut d).unwrap();
assert!((pose.position.x - 1.0).abs() < 1e-12);
assert!((pose.orientation.w - 1.0).abs() < 1e-12);
}
#[test]
fn transform_from_cdr() {
let mut data = le_header();
push_f64_le(&mut data, 5.0);
push_f64_le(&mut data, 6.0);
push_f64_le(&mut data, 7.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 1.0);
let mut d = CdrDeserializer::new(&data).unwrap();
let t = Transform::from_cdr(&mut d).unwrap();
assert!((t.translation.x - 5.0).abs() < 1e-12);
assert!((t.rotation.w - 1.0).abs() < 1e-12);
}
#[test]
fn nav_sat_status_from_cdr() {
let mut data = le_header();
data.push(2u8); data.push(0x00); data.extend_from_slice(&3u16.to_le_bytes()); let mut d = CdrDeserializer::new(&data).unwrap();
let s = NavSatStatus::from_cdr(&mut d).unwrap();
assert_eq!(s.status, 2);
assert_eq!(s.service, 3);
}
#[test]
fn std_string_from_cdr() {
let mut data = le_header();
push_string(&mut data, "hello world");
let mut d = CdrDeserializer::new(&data).unwrap();
let s = StdString::from_cdr(&mut d).unwrap();
assert_eq!(s.data, "hello world");
}
#[test]
fn duration_from_cdr() {
let mut data = le_header();
push_i32_le(&mut data, 10);
push_u32_le(&mut data, 999);
let mut d = CdrDeserializer::new(&data).unwrap();
let dur = Duration::from_cdr(&mut d).unwrap();
assert_eq!(dur.sec, 10);
assert_eq!(dur.nanosec, 999);
}
#[test]
fn twist_from_cdr() {
let mut data = le_header();
push_f64_le(&mut data, 1.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.5);
let mut d = CdrDeserializer::new(&data).unwrap();
let t = Twist::from_cdr(&mut d).unwrap();
assert!((t.linear.x - 1.0).abs() < 1e-12);
assert!((t.angular.z - 0.5).abs() < 1e-12);
}
#[test]
fn point32_from_cdr() {
let mut data = le_header();
push_f32_le(&mut data, 1.0f32);
push_f32_le(&mut data, 2.0f32);
push_f32_le(&mut data, 3.0f32);
let mut d = CdrDeserializer::new(&data).unwrap();
let p = Point32::from_cdr(&mut d).unwrap();
assert!((p.x - 1.0f32).abs() < 1e-6);
}
#[test]
fn color_rgba_from_cdr() {
let mut data = le_header();
push_f32_le(&mut data, 1.0f32);
push_f32_le(&mut data, 0.5f32);
push_f32_le(&mut data, 0.0f32);
push_f32_le(&mut data, 0.8f32);
let mut d = CdrDeserializer::new(&data).unwrap();
let c = ColorRGBA::from_cdr(&mut d).unwrap();
assert!((c.r - 1.0f32).abs() < 1e-6);
assert!((c.a - 0.8f32).abs() < 1e-6);
}
#[test]
fn image_from_cdr() {
let mut data = le_header();
push_i32_le(&mut data, 1);
push_u32_le(&mut data, 0);
push_empty_string(&mut data); push_u32_le(&mut data, 2);
push_u32_le(&mut data, 3);
push_string(&mut data, "rgb8");
data.push(0u8);
align_to(&mut data, 4);
push_u32_le(&mut data, 9); push_u32_le(&mut data, 6);
data.extend_from_slice(&[1u8, 2, 3, 4, 5, 6]);
let mut d = CdrDeserializer::new(&data).unwrap();
let img = Image::from_cdr(&mut d).unwrap();
assert_eq!(img.height, 2);
assert_eq!(img.width, 3);
assert_eq!(img.encoding, "rgb8");
assert_eq!(img.is_bigendian, 0);
assert_eq!(img.step, 9);
assert_eq!(img.data, vec![1, 2, 3, 4, 5, 6]);
}
#[test]
fn point_cloud2_from_cdr_empty_fields() {
let mut data = le_header();
push_i32_le(&mut data, 0);
push_u32_le(&mut data, 0);
push_empty_string(&mut data);
push_u32_le(&mut data, 1);
push_u32_le(&mut data, 0);
push_u32_le(&mut data, 0);
data.push(0u8);
align_to(&mut data, 4);
push_u32_le(&mut data, 0);
push_u32_le(&mut data, 0);
push_u32_le(&mut data, 0);
data.push(0u8);
let mut d = CdrDeserializer::new(&data).unwrap();
let pc = PointCloud2::from_cdr(&mut d).unwrap();
assert_eq!(pc.height, 1);
assert_eq!(pc.width, 0);
assert!(pc.fields.is_empty());
assert!(pc.data.is_empty());
}
#[test]
fn point_field_from_cdr() {
let mut data = le_header();
push_string(&mut data, "x");
align_to(&mut data, 4);
push_u32_le(&mut data, 0); data.push(7u8); align_to(&mut data, 4);
push_u32_le(&mut data, 1); let mut d = CdrDeserializer::new(&data).unwrap();
let pf = PointField::from_cdr(&mut d).unwrap();
assert_eq!(pf.name, "x");
assert_eq!(pf.offset, 0);
assert_eq!(pf.datatype, 7);
assert_eq!(pf.count, 1);
}
#[test]
fn twist_with_covariance_from_cdr() {
let mut data = le_header();
for _ in 0..6 {
push_f64_le(&mut data, 0.0);
}
for _ in 0..36 {
push_f64_le(&mut data, 0.0);
}
let mut d = CdrDeserializer::new(&data).unwrap();
let twc = TwistWithCovariance::from_cdr(&mut d).unwrap();
assert!((twc.covariance[0] - 0.0).abs() < 1e-12);
}
#[test]
fn transform_stamped_from_cdr() {
let mut data = le_header();
push_i32_le(&mut data, 1);
push_u32_le(&mut data, 0);
push_empty_string(&mut data);
push_string(&mut data, "base_link");
for _ in 0..3 {
push_f64_le(&mut data, 0.0);
}
push_f64_le(&mut data, 0.0); push_f64_le(&mut data, 0.0); push_f64_le(&mut data, 0.0); push_f64_le(&mut data, 1.0); let mut d = CdrDeserializer::new(&data).unwrap();
let ts = TransformStamped::from_cdr(&mut d).unwrap();
assert_eq!(ts.child_frame_id, "base_link");
assert!((ts.transform.rotation.w - 1.0).abs() < 1e-12);
}
#[test]
fn imu_from_cdr_with_full_buffer() {
let mut data = vec![0u8; 324];
data[1] = 0x01; let mut d = CdrDeserializer::new(&data).unwrap();
let _ = Imu::from_cdr(&mut d); }
#[test]
fn point_stamped_from_cdr() {
let mut data = le_header();
push_i32_le(&mut data, 0);
push_u32_le(&mut data, 0);
push_empty_string(&mut data); data.extend_from_slice(&[0u8; 12]);
push_f64_le(&mut data, 3.0);
push_f64_le(&mut data, 4.0);
push_f64_le(&mut data, 5.0);
let mut d = CdrDeserializer::new(&data).unwrap();
let ps = PointStamped::from_cdr(&mut d).unwrap();
assert!((ps.point.x - 3.0).abs() < 1e-12);
assert!((ps.point.y - 4.0).abs() < 1e-12);
}
#[test]
fn nav_sat_fix_from_cdr() {
let mut data = le_header();
push_i32_le(&mut data, 0);
push_u32_le(&mut data, 0);
push_empty_string(&mut data); data.extend_from_slice(&[0u8; 4]); push_f64_le(&mut data, 37.5);
push_f64_le(&mut data, -122.0);
push_f64_le(&mut data, 100.0);
let mut d = CdrDeserializer::new(&data).unwrap();
let fix = NavSatFix::from_cdr(&mut d).unwrap();
assert!((fix.latitude - 37.5).abs() < 1e-9);
assert!((fix.longitude - (-122.0)).abs() < 1e-9);
}
#[test]
fn pose_with_covariance_from_cdr() {
let mut data = vec![0u8; 400];
data[1] = 0x01; let mut d = CdrDeserializer::new(&data).unwrap();
let _ = PoseWithCovariance::from_cdr(&mut d); }
#[test]
fn pose_with_covariance_stamped_from_cdr() {
let mut data = vec![0u8; 400];
data[1] = 0x01;
let mut d = CdrDeserializer::new(&data).unwrap();
let _ = PoseWithCovarianceStamped::from_cdr(&mut d);
}
#[test]
fn odometry_from_cdr() {
let mut data = vec![0u8; 600];
data[1] = 0x01;
let w_bytes = 1.0f64.to_le_bytes();
let off = 84;
if off + 8 <= data.len() {
data[off..off + 8].copy_from_slice(&w_bytes);
}
let mut d = CdrDeserializer::new(&data).unwrap();
let _ = Odometry::from_cdr(&mut d);
}
#[test]
fn deserialize_message_unknown_type_returns_err() {
let data = vec![0x00u8, 0x01, 0x00, 0x00];
let result = deserialize_message(&data, "unknown_pkg/msg/Unknown");
assert!(result.is_err());
}
#[test]
fn deserialize_message_too_short_returns_err() {
let result = deserialize_message(&[0x00, 0x01], "sensor_msgs/msg/Imu");
assert!(result.is_err());
}
#[test]
fn deserialize_message_dispatches_point_stamped() {
let mut data = le_header();
push_i32_le(&mut data, 0);
push_u32_le(&mut data, 0);
push_empty_string(&mut data);
data.extend_from_slice(&[0u8; 12]); push_f64_le(&mut data, 1.0);
push_f64_le(&mut data, 2.0);
push_f64_le(&mut data, 3.0);
let result = deserialize_message(&data, "geometry_msgs/msg/PointStamped");
assert!(result.is_ok());
}
#[test]
fn deserialize_message_dispatches_nav_sat_fix() {
let mut data = le_header();
push_i32_le(&mut data, 0);
push_u32_le(&mut data, 0);
push_empty_string(&mut data);
data.extend_from_slice(&[0u8; 4]); push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
push_f64_le(&mut data, 0.0);
let result = deserialize_message(&data, "sensor_msgs/msg/NavSatFix");
assert!(result.is_ok());
}
#[test]
fn deserialize_message_dispatches_transform_stamped() {
let mut data = le_header();
push_i32_le(&mut data, 0);
push_u32_le(&mut data, 0);
push_empty_string(&mut data);
push_empty_string(&mut data); for _ in 0..7 {
push_f64_le(&mut data, 0.0);
}
let result = deserialize_message(&data, "geometry_msgs/msg/TransformStamped");
assert!(result.is_ok());
}
#[test]
fn deserialize_message_dispatches_odometry() {
let mut data = vec![0u8; 600];
data[1] = 0x01;
let _ = deserialize_message(&data, "nav_msgs/msg/Odometry");
}
#[test]
fn deserialize_message_dispatches_pose_with_covariance_stamped() {
let mut data = vec![0u8; 400];
data[1] = 0x01;
let _ = deserialize_message(&data, "geometry_msgs/msg/PoseWithCovarianceStamped");
}
#[test]
fn deserialize_message_dispatches_imu() {
let mut data = vec![0u8; 324];
data[1] = 0x01;
let _ = deserialize_message(&data, "sensor_msgs/msg/Imu");
}
}