1use crate::FrameIdString;
2use cu_spatial_payloads::Transform3D;
3use cu29::clock::{CuTime, CuTimeRange};
4use dashmap::DashMap;
5use num_traits;
6use serde::{Deserialize, Serialize};
7use std::collections::VecDeque;
8use std::fmt::Debug;
9use std::sync::{Arc, RwLock};
10
11const DEFAULT_CACHE_SIZE: usize = 100;
12
13#[derive(Clone, Debug, Serialize, Deserialize)]
14pub struct StampedTransform<T: Copy + Debug + Default + 'static> {
15 pub transform: Transform3D<T>,
16 pub stamp: CuTime,
17 pub parent_frame: FrameIdString,
18 pub child_frame: FrameIdString,
19}
20
21impl<
22 T: Copy
23 + Debug
24 + 'static
25 + Default
26 + std::ops::Add<Output = T>
27 + std::ops::Sub<Output = T>
28 + std::ops::Mul<Output = T>
29 + std::ops::Div<Output = T>
30 + num_traits::NumCast,
31> StampedTransform<T>
32{
33 pub fn compute_velocity(
42 &self,
43 previous: &Self,
44 ) -> Option<crate::velocity::VelocityTransform<T>> {
45 if self.parent_frame != previous.parent_frame || self.child_frame != previous.child_frame {
47 return None;
48 }
49
50 let dt_nanos = self.stamp.as_nanos() as i64 - previous.stamp.as_nanos() as i64;
52 if dt_nanos <= 0 {
53 return None;
54 }
55
56 let dt = dt_nanos as f64 / 1_000_000_000.0;
58
59 let dt_t = num_traits::cast::cast::<f64, T>(dt)?;
63
64 let self_mat = self.transform.to_matrix();
66 let prev_mat = previous.transform.to_matrix();
67 let mut linear_velocity = [T::default(); 3];
68 for (i, vel) in linear_velocity.iter_mut().enumerate() {
71 let pos_diff = self_mat[3][i] - prev_mat[3][i];
73 *vel = pos_diff / dt_t;
75 }
76
77 let rot1 = [
79 [prev_mat[0][0], prev_mat[0][1], prev_mat[0][2]],
80 [prev_mat[1][0], prev_mat[1][1], prev_mat[1][2]],
81 [prev_mat[2][0], prev_mat[2][1], prev_mat[2][2]],
82 ];
83
84 let rot2 = [
85 [self_mat[0][0], self_mat[0][1], self_mat[0][2]],
86 [self_mat[1][0], self_mat[1][1], self_mat[1][2]],
87 [self_mat[2][0], self_mat[2][1], self_mat[2][2]],
88 ];
89
90 let rot1_t = [
96 [rot1[0][0], rot1[1][0], rot1[2][0]],
97 [rot1[0][1], rot1[1][1], rot1[2][1]],
98 [rot1[0][2], rot1[1][2], rot1[2][2]],
99 ];
100
101 let mut rot_diff = [[T::default(); 3]; 3];
103 for i in 0..3 {
104 for j in 0..3 {
105 let mut sum = T::default();
106 for (k, r1t) in rot1_t.iter().enumerate() {
107 sum = sum + (rot2[i][k] * r1t[j]);
109 }
110 rot_diff[i][j] = sum;
111 }
112 }
113
114 let mut angular_velocity = [T::default(); 3];
120
121 angular_velocity[0] = (rot_diff[2][1] - rot_diff[1][2]) / (dt_t + dt_t);
123
124 angular_velocity[1] = (rot_diff[0][2] - rot_diff[2][0]) / (dt_t + dt_t);
126
127 angular_velocity[2] = (rot_diff[1][0] - rot_diff[0][1]) / (dt_t + dt_t);
129
130 Some(crate::velocity::VelocityTransform {
131 linear: linear_velocity,
132 angular: angular_velocity,
133 })
134 }
135}
136
137#[derive(Clone, Debug, Serialize, Deserialize)]
139struct TransformBufferInternal<T: Copy + Debug + Default + 'static> {
140 transforms: VecDeque<StampedTransform<T>>,
141 max_capacity: usize,
142}
143
144#[derive(Clone, Debug)]
146pub struct ConstTransformBuffer<T: Copy + Debug + Default + 'static, const N: usize> {
147 transforms: [Option<StampedTransform<T>>; N],
148 count: usize,
149 head: usize, }
151
152#[derive(Clone)]
154pub struct TransformBuffer<T: Copy + Debug + Default + 'static> {
155 buffer: Arc<RwLock<TransformBufferInternal<T>>>,
156}
157
158#[derive(Clone)]
160pub struct ConstTransformBufferSync<T: Copy + Debug + Default + 'static, const N: usize> {
161 buffer: Arc<RwLock<ConstTransformBuffer<T, N>>>,
162}
163
164impl<T: Copy + Debug + Default + 'static, const N: usize> ConstTransformBuffer<T, N> {
165 pub fn new() -> Self {
166 Self {
167 transforms: [const { None }; N],
168 count: 0,
169 head: 0,
170 }
171 }
172
173 pub fn add_transform(&mut self, transform: StampedTransform<T>) {
175 if self.count == 0 {
176 self.transforms[0] = Some(transform);
178 self.count = 1;
179 self.head = 1;
180 } else if self.count < N {
181 let mut insert_pos = 0;
183 for i in 0..self.count {
184 if let Some(ref t) = self.transforms[i] {
185 if t.stamp <= transform.stamp {
186 insert_pos = i + 1;
187 } else {
188 break;
189 }
190 }
191 }
192
193 for i in (insert_pos..self.count).rev() {
195 self.transforms[i + 1] = self.transforms[i].take();
196 }
197
198 self.transforms[insert_pos] = Some(transform);
199 self.count += 1;
200 if self.count < N {
201 self.head = self.count;
202 } else {
203 self.head = 0; }
205 } else {
206 self.transforms[self.head] = Some(transform);
209 self.head = (self.head + 1) % N;
210 }
211 }
212
213 pub fn get_latest_transform(&self) -> Option<&StampedTransform<T>> {
214 if self.count == 0 {
215 return None;
216 }
217
218 let mut latest_time = None;
219 let mut latest_idx = 0;
220
221 for i in 0..self.count.min(N) {
222 if let Some(ref t) = self.transforms[i]
223 && (latest_time.is_none() || t.stamp > latest_time.unwrap())
224 {
225 latest_time = Some(t.stamp);
226 latest_idx = i;
227 }
228 }
229
230 self.transforms[latest_idx].as_ref()
231 }
232
233 pub fn get_time_range(&self) -> Option<CuTimeRange> {
234 if self.count == 0 {
235 return None;
236 }
237
238 let mut min_time = None;
239 let mut max_time = None;
240
241 for i in 0..self.count.min(N) {
242 if let Some(ref t) = self.transforms[i] {
243 match (min_time, max_time) {
244 (None, None) => {
245 min_time = Some(t.stamp);
246 max_time = Some(t.stamp);
247 }
248 (Some(min), Some(max)) => {
249 if t.stamp < min {
250 min_time = Some(t.stamp);
251 }
252 if t.stamp > max {
253 max_time = Some(t.stamp);
254 }
255 }
256 _ => unreachable!(),
257 }
258 }
259 }
260
261 Some(CuTimeRange {
262 start: min_time.unwrap(),
263 end: max_time.unwrap(),
264 })
265 }
266
267 pub fn get_transforms_in_range(
268 &self,
269 start_time: CuTime,
270 end_time: CuTime,
271 ) -> Vec<&StampedTransform<T>> {
272 let mut result = Vec::new();
273
274 for i in 0..self.count.min(N) {
275 if let Some(ref t) = self.transforms[i]
276 && t.stamp >= start_time
277 && t.stamp <= end_time
278 {
279 result.push(t);
280 }
281 }
282
283 result.sort_by_key(|t| t.stamp);
284 result
285 }
286
287 pub fn get_closest_transform(&self, time: CuTime) -> Option<&StampedTransform<T>> {
288 if self.count == 0 {
289 return None;
290 }
291
292 let mut closest_diff = None;
293 let mut closest_idx = 0;
294
295 for i in 0..self.count.min(N) {
296 if let Some(ref t) = self.transforms[i] {
297 let diff = if t.stamp.as_nanos() > time.as_nanos() {
298 t.stamp.as_nanos() - time.as_nanos()
299 } else {
300 time.as_nanos() - t.stamp.as_nanos()
301 };
302
303 if closest_diff.is_none() || diff < closest_diff.unwrap() {
304 closest_diff = Some(diff);
305 closest_idx = i;
306 }
307 }
308 }
309
310 self.transforms[closest_idx].as_ref()
311 }
312}
313
314impl<T: Copy + Debug + Default + 'static, const N: usize> Default for ConstTransformBuffer<T, N> {
315 fn default() -> Self {
316 Self::new()
317 }
318}
319
320impl<T: Copy + Debug + Default + 'static> TransformBufferInternal<T> {
321 fn new() -> Self {
322 Self::with_capacity(DEFAULT_CACHE_SIZE)
323 }
324
325 fn with_capacity(capacity: usize) -> Self {
326 Self {
327 transforms: VecDeque::with_capacity(capacity),
328 max_capacity: capacity,
329 }
330 }
331
332 fn add_transform(&mut self, transform: StampedTransform<T>) {
333 let pos = self
334 .transforms
335 .partition_point(|t| t.stamp <= transform.stamp);
336
337 self.transforms.insert(pos, transform);
338
339 while self.transforms.len() > self.max_capacity {
340 self.transforms.pop_front();
341 }
342 }
343
344 fn get_latest_transform(&self) -> Option<&StampedTransform<T>> {
345 self.transforms.back()
346 }
347
348 fn get_time_range(&self) -> Option<CuTimeRange> {
349 if self.transforms.is_empty() {
350 return None;
351 }
352
353 Some(CuTimeRange {
354 start: self.transforms.front().unwrap().stamp,
355 end: self.transforms.back().unwrap().stamp,
356 })
357 }
358
359 fn get_transforms_in_range(
360 &self,
361 start_time: CuTime,
362 end_time: CuTime,
363 ) -> Vec<&StampedTransform<T>> {
364 self.transforms
365 .iter()
366 .filter(|t| t.stamp >= start_time && t.stamp <= end_time)
367 .collect()
368 }
369
370 fn get_closest_transform(&self, time: CuTime) -> Option<&StampedTransform<T>> {
371 if self.transforms.is_empty() {
372 return None;
373 }
374
375 let pos = self.transforms.partition_point(|t| t.stamp <= time);
376
377 match pos {
378 0 => self.transforms.front(),
379
380 p if p == self.transforms.len() => self.transforms.back(),
381
382 p => {
383 let before = &self.transforms[p - 1];
384 let after = &self.transforms[p];
385
386 if time.as_nanos() - before.stamp.as_nanos()
387 < after.stamp.as_nanos() - time.as_nanos()
388 {
389 Some(before)
390 } else {
391 Some(after)
392 }
393 }
394 }
395 }
396}
397
398impl<T: Copy + Debug + Default + 'static> TransformBuffer<T> {
399 pub fn new() -> Self {
400 Self {
401 buffer: Arc::new(RwLock::new(TransformBufferInternal::new())),
402 }
403 }
404
405 pub fn with_capacity(capacity: usize) -> Self {
406 Self {
407 buffer: Arc::new(RwLock::new(TransformBufferInternal::with_capacity(
408 capacity,
409 ))),
410 }
411 }
412
413 pub fn add_transform(&self, transform: StampedTransform<T>) {
415 let mut buffer = self.buffer.write().unwrap();
416 buffer.add_transform(transform);
417 }
418
419 pub fn get_latest_transform(&self) -> Option<StampedTransform<T>> {
421 let buffer = self.buffer.read().unwrap();
422 buffer.get_latest_transform().cloned()
423 }
424
425 pub fn get_time_range(&self) -> Option<CuTimeRange> {
427 let buffer = self.buffer.read().unwrap();
428 buffer.get_time_range()
429 }
430
431 pub fn get_transforms_in_range(
433 &self,
434 start_time: CuTime,
435 end_time: CuTime,
436 ) -> Vec<StampedTransform<T>> {
437 let buffer = self.buffer.read().unwrap();
438 buffer
439 .get_transforms_in_range(start_time, end_time)
440 .into_iter()
441 .cloned()
442 .collect()
443 }
444
445 pub fn get_closest_transform(&self, time: CuTime) -> Option<StampedTransform<T>> {
447 let buffer = self.buffer.read().unwrap();
448 buffer.get_closest_transform(time).cloned()
449 }
450
451 pub fn get_transforms_around(
453 &self,
454 time: CuTime,
455 ) -> Option<(StampedTransform<T>, StampedTransform<T>)> {
456 let buffer = self.buffer.read().unwrap();
457
458 if buffer.transforms.len() < 2 {
459 return None;
460 }
461
462 let pos = buffer.transforms.partition_point(|t| t.stamp <= time);
463
464 match pos {
465 0 => Some((buffer.transforms[0].clone(), buffer.transforms[1].clone())),
467
468 p if p >= buffer.transforms.len() => {
470 let len = buffer.transforms.len();
471 Some((
472 buffer.transforms[len - 2].clone(),
473 buffer.transforms[len - 1].clone(),
474 ))
475 }
476
477 p => Some((
479 buffer.transforms[p - 1].clone(),
480 buffer.transforms[p].clone(),
481 )),
482 }
483 }
484
485 pub fn compute_velocity_at_time(
487 &self,
488 time: CuTime,
489 ) -> Option<crate::velocity::VelocityTransform<T>>
490 where
491 T: Default
492 + std::ops::Add<Output = T>
493 + std::ops::Sub<Output = T>
494 + std::ops::Mul<Output = T>
495 + std::ops::Div<Output = T>
496 + num_traits::NumCast,
497 {
498 let transforms = self.get_transforms_around(time)?;
499
500 let (before, after) = if transforms.0.stamp < transforms.1.stamp {
502 (transforms.0, transforms.1)
503 } else {
504 (transforms.1, transforms.0)
505 };
506
507 after.compute_velocity(&before)
509 }
510}
511
512impl<T: Copy + std::fmt::Debug + Default + 'static> Default for TransformBuffer<T> {
513 fn default() -> Self {
514 Self::new()
515 }
516}
517
518impl<T: Copy + Debug + Default + 'static, const N: usize> ConstTransformBufferSync<T, N> {
519 pub fn new() -> Self {
520 Self {
521 buffer: Arc::new(RwLock::new(ConstTransformBuffer::new())),
522 }
523 }
524
525 pub fn add_transform(&self, transform: StampedTransform<T>) {
527 let mut buffer = self.buffer.write().unwrap();
528 buffer.add_transform(transform);
529 }
530
531 pub fn get_latest_transform(&self) -> Option<StampedTransform<T>> {
533 let buffer = self.buffer.read().unwrap();
534 buffer.get_latest_transform().cloned()
535 }
536
537 pub fn get_time_range(&self) -> Option<CuTimeRange> {
539 let buffer = self.buffer.read().unwrap();
540 buffer.get_time_range()
541 }
542
543 pub fn get_transforms_in_range(
545 &self,
546 start_time: CuTime,
547 end_time: CuTime,
548 ) -> Vec<StampedTransform<T>> {
549 let buffer = self.buffer.read().unwrap();
550 buffer
551 .get_transforms_in_range(start_time, end_time)
552 .into_iter()
553 .cloned()
554 .collect()
555 }
556
557 pub fn get_closest_transform(&self, time: CuTime) -> Option<StampedTransform<T>> {
559 let buffer = self.buffer.read().unwrap();
560 buffer.get_closest_transform(time).cloned()
561 }
562
563 pub fn get_transforms_around(
565 &self,
566 time: CuTime,
567 ) -> Option<(StampedTransform<T>, StampedTransform<T>)> {
568 let buffer = self.buffer.read().unwrap();
569
570 if buffer.count < 2 {
572 return None;
573 }
574
575 let mut best_pair: Option<(usize, usize)> = None;
576 let mut best_distance = u64::MAX;
577
578 for i in 0..buffer.count.min(N) {
580 if let Some(ref t1) = buffer.transforms[i] {
581 for j in (i + 1)..buffer.count.min(N) {
582 if let Some(ref t2) = buffer.transforms[j] {
583 let (earlier, later) = if t1.stamp <= t2.stamp {
585 (t1, t2)
586 } else {
587 (t2, t1)
588 };
589
590 let distance = if time <= earlier.stamp {
592 earlier.stamp.as_nanos() - time.as_nanos()
594 } else if time >= later.stamp {
595 time.as_nanos() - later.stamp.as_nanos()
597 } else {
598 0
600 };
601
602 if distance < best_distance {
603 best_distance = distance;
604 best_pair = Some((i, j));
605 }
606 }
607 }
608 }
609 }
610
611 if let Some((i, j)) = best_pair {
612 let t1 = buffer.transforms[i].as_ref()?.clone();
613 let t2 = buffer.transforms[j].as_ref()?.clone();
614
615 if t1.stamp <= t2.stamp {
617 Some((t1, t2))
618 } else {
619 Some((t2, t1))
620 }
621 } else {
622 None
623 }
624 }
625
626 pub fn compute_velocity_at_time(
628 &self,
629 time: CuTime,
630 ) -> Option<crate::velocity::VelocityTransform<T>>
631 where
632 T: Default
633 + std::ops::Add<Output = T>
634 + std::ops::Sub<Output = T>
635 + std::ops::Mul<Output = T>
636 + std::ops::Div<Output = T>
637 + num_traits::NumCast,
638 {
639 let transforms = self.get_transforms_around(time)?;
640
641 let (before, after) = if transforms.0.stamp < transforms.1.stamp {
643 (transforms.0, transforms.1)
644 } else {
645 (transforms.1, transforms.0)
646 };
647
648 after.compute_velocity(&before)
650 }
651}
652
653impl<T: Copy + Debug + Default + 'static, const N: usize> Default
654 for ConstTransformBufferSync<T, N>
655{
656 fn default() -> Self {
657 Self::new()
658 }
659}
660
661pub struct TransformStore<T: Copy + Debug + Default + 'static> {
663 buffers: DashMap<(FrameIdString, FrameIdString), TransformBuffer<T>>,
664}
665
666impl<T: Copy + Debug + Default + 'static> TransformStore<T> {
667 pub fn new() -> Self {
668 Self {
669 buffers: DashMap::new(),
670 }
671 }
672
673 pub fn get_or_create_buffer(&self, parent: &str, child: &str) -> TransformBuffer<T> {
675 let parent_frame = FrameIdString::from(parent).expect("Parent frame name too long");
676 let child_frame = FrameIdString::from(child).expect("Child frame name too long");
677 self.buffers
678 .entry((parent_frame, child_frame))
679 .or_insert_with(|| TransformBuffer::new())
680 .clone()
681 }
682
683 pub fn add_transform(&self, transform: StampedTransform<T>) {
685 let buffer = self.get_or_create_buffer(&transform.parent_frame, &transform.child_frame);
686 buffer.add_transform(transform);
687 }
688
689 pub fn get_buffer(&self, parent: &str, child: &str) -> Option<TransformBuffer<T>> {
691 let parent_frame = FrameIdString::from(parent).ok()?;
692 let child_frame = FrameIdString::from(child).ok()?;
693 self.buffers
694 .get(&(parent_frame, child_frame))
695 .map(|entry| entry.clone())
696 }
697}
698
699impl<T: Copy + Debug + Default + 'static> Default for TransformStore<T> {
700 fn default() -> Self {
701 Self::new()
702 }
703}
704
705pub struct ConstTransformStore<T: Copy + Debug + Default + 'static, const N: usize> {
707 buffers: DashMap<(FrameIdString, FrameIdString), ConstTransformBufferSync<T, N>>,
708}
709
710impl<T: Copy + Debug + Default + 'static, const N: usize> ConstTransformStore<T, N> {
711 pub fn new() -> Self {
712 Self {
713 buffers: DashMap::new(),
714 }
715 }
716
717 pub fn get_or_create_buffer(
719 &self,
720 parent: &str,
721 child: &str,
722 ) -> ConstTransformBufferSync<T, N> {
723 let parent_frame = FrameIdString::from(parent).expect("Parent frame name too long");
724 let child_frame = FrameIdString::from(child).expect("Child frame name too long");
725 self.buffers
726 .entry((parent_frame, child_frame))
727 .or_insert_with(|| ConstTransformBufferSync::new())
728 .clone()
729 }
730
731 pub fn add_transform(&self, transform: StampedTransform<T>) {
733 let buffer = self.get_or_create_buffer(&transform.parent_frame, &transform.child_frame);
734 buffer.add_transform(transform);
735 }
736
737 pub fn get_buffer(&self, parent: &str, child: &str) -> Option<ConstTransformBufferSync<T, N>> {
739 let parent_frame = FrameIdString::from(parent).ok()?;
740 let child_frame = FrameIdString::from(child).ok()?;
741 self.buffers
742 .get(&(parent_frame, child_frame))
743 .map(|entry| entry.clone())
744 }
745}
746
747impl<T: Copy + Debug + Default + 'static, const N: usize> Default for ConstTransformStore<T, N> {
748 fn default() -> Self {
749 Self::new()
750 }
751}
752
753#[cfg(test)]
754mod tests {
755 use super::*;
756 fn assert_approx_eq(actual: f32, expected: f32, epsilon: f32) {
758 let diff = (actual - expected).abs();
759 assert!(
760 diff <= epsilon,
761 "expected {expected}, got {actual}, difference {diff} exceeds epsilon {epsilon}",
762 );
763 }
764 use cu29::clock::CuDuration;
765
766 #[test]
767 fn test_add_transform() {
768 let buffer = TransformBuffer::<f32>::new();
769
770 let transform = StampedTransform {
771 transform: Transform3D::default(),
772 stamp: CuDuration(1000),
773 parent_frame: FrameIdString::from("world").unwrap(),
774 child_frame: FrameIdString::from("robot").unwrap(),
775 };
776
777 buffer.add_transform(transform);
778
779 let latest = buffer.get_latest_transform();
780 assert!(latest.is_some());
781 }
782
783 #[test]
784 fn test_time_ordering() {
785 let buffer = TransformBuffer::<f32>::new();
786
787 let transform1 = StampedTransform {
788 transform: Transform3D::default(),
789 stamp: CuDuration(2000),
790 parent_frame: FrameIdString::from("world").unwrap(),
791 child_frame: FrameIdString::from("robot").unwrap(),
792 };
793
794 let transform2 = StampedTransform {
795 transform: Transform3D::default(),
796 stamp: CuDuration(1000),
797 parent_frame: FrameIdString::from("world").unwrap(),
798 child_frame: FrameIdString::from("robot").unwrap(),
799 };
800
801 let transform3 = StampedTransform {
802 transform: Transform3D::default(),
803 stamp: CuDuration(3000),
804 parent_frame: FrameIdString::from("world").unwrap(),
805 child_frame: FrameIdString::from("robot").unwrap(),
806 };
807
808 buffer.add_transform(transform1);
809 buffer.add_transform(transform2);
810 buffer.add_transform(transform3);
811
812 let range = buffer.get_time_range().unwrap();
813 assert_eq!(range.start.as_nanos(), 1000);
814 assert_eq!(range.end.as_nanos(), 3000);
815
816 let transforms = buffer.get_transforms_in_range(CuDuration(1500), CuDuration(2500));
817 assert_eq!(transforms.len(), 1);
818 assert_eq!(transforms[0].stamp.as_nanos(), 2000);
819 }
820
821 #[test]
822 fn test_capacity_limit() {
823 let buffer = TransformBuffer::<f32>::with_capacity(2);
824
825 let transform1 = StampedTransform {
826 transform: Transform3D::default(),
827 stamp: CuDuration(1000),
828 parent_frame: FrameIdString::from("world").unwrap(),
829 child_frame: FrameIdString::from("robot").unwrap(),
830 };
831
832 let transform2 = StampedTransform {
833 transform: Transform3D::default(),
834 stamp: CuDuration(2000),
835 parent_frame: FrameIdString::from("world").unwrap(),
836 child_frame: FrameIdString::from("robot").unwrap(),
837 };
838
839 let transform3 = StampedTransform {
840 transform: Transform3D::default(),
841 stamp: CuDuration(3000),
842 parent_frame: FrameIdString::from("world").unwrap(),
843 child_frame: FrameIdString::from("robot").unwrap(),
844 };
845
846 buffer.add_transform(transform1);
847 buffer.add_transform(transform2);
848 buffer.add_transform(transform3);
849
850 let range = buffer.get_time_range().unwrap();
851 assert_eq!(range.start.as_nanos(), 2000);
852 assert_eq!(range.end.as_nanos(), 3000);
853 }
854
855 #[test]
856 fn test_get_closest_transform() {
857 let buffer = TransformBuffer::<f32>::new();
858
859 let transform1 = StampedTransform {
860 transform: Transform3D::default(),
861 stamp: CuDuration(1000),
862 parent_frame: FrameIdString::from("world").unwrap(),
863 child_frame: FrameIdString::from("robot").unwrap(),
864 };
865
866 let transform2 = StampedTransform {
867 transform: Transform3D::default(),
868 stamp: CuDuration(3000),
869 parent_frame: FrameIdString::from("world").unwrap(),
870 child_frame: FrameIdString::from("robot").unwrap(),
871 };
872
873 buffer.add_transform(transform1);
874 buffer.add_transform(transform2);
875
876 let closest = buffer.get_closest_transform(CuDuration(1000));
877 assert!(closest.is_some());
878 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
879
880 let closest = buffer.get_closest_transform(CuDuration(500));
881 assert!(closest.is_some());
882 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
883
884 let closest = buffer.get_closest_transform(CuDuration(4000));
885 assert!(closest.is_some());
886 assert_eq!(closest.unwrap().stamp.as_nanos(), 3000);
887
888 let closest = buffer.get_closest_transform(CuDuration(1600));
889 assert!(closest.is_some());
890 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
891
892 let closest = buffer.get_closest_transform(CuDuration(2600));
893 assert!(closest.is_some());
894 assert_eq!(closest.unwrap().stamp.as_nanos(), 3000);
895 }
896
897 #[test]
898 fn test_transform_store() {
899 let store = TransformStore::<f32>::new();
900
901 let transform = StampedTransform {
902 transform: Transform3D::default(),
903 stamp: CuDuration(1000),
904 parent_frame: FrameIdString::from("world").unwrap(),
905 child_frame: FrameIdString::from("robot").unwrap(),
906 };
907
908 store.add_transform(transform.clone());
909
910 let buffer = store.get_buffer("world", "robot").unwrap();
911 let closest = buffer.get_closest_transform(CuDuration(1000));
912 assert!(closest.is_some());
913
914 let missing = store.get_buffer("world", "camera");
916 assert!(missing.is_none());
917
918 let _ = store.get_or_create_buffer("world", "camera");
920 assert!(store.get_buffer("world", "camera").is_some());
921 }
922
923 #[test]
924 fn test_compute_velocity() {
925 let mut transform1 = StampedTransform {
927 transform: Transform3D::<f32>::default(),
928 stamp: CuDuration(1_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
930 child_frame: FrameIdString::from("robot").unwrap(),
931 };
932
933 let mut transform2 = StampedTransform {
934 transform: Transform3D::<f32>::default(),
935 stamp: CuDuration(2_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
937 child_frame: FrameIdString::from("robot").unwrap(),
938 };
939
940 transform1.transform = Transform3D::from_matrix([
944 [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0], ]);
949
950 transform2.transform = Transform3D::from_matrix([
951 [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [1.0, 2.0, 0.0, 1.0], ]);
956
957 let velocity = transform2.compute_velocity(&transform1);
959 assert!(velocity.is_some());
960
961 let vel = velocity.unwrap();
962
963 assert_approx_eq(vel.linear[0], 1.0, 1e-5);
965 assert_approx_eq(vel.linear[1], 2.0, 1e-5);
966 assert_approx_eq(vel.linear[2], 0.0, 1e-5);
967
968 assert_approx_eq(vel.angular[0], 0.0, 1e-5);
971 assert_approx_eq(vel.angular[1], 0.0, 1e-5);
972 assert_approx_eq(vel.angular[2], 0.0, 1e-5);
973 }
974
975 #[test]
976 fn test_velocity_failure_cases() {
977 let transform1 = StampedTransform {
979 transform: Transform3D::<f32>::default(),
980 stamp: CuDuration(1000),
981 parent_frame: FrameIdString::from("world").unwrap(),
982 child_frame: FrameIdString::from("robot").unwrap(),
983 };
984
985 let transform2 = StampedTransform {
986 transform: Transform3D::<f32>::default(),
987 stamp: CuDuration(2000),
988 parent_frame: FrameIdString::from("different").unwrap(), child_frame: FrameIdString::from("robot").unwrap(),
990 };
991
992 let velocity = transform2.compute_velocity(&transform1);
993 assert!(velocity.is_none());
994
995 let transform1 = StampedTransform {
997 transform: Transform3D::<f32>::default(),
998 stamp: CuDuration(2000), parent_frame: FrameIdString::from("world").unwrap(),
1000 child_frame: FrameIdString::from("robot").unwrap(),
1001 };
1002
1003 let transform2 = StampedTransform {
1004 transform: Transform3D::<f32>::default(),
1005 stamp: CuDuration(1000), parent_frame: FrameIdString::from("world").unwrap(),
1007 child_frame: FrameIdString::from("robot").unwrap(),
1008 };
1009
1010 let velocity = transform2.compute_velocity(&transform1);
1011 assert!(velocity.is_none());
1012 }
1013
1014 #[test]
1015 fn test_get_transforms_around() {
1016 let buffer = TransformBuffer::<f32>::new();
1017
1018 let transform1 = StampedTransform {
1020 transform: Transform3D::default(),
1021 stamp: CuDuration(1000),
1022 parent_frame: FrameIdString::from("world").unwrap(),
1023 child_frame: FrameIdString::from("robot").unwrap(),
1024 };
1025
1026 let transform2 = StampedTransform {
1027 transform: Transform3D::default(),
1028 stamp: CuDuration(2000),
1029 parent_frame: FrameIdString::from("world").unwrap(),
1030 child_frame: FrameIdString::from("robot").unwrap(),
1031 };
1032
1033 let transform3 = StampedTransform {
1034 transform: Transform3D::default(),
1035 stamp: CuDuration(3000),
1036 parent_frame: FrameIdString::from("world").unwrap(),
1037 child_frame: FrameIdString::from("robot").unwrap(),
1038 };
1039
1040 buffer.add_transform(transform1);
1041 buffer.add_transform(transform2);
1042 buffer.add_transform(transform3);
1043
1044 let transforms = buffer.get_transforms_around(CuDuration(2500));
1046 assert!(transforms.is_some());
1047 let (t1, t2) = transforms.unwrap();
1048 assert_eq!(t1.stamp.as_nanos(), 2000); assert_eq!(t2.stamp.as_nanos(), 3000); let transforms = buffer.get_transforms_around(CuDuration(500));
1053 assert!(transforms.is_some());
1054 let (t1, t2) = transforms.unwrap();
1055 assert_eq!(t1.stamp.as_nanos(), 1000); assert_eq!(t2.stamp.as_nanos(), 2000); let transforms = buffer.get_transforms_around(CuDuration(4000));
1060 assert!(transforms.is_some());
1061 let (t1, t2) = transforms.unwrap();
1062 assert_eq!(t1.stamp.as_nanos(), 2000); assert_eq!(t2.stamp.as_nanos(), 3000); }
1065
1066 #[test]
1067 fn test_compute_velocity_from_buffer() {
1068 let buffer = TransformBuffer::<f32>::new();
1069
1070 let mut transform1 = StampedTransform {
1072 transform: Transform3D::<f32>::default(),
1073 stamp: CuDuration(1_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1075 child_frame: FrameIdString::from("robot").unwrap(),
1076 };
1077
1078 let mut transform2 = StampedTransform {
1079 transform: Transform3D::<f32>::default(),
1080 stamp: CuDuration(2_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1082 child_frame: FrameIdString::from("robot").unwrap(),
1083 };
1084
1085 transform1.transform = Transform3D::from_matrix([
1088 [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0], ]);
1093
1094 transform2.transform = Transform3D::from_matrix([
1095 [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [2.0, 0.0, 0.0, 1.0], ]);
1100
1101 buffer.add_transform(transform1);
1102 buffer.add_transform(transform2);
1103
1104 let velocity = buffer.compute_velocity_at_time(CuDuration(1_500_000_000)); assert!(velocity.is_some());
1107
1108 let vel = velocity.unwrap();
1109 assert_approx_eq(vel.linear[0], 2.0, 1e-5);
1111 assert_approx_eq(vel.linear[1], 0.0, 1e-5);
1112 assert_approx_eq(vel.linear[2], 0.0, 1e-5);
1113 }
1114
1115 #[test]
1116 fn test_compute_angular_velocity() {
1117 let buffer = TransformBuffer::<f32>::new();
1118
1119 let mut transform1 = StampedTransform {
1121 transform: Transform3D::<f32>::default(),
1122 stamp: CuDuration(1_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1124 child_frame: FrameIdString::from("robot").unwrap(),
1125 };
1126
1127 transform1.transform = Transform3D::from_matrix([
1129 [1.0, 0.0, 0.0, 0.0],
1130 [0.0, 1.0, 0.0, 0.0],
1131 [0.0, 0.0, 1.0, 0.0],
1132 [0.0, 0.0, 0.0, 1.0],
1133 ]);
1134
1135 let mut transform2 = StampedTransform {
1136 transform: Transform3D::<f32>::default(),
1137 stamp: CuDuration(2_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1139 child_frame: FrameIdString::from("robot").unwrap(),
1140 };
1141
1142 transform2.transform = Transform3D::from_matrix([
1146 [0.0, -1.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0], ]);
1151
1152 buffer.add_transform(transform1);
1153 buffer.add_transform(transform2);
1154
1155 let velocity = buffer.compute_velocity_at_time(CuDuration(1_500_000_000)); assert!(velocity.is_some());
1158
1159 let vel = velocity.unwrap();
1160
1161 assert_approx_eq(vel.angular[0], 0.0, 1e-5);
1164 assert_approx_eq(vel.angular[1], 0.0, 1e-5);
1165 assert_approx_eq(vel.angular[2], 1.0, 1e-5); }
1167
1168 #[test]
1169 fn test_const_transform_buffer_basic() {
1170 let mut buffer = ConstTransformBuffer::<f32, 10>::new();
1171
1172 let transform = StampedTransform {
1173 transform: Transform3D::default(),
1174 stamp: CuDuration(1000),
1175 parent_frame: FrameIdString::from("world").unwrap(),
1176 child_frame: FrameIdString::from("robot").unwrap(),
1177 };
1178
1179 buffer.add_transform(transform);
1180
1181 let latest = buffer.get_latest_transform();
1182 assert!(latest.is_some());
1183 assert_eq!(latest.unwrap().stamp.as_nanos(), 1000);
1184 }
1185
1186 #[test]
1187 fn test_const_transform_buffer_ordering() {
1188 let mut buffer = ConstTransformBuffer::<f32, 10>::new();
1189
1190 let transform1 = StampedTransform {
1191 transform: Transform3D::default(),
1192 stamp: CuDuration(2000),
1193 parent_frame: FrameIdString::from("world").unwrap(),
1194 child_frame: FrameIdString::from("robot").unwrap(),
1195 };
1196
1197 let transform2 = StampedTransform {
1198 transform: Transform3D::default(),
1199 stamp: CuDuration(1000),
1200 parent_frame: FrameIdString::from("world").unwrap(),
1201 child_frame: FrameIdString::from("robot").unwrap(),
1202 };
1203
1204 let transform3 = StampedTransform {
1205 transform: Transform3D::default(),
1206 stamp: CuDuration(3000),
1207 parent_frame: FrameIdString::from("world").unwrap(),
1208 child_frame: FrameIdString::from("robot").unwrap(),
1209 };
1210
1211 buffer.add_transform(transform1);
1212 buffer.add_transform(transform2);
1213 buffer.add_transform(transform3);
1214
1215 let range = buffer.get_time_range().unwrap();
1216 assert_eq!(range.start.as_nanos(), 1000);
1217 assert_eq!(range.end.as_nanos(), 3000);
1218
1219 let transforms = buffer.get_transforms_in_range(CuDuration(1500), CuDuration(2500));
1220 assert_eq!(transforms.len(), 1);
1221 assert_eq!(transforms[0].stamp.as_nanos(), 2000);
1222 }
1223
1224 #[test]
1225 fn test_const_transform_buffer_capacity() {
1226 let mut buffer = ConstTransformBuffer::<f32, 2>::new();
1227
1228 let transform1 = StampedTransform {
1229 transform: Transform3D::default(),
1230 stamp: CuDuration(1000),
1231 parent_frame: FrameIdString::from("world").unwrap(),
1232 child_frame: FrameIdString::from("robot").unwrap(),
1233 };
1234
1235 let transform2 = StampedTransform {
1236 transform: Transform3D::default(),
1237 stamp: CuDuration(2000),
1238 parent_frame: FrameIdString::from("world").unwrap(),
1239 child_frame: FrameIdString::from("robot").unwrap(),
1240 };
1241
1242 let transform3 = StampedTransform {
1243 transform: Transform3D::default(),
1244 stamp: CuDuration(3000),
1245 parent_frame: FrameIdString::from("world").unwrap(),
1246 child_frame: FrameIdString::from("robot").unwrap(),
1247 };
1248
1249 buffer.add_transform(transform1);
1250 buffer.add_transform(transform2);
1251 buffer.add_transform(transform3);
1252
1253 let latest = buffer.get_latest_transform();
1255 assert!(latest.is_some());
1256
1257 let closest = buffer.get_closest_transform(CuDuration(2500));
1259 assert!(closest.is_some());
1260 }
1261
1262 #[test]
1263 fn test_const_transform_buffer_closest() {
1264 let mut buffer = ConstTransformBuffer::<f32, 10>::new();
1265
1266 let transform1 = StampedTransform {
1267 transform: Transform3D::default(),
1268 stamp: CuDuration(1000),
1269 parent_frame: FrameIdString::from("world").unwrap(),
1270 child_frame: FrameIdString::from("robot").unwrap(),
1271 };
1272
1273 let transform2 = StampedTransform {
1274 transform: Transform3D::default(),
1275 stamp: CuDuration(3000),
1276 parent_frame: FrameIdString::from("world").unwrap(),
1277 child_frame: FrameIdString::from("robot").unwrap(),
1278 };
1279
1280 buffer.add_transform(transform1);
1281 buffer.add_transform(transform2);
1282
1283 let closest = buffer.get_closest_transform(CuDuration(1000));
1284 assert!(closest.is_some());
1285 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
1286
1287 let closest = buffer.get_closest_transform(CuDuration(500));
1288 assert!(closest.is_some());
1289 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
1290
1291 let closest = buffer.get_closest_transform(CuDuration(4000));
1292 assert!(closest.is_some());
1293 assert_eq!(closest.unwrap().stamp.as_nanos(), 3000);
1294
1295 let closest = buffer.get_closest_transform(CuDuration(1900));
1296 assert!(closest.is_some());
1297 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000); let closest = buffer.get_closest_transform(CuDuration(2100));
1300 assert!(closest.is_some());
1301 assert_eq!(closest.unwrap().stamp.as_nanos(), 3000); }
1303
1304 #[test]
1305 fn test_const_transform_buffer_sync_basic() {
1306 let buffer = ConstTransformBufferSync::<f32, 10>::new();
1307
1308 let transform = StampedTransform {
1309 transform: Transform3D::default(),
1310 stamp: CuDuration(1000),
1311 parent_frame: FrameIdString::from("world").unwrap(),
1312 child_frame: FrameIdString::from("robot").unwrap(),
1313 };
1314
1315 buffer.add_transform(transform);
1316
1317 let latest = buffer.get_latest_transform();
1318 assert!(latest.is_some());
1319 assert_eq!(latest.unwrap().stamp.as_nanos(), 1000);
1320 }
1321
1322 #[test]
1323 fn test_const_transform_buffer_sync_velocity() {
1324 let buffer = ConstTransformBufferSync::<f32, 10>::new();
1325
1326 let mut transform1 = StampedTransform {
1328 transform: Transform3D::<f32>::default(),
1329 stamp: CuDuration(1_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1331 child_frame: FrameIdString::from("robot").unwrap(),
1332 };
1333
1334 let mut transform2 = StampedTransform {
1335 transform: Transform3D::<f32>::default(),
1336 stamp: CuDuration(2_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1338 child_frame: FrameIdString::from("robot").unwrap(),
1339 };
1340
1341 transform1.transform = Transform3D::from_matrix([
1344 [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0], ]);
1349
1350 transform2.transform = Transform3D::from_matrix([
1351 [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [3.0, 0.0, 0.0, 1.0], ]);
1356
1357 buffer.add_transform(transform1);
1358 buffer.add_transform(transform2);
1359
1360 let velocity = buffer.compute_velocity_at_time(CuDuration(1_500_000_000)); assert!(velocity.is_some());
1363
1364 let vel = velocity.unwrap();
1365 assert_approx_eq(vel.linear[0], 3.0, 1e-5);
1367 assert_approx_eq(vel.linear[1], 0.0, 1e-5);
1368 assert_approx_eq(vel.linear[2], 0.0, 1e-5);
1369 }
1370
1371 #[test]
1372 fn test_const_transform_store() {
1373 let store = ConstTransformStore::<f32, 10>::new();
1374
1375 let transform = StampedTransform {
1376 transform: Transform3D::default(),
1377 stamp: CuDuration(1000),
1378 parent_frame: FrameIdString::from("world").unwrap(),
1379 child_frame: FrameIdString::from("robot").unwrap(),
1380 };
1381
1382 store.add_transform(transform.clone());
1383
1384 let buffer = store.get_buffer("world", "robot").unwrap();
1385 let closest = buffer.get_closest_transform(CuDuration(1000));
1386 assert!(closest.is_some());
1387
1388 let missing = store.get_buffer("world", "camera");
1390 assert!(missing.is_none());
1391
1392 let _ = store.get_or_create_buffer("world", "camera");
1394 assert!(store.get_buffer("world", "camera").is_some());
1395 }
1396}