isaac_sim_bridge/odometry/
mod.rs1use std::sync::OnceLock;
3
4use crate::channel::{channel_singleton, Channel};
5use crate::ffi::OdometryMeta;
6use crate::sensor::Sensor;
7
8pub struct Odometry;
12
13impl Sensor for Odometry {
14 const NAME: &'static str = "odometry";
15}
16
17pub type Callback = Box<dyn Fn(&str, &str, &str, &OdometryMeta) + Send + Sync + 'static>;
18
19#[unsafe(no_mangle)]
20pub extern "C" fn isaac_sim_bridge_channel_odometry() -> *const Channel<Callback> {
21 static SLOT: OnceLock<Box<Channel<Callback>>> = OnceLock::new();
22 channel_singleton(&SLOT)
23}
24
25fn channel() -> &'static Channel<Callback> {
26 unsafe { &*isaac_sim_bridge_channel_odometry() }
27}
28
29pub fn register_odometry_consumer<F>(cb: F)
32where
33 F: Fn(&str, &str, &str, &OdometryMeta) + Send + Sync + 'static,
34{
35 channel().register(Box::new(cb));
36}
37
38pub fn dispatch_odometry(
40 source_id: &str,
41 chassis_frame_id: &str,
42 odom_frame_id: &str,
43 meta: &OdometryMeta,
44) {
45 channel().for_each(|cb| cb(source_id, chassis_frame_id, odom_frame_id, meta));
46}
47
48pub fn odometry_consumer_count() -> usize {
50 channel().count()
51}
52
53pub fn forward_odometry(
55 source_id: &str,
56 chassis_frame_id: &str,
57 odom_frame_id: &str,
58 meta: &OdometryMeta,
59) {
60 log::debug!(
61 "[isaac-sim-rs] forward_odometry: source='{}' chassis='{}' odom='{}' pos=[{:.3},{:.3},{:.3}] q=[{:.3},{:.3},{:.3},{:.3}] lin=[{:.3},{:.3},{:.3}] ang=[{:.3},{:.3},{:.3}]",
62 source_id,
63 chassis_frame_id,
64 odom_frame_id,
65 meta.position_x,
66 meta.position_y,
67 meta.position_z,
68 meta.orientation_w,
69 meta.orientation_x,
70 meta.orientation_y,
71 meta.orientation_z,
72 meta.lin_vel_x,
73 meta.lin_vel_y,
74 meta.lin_vel_z,
75 meta.ang_vel_x,
76 meta.ang_vel_y,
77 meta.ang_vel_z,
78 );
79 dispatch_odometry(source_id, chassis_frame_id, odom_frame_id, meta);
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use std::sync::atomic::{AtomicUsize, Ordering};
86 use std::sync::Arc;
87
88 fn fake_meta() -> OdometryMeta {
89 OdometryMeta {
90 position_x: 1.0,
91 position_y: 2.0,
92 position_z: 0.5,
93 orientation_w: 1.0,
94 orientation_x: 0.0,
95 orientation_y: 0.0,
96 orientation_z: 0.0,
97 lin_vel_x: 0.3,
98 lin_vel_y: 0.0,
99 lin_vel_z: 0.0,
100 ang_vel_x: 0.0,
101 ang_vel_y: 0.0,
102 ang_vel_z: 0.1,
103 timestamp_ns: 0,
104 }
105 }
106
107 #[test]
108 fn registered_consumer_receives_dispatch_with_source() {
109 let count = Arc::new(AtomicUsize::new(0));
110 let count_clone = Arc::clone(&count);
111 let n_baseline = odometry_consumer_count();
112
113 register_odometry_consumer(move |src, chassis, odom, meta| {
114 assert_eq!(src, "/World/Carter");
115 assert_eq!(chassis, "base_link");
116 assert_eq!(odom, "odom");
117 assert_eq!(meta.position_x, 1.0);
118 assert_eq!(meta.lin_vel_x, 0.3);
119 count_clone.fetch_add(1, Ordering::SeqCst);
120 });
121
122 assert_eq!(odometry_consumer_count(), n_baseline + 1);
123
124 forward_odometry("/World/Carter", "base_link", "odom", &fake_meta());
125
126 assert_eq!(count.load(Ordering::SeqCst), 1);
127 }
128}