1use crate::FrameIdString;
2use cu29::clock::{CuTime, CuTimeRange};
3use cu_spatial_payloads::Transform3D;
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 if latest_time.is_none() || t.stamp > latest_time.unwrap() {
224 latest_time = Some(t.stamp);
225 latest_idx = i;
226 }
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 if t.stamp >= start_time && t.stamp <= end_time {
277 result.push(t);
278 }
279 }
280 }
281
282 result.sort_by_key(|t| t.stamp);
283 result
284 }
285
286 pub fn get_closest_transform(&self, time: CuTime) -> Option<&StampedTransform<T>> {
287 if self.count == 0 {
288 return None;
289 }
290
291 let mut closest_diff = None;
292 let mut closest_idx = 0;
293
294 for i in 0..self.count.min(N) {
295 if let Some(ref t) = self.transforms[i] {
296 let diff = if t.stamp.as_nanos() > time.as_nanos() {
297 t.stamp.as_nanos() - time.as_nanos()
298 } else {
299 time.as_nanos() - t.stamp.as_nanos()
300 };
301
302 if closest_diff.is_none() || diff < closest_diff.unwrap() {
303 closest_diff = Some(diff);
304 closest_idx = i;
305 }
306 }
307 }
308
309 self.transforms[closest_idx].as_ref()
310 }
311}
312
313impl<T: Copy + Debug + Default + 'static, const N: usize> Default for ConstTransformBuffer<T, N> {
314 fn default() -> Self {
315 Self::new()
316 }
317}
318
319impl<T: Copy + Debug + Default + 'static> TransformBufferInternal<T> {
320 fn new() -> Self {
321 Self::with_capacity(DEFAULT_CACHE_SIZE)
322 }
323
324 fn with_capacity(capacity: usize) -> Self {
325 Self {
326 transforms: VecDeque::with_capacity(capacity),
327 max_capacity: capacity,
328 }
329 }
330
331 fn add_transform(&mut self, transform: StampedTransform<T>) {
332 let pos = self
333 .transforms
334 .partition_point(|t| t.stamp <= transform.stamp);
335
336 self.transforms.insert(pos, transform);
337
338 while self.transforms.len() > self.max_capacity {
339 self.transforms.pop_front();
340 }
341 }
342
343 fn get_latest_transform(&self) -> Option<&StampedTransform<T>> {
344 self.transforms.back()
345 }
346
347 fn get_time_range(&self) -> Option<CuTimeRange> {
348 if self.transforms.is_empty() {
349 return None;
350 }
351
352 Some(CuTimeRange {
353 start: self.transforms.front().unwrap().stamp,
354 end: self.transforms.back().unwrap().stamp,
355 })
356 }
357
358 fn get_transforms_in_range(
359 &self,
360 start_time: CuTime,
361 end_time: CuTime,
362 ) -> Vec<&StampedTransform<T>> {
363 self.transforms
364 .iter()
365 .filter(|t| t.stamp >= start_time && t.stamp <= end_time)
366 .collect()
367 }
368
369 fn get_closest_transform(&self, time: CuTime) -> Option<&StampedTransform<T>> {
370 if self.transforms.is_empty() {
371 return None;
372 }
373
374 let pos = self.transforms.partition_point(|t| t.stamp <= time);
375
376 match pos {
377 0 => self.transforms.front(),
378
379 p if p == self.transforms.len() => self.transforms.back(),
380
381 p => {
382 let before = &self.transforms[p - 1];
383 let after = &self.transforms[p];
384
385 if time.as_nanos() - before.stamp.as_nanos()
386 < after.stamp.as_nanos() - time.as_nanos()
387 {
388 Some(before)
389 } else {
390 Some(after)
391 }
392 }
393 }
394 }
395}
396
397impl<T: Copy + Debug + Default + 'static> TransformBuffer<T> {
398 pub fn new() -> Self {
399 Self {
400 buffer: Arc::new(RwLock::new(TransformBufferInternal::new())),
401 }
402 }
403
404 pub fn with_capacity(capacity: usize) -> Self {
405 Self {
406 buffer: Arc::new(RwLock::new(TransformBufferInternal::with_capacity(
407 capacity,
408 ))),
409 }
410 }
411
412 pub fn add_transform(&self, transform: StampedTransform<T>) {
414 let mut buffer = self.buffer.write().unwrap();
415 buffer.add_transform(transform);
416 }
417
418 pub fn get_latest_transform(&self) -> Option<StampedTransform<T>> {
420 let buffer = self.buffer.read().unwrap();
421 buffer.get_latest_transform().cloned()
422 }
423
424 pub fn get_time_range(&self) -> Option<CuTimeRange> {
426 let buffer = self.buffer.read().unwrap();
427 buffer.get_time_range()
428 }
429
430 pub fn get_transforms_in_range(
432 &self,
433 start_time: CuTime,
434 end_time: CuTime,
435 ) -> Vec<StampedTransform<T>> {
436 let buffer = self.buffer.read().unwrap();
437 buffer
438 .get_transforms_in_range(start_time, end_time)
439 .into_iter()
440 .cloned()
441 .collect()
442 }
443
444 pub fn get_closest_transform(&self, time: CuTime) -> Option<StampedTransform<T>> {
446 let buffer = self.buffer.read().unwrap();
447 buffer.get_closest_transform(time).cloned()
448 }
449
450 pub fn get_transforms_around(
452 &self,
453 time: CuTime,
454 ) -> Option<(StampedTransform<T>, StampedTransform<T>)> {
455 let buffer = self.buffer.read().unwrap();
456
457 if buffer.transforms.len() < 2 {
458 return None;
459 }
460
461 let pos = buffer.transforms.partition_point(|t| t.stamp <= time);
462
463 match pos {
464 0 => Some((buffer.transforms[0].clone(), buffer.transforms[1].clone())),
466
467 p if p >= buffer.transforms.len() => {
469 let len = buffer.transforms.len();
470 Some((
471 buffer.transforms[len - 2].clone(),
472 buffer.transforms[len - 1].clone(),
473 ))
474 }
475
476 p => Some((
478 buffer.transforms[p - 1].clone(),
479 buffer.transforms[p].clone(),
480 )),
481 }
482 }
483
484 pub fn compute_velocity_at_time(
486 &self,
487 time: CuTime,
488 ) -> Option<crate::velocity::VelocityTransform<T>>
489 where
490 T: Default
491 + std::ops::Add<Output = T>
492 + std::ops::Sub<Output = T>
493 + std::ops::Mul<Output = T>
494 + std::ops::Div<Output = T>
495 + num_traits::NumCast,
496 {
497 let transforms = self.get_transforms_around(time)?;
498
499 let (before, after) = if transforms.0.stamp < transforms.1.stamp {
501 (transforms.0, transforms.1)
502 } else {
503 (transforms.1, transforms.0)
504 };
505
506 after.compute_velocity(&before)
508 }
509}
510
511impl<T: Copy + std::fmt::Debug + Default + 'static> Default for TransformBuffer<T> {
512 fn default() -> Self {
513 Self::new()
514 }
515}
516
517impl<T: Copy + Debug + Default + 'static, const N: usize> ConstTransformBufferSync<T, N> {
518 pub fn new() -> Self {
519 Self {
520 buffer: Arc::new(RwLock::new(ConstTransformBuffer::new())),
521 }
522 }
523
524 pub fn add_transform(&self, transform: StampedTransform<T>) {
526 let mut buffer = self.buffer.write().unwrap();
527 buffer.add_transform(transform);
528 }
529
530 pub fn get_latest_transform(&self) -> Option<StampedTransform<T>> {
532 let buffer = self.buffer.read().unwrap();
533 buffer.get_latest_transform().cloned()
534 }
535
536 pub fn get_time_range(&self) -> Option<CuTimeRange> {
538 let buffer = self.buffer.read().unwrap();
539 buffer.get_time_range()
540 }
541
542 pub fn get_transforms_in_range(
544 &self,
545 start_time: CuTime,
546 end_time: CuTime,
547 ) -> Vec<StampedTransform<T>> {
548 let buffer = self.buffer.read().unwrap();
549 buffer
550 .get_transforms_in_range(start_time, end_time)
551 .into_iter()
552 .cloned()
553 .collect()
554 }
555
556 pub fn get_closest_transform(&self, time: CuTime) -> Option<StampedTransform<T>> {
558 let buffer = self.buffer.read().unwrap();
559 buffer.get_closest_transform(time).cloned()
560 }
561
562 pub fn get_transforms_around(
564 &self,
565 time: CuTime,
566 ) -> Option<(StampedTransform<T>, StampedTransform<T>)> {
567 let buffer = self.buffer.read().unwrap();
568
569 if buffer.count < 2 {
571 return None;
572 }
573
574 let mut best_pair: Option<(usize, usize)> = None;
575 let mut best_distance = u64::MAX;
576
577 for i in 0..buffer.count.min(N) {
579 if let Some(ref t1) = buffer.transforms[i] {
580 for j in (i + 1)..buffer.count.min(N) {
581 if let Some(ref t2) = buffer.transforms[j] {
582 let (earlier, later) = if t1.stamp <= t2.stamp {
584 (t1, t2)
585 } else {
586 (t2, t1)
587 };
588
589 let distance = if time <= earlier.stamp {
591 earlier.stamp.as_nanos() - time.as_nanos()
593 } else if time >= later.stamp {
594 time.as_nanos() - later.stamp.as_nanos()
596 } else {
597 0
599 };
600
601 if distance < best_distance {
602 best_distance = distance;
603 best_pair = Some((i, j));
604 }
605 }
606 }
607 }
608 }
609
610 if let Some((i, j)) = best_pair {
611 let t1 = buffer.transforms[i].as_ref()?.clone();
612 let t2 = buffer.transforms[j].as_ref()?.clone();
613
614 if t1.stamp <= t2.stamp {
616 Some((t1, t2))
617 } else {
618 Some((t2, t1))
619 }
620 } else {
621 None
622 }
623 }
624
625 pub fn compute_velocity_at_time(
627 &self,
628 time: CuTime,
629 ) -> Option<crate::velocity::VelocityTransform<T>>
630 where
631 T: Default
632 + std::ops::Add<Output = T>
633 + std::ops::Sub<Output = T>
634 + std::ops::Mul<Output = T>
635 + std::ops::Div<Output = T>
636 + num_traits::NumCast,
637 {
638 let transforms = self.get_transforms_around(time)?;
639
640 let (before, after) = if transforms.0.stamp < transforms.1.stamp {
642 (transforms.0, transforms.1)
643 } else {
644 (transforms.1, transforms.0)
645 };
646
647 after.compute_velocity(&before)
649 }
650}
651
652impl<T: Copy + Debug + Default + 'static, const N: usize> Default
653 for ConstTransformBufferSync<T, N>
654{
655 fn default() -> Self {
656 Self::new()
657 }
658}
659
660pub struct TransformStore<T: Copy + Debug + Default + 'static> {
662 buffers: DashMap<(FrameIdString, FrameIdString), TransformBuffer<T>>,
663}
664
665impl<T: Copy + Debug + Default + 'static> TransformStore<T> {
666 pub fn new() -> Self {
667 Self {
668 buffers: DashMap::new(),
669 }
670 }
671
672 pub fn get_or_create_buffer(&self, parent: &str, child: &str) -> TransformBuffer<T> {
674 let parent_frame = FrameIdString::from(parent).expect("Parent frame name too long");
675 let child_frame = FrameIdString::from(child).expect("Child frame name too long");
676 self.buffers
677 .entry((parent_frame, child_frame))
678 .or_insert_with(|| TransformBuffer::new())
679 .clone()
680 }
681
682 pub fn add_transform(&self, transform: StampedTransform<T>) {
684 let buffer = self.get_or_create_buffer(&transform.parent_frame, &transform.child_frame);
685 buffer.add_transform(transform);
686 }
687
688 pub fn get_buffer(&self, parent: &str, child: &str) -> Option<TransformBuffer<T>> {
690 let parent_frame = FrameIdString::from(parent).ok()?;
691 let child_frame = FrameIdString::from(child).ok()?;
692 self.buffers
693 .get(&(parent_frame, child_frame))
694 .map(|entry| entry.clone())
695 }
696}
697
698impl<T: Copy + Debug + Default + 'static> Default for TransformStore<T> {
699 fn default() -> Self {
700 Self::new()
701 }
702}
703
704pub struct ConstTransformStore<T: Copy + Debug + Default + 'static, const N: usize> {
706 buffers: DashMap<(FrameIdString, FrameIdString), ConstTransformBufferSync<T, N>>,
707}
708
709impl<T: Copy + Debug + Default + 'static, const N: usize> ConstTransformStore<T, N> {
710 pub fn new() -> Self {
711 Self {
712 buffers: DashMap::new(),
713 }
714 }
715
716 pub fn get_or_create_buffer(
718 &self,
719 parent: &str,
720 child: &str,
721 ) -> ConstTransformBufferSync<T, N> {
722 let parent_frame = FrameIdString::from(parent).expect("Parent frame name too long");
723 let child_frame = FrameIdString::from(child).expect("Child frame name too long");
724 self.buffers
725 .entry((parent_frame, child_frame))
726 .or_insert_with(|| ConstTransformBufferSync::new())
727 .clone()
728 }
729
730 pub fn add_transform(&self, transform: StampedTransform<T>) {
732 let buffer = self.get_or_create_buffer(&transform.parent_frame, &transform.child_frame);
733 buffer.add_transform(transform);
734 }
735
736 pub fn get_buffer(&self, parent: &str, child: &str) -> Option<ConstTransformBufferSync<T, N>> {
738 let parent_frame = FrameIdString::from(parent).ok()?;
739 let child_frame = FrameIdString::from(child).ok()?;
740 self.buffers
741 .get(&(parent_frame, child_frame))
742 .map(|entry| entry.clone())
743 }
744}
745
746impl<T: Copy + Debug + Default + 'static, const N: usize> Default for ConstTransformStore<T, N> {
747 fn default() -> Self {
748 Self::new()
749 }
750}
751
752#[cfg(test)]
753mod tests {
754 use super::*;
755 fn assert_approx_eq(actual: f32, expected: f32, epsilon: f32) {
757 let diff = (actual - expected).abs();
758 assert!(
759 diff <= epsilon,
760 "expected {expected}, got {actual}, difference {diff} exceeds epsilon {epsilon}",
761 );
762 }
763 use cu29::clock::CuDuration;
764
765 #[test]
766 fn test_add_transform() {
767 let buffer = TransformBuffer::<f32>::new();
768
769 let transform = StampedTransform {
770 transform: Transform3D::default(),
771 stamp: CuDuration(1000),
772 parent_frame: FrameIdString::from("world").unwrap(),
773 child_frame: FrameIdString::from("robot").unwrap(),
774 };
775
776 buffer.add_transform(transform);
777
778 let latest = buffer.get_latest_transform();
779 assert!(latest.is_some());
780 }
781
782 #[test]
783 fn test_time_ordering() {
784 let buffer = TransformBuffer::<f32>::new();
785
786 let transform1 = StampedTransform {
787 transform: Transform3D::default(),
788 stamp: CuDuration(2000),
789 parent_frame: FrameIdString::from("world").unwrap(),
790 child_frame: FrameIdString::from("robot").unwrap(),
791 };
792
793 let transform2 = StampedTransform {
794 transform: Transform3D::default(),
795 stamp: CuDuration(1000),
796 parent_frame: FrameIdString::from("world").unwrap(),
797 child_frame: FrameIdString::from("robot").unwrap(),
798 };
799
800 let transform3 = StampedTransform {
801 transform: Transform3D::default(),
802 stamp: CuDuration(3000),
803 parent_frame: FrameIdString::from("world").unwrap(),
804 child_frame: FrameIdString::from("robot").unwrap(),
805 };
806
807 buffer.add_transform(transform1);
808 buffer.add_transform(transform2);
809 buffer.add_transform(transform3);
810
811 let range = buffer.get_time_range().unwrap();
812 assert_eq!(range.start.as_nanos(), 1000);
813 assert_eq!(range.end.as_nanos(), 3000);
814
815 let transforms = buffer.get_transforms_in_range(CuDuration(1500), CuDuration(2500));
816 assert_eq!(transforms.len(), 1);
817 assert_eq!(transforms[0].stamp.as_nanos(), 2000);
818 }
819
820 #[test]
821 fn test_capacity_limit() {
822 let buffer = TransformBuffer::<f32>::with_capacity(2);
823
824 let transform1 = StampedTransform {
825 transform: Transform3D::default(),
826 stamp: CuDuration(1000),
827 parent_frame: FrameIdString::from("world").unwrap(),
828 child_frame: FrameIdString::from("robot").unwrap(),
829 };
830
831 let transform2 = StampedTransform {
832 transform: Transform3D::default(),
833 stamp: CuDuration(2000),
834 parent_frame: FrameIdString::from("world").unwrap(),
835 child_frame: FrameIdString::from("robot").unwrap(),
836 };
837
838 let transform3 = StampedTransform {
839 transform: Transform3D::default(),
840 stamp: CuDuration(3000),
841 parent_frame: FrameIdString::from("world").unwrap(),
842 child_frame: FrameIdString::from("robot").unwrap(),
843 };
844
845 buffer.add_transform(transform1);
846 buffer.add_transform(transform2);
847 buffer.add_transform(transform3);
848
849 let range = buffer.get_time_range().unwrap();
850 assert_eq!(range.start.as_nanos(), 2000);
851 assert_eq!(range.end.as_nanos(), 3000);
852 }
853
854 #[test]
855 fn test_get_closest_transform() {
856 let buffer = TransformBuffer::<f32>::new();
857
858 let transform1 = StampedTransform {
859 transform: Transform3D::default(),
860 stamp: CuDuration(1000),
861 parent_frame: FrameIdString::from("world").unwrap(),
862 child_frame: FrameIdString::from("robot").unwrap(),
863 };
864
865 let transform2 = StampedTransform {
866 transform: Transform3D::default(),
867 stamp: CuDuration(3000),
868 parent_frame: FrameIdString::from("world").unwrap(),
869 child_frame: FrameIdString::from("robot").unwrap(),
870 };
871
872 buffer.add_transform(transform1);
873 buffer.add_transform(transform2);
874
875 let closest = buffer.get_closest_transform(CuDuration(1000));
876 assert!(closest.is_some());
877 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
878
879 let closest = buffer.get_closest_transform(CuDuration(500));
880 assert!(closest.is_some());
881 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
882
883 let closest = buffer.get_closest_transform(CuDuration(4000));
884 assert!(closest.is_some());
885 assert_eq!(closest.unwrap().stamp.as_nanos(), 3000);
886
887 let closest = buffer.get_closest_transform(CuDuration(1600));
888 assert!(closest.is_some());
889 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
890
891 let closest = buffer.get_closest_transform(CuDuration(2600));
892 assert!(closest.is_some());
893 assert_eq!(closest.unwrap().stamp.as_nanos(), 3000);
894 }
895
896 #[test]
897 fn test_transform_store() {
898 let store = TransformStore::<f32>::new();
899
900 let transform = StampedTransform {
901 transform: Transform3D::default(),
902 stamp: CuDuration(1000),
903 parent_frame: FrameIdString::from("world").unwrap(),
904 child_frame: FrameIdString::from("robot").unwrap(),
905 };
906
907 store.add_transform(transform.clone());
908
909 let buffer = store.get_buffer("world", "robot").unwrap();
910 let closest = buffer.get_closest_transform(CuDuration(1000));
911 assert!(closest.is_some());
912
913 let missing = store.get_buffer("world", "camera");
915 assert!(missing.is_none());
916
917 let _ = store.get_or_create_buffer("world", "camera");
919 assert!(store.get_buffer("world", "camera").is_some());
920 }
921
922 #[test]
923 fn test_compute_velocity() {
924 let mut transform1 = StampedTransform {
926 transform: Transform3D::<f32>::default(),
927 stamp: CuDuration(1_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
929 child_frame: FrameIdString::from("robot").unwrap(),
930 };
931
932 let mut transform2 = StampedTransform {
933 transform: Transform3D::<f32>::default(),
934 stamp: CuDuration(2_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
936 child_frame: FrameIdString::from("robot").unwrap(),
937 };
938
939 transform1.transform = Transform3D::from_matrix([
943 [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], ]);
948
949 transform2.transform = Transform3D::from_matrix([
950 [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], ]);
955
956 let velocity = transform2.compute_velocity(&transform1);
958 assert!(velocity.is_some());
959
960 let vel = velocity.unwrap();
961
962 assert_approx_eq(vel.linear[0], 1.0, 1e-5);
964 assert_approx_eq(vel.linear[1], 2.0, 1e-5);
965 assert_approx_eq(vel.linear[2], 0.0, 1e-5);
966
967 assert_approx_eq(vel.angular[0], 0.0, 1e-5);
970 assert_approx_eq(vel.angular[1], 0.0, 1e-5);
971 assert_approx_eq(vel.angular[2], 0.0, 1e-5);
972 }
973
974 #[test]
975 fn test_velocity_failure_cases() {
976 let transform1 = StampedTransform {
978 transform: Transform3D::<f32>::default(),
979 stamp: CuDuration(1000),
980 parent_frame: FrameIdString::from("world").unwrap(),
981 child_frame: FrameIdString::from("robot").unwrap(),
982 };
983
984 let transform2 = StampedTransform {
985 transform: Transform3D::<f32>::default(),
986 stamp: CuDuration(2000),
987 parent_frame: FrameIdString::from("different").unwrap(), child_frame: FrameIdString::from("robot").unwrap(),
989 };
990
991 let velocity = transform2.compute_velocity(&transform1);
992 assert!(velocity.is_none());
993
994 let transform1 = StampedTransform {
996 transform: Transform3D::<f32>::default(),
997 stamp: CuDuration(2000), parent_frame: FrameIdString::from("world").unwrap(),
999 child_frame: FrameIdString::from("robot").unwrap(),
1000 };
1001
1002 let transform2 = StampedTransform {
1003 transform: Transform3D::<f32>::default(),
1004 stamp: CuDuration(1000), parent_frame: FrameIdString::from("world").unwrap(),
1006 child_frame: FrameIdString::from("robot").unwrap(),
1007 };
1008
1009 let velocity = transform2.compute_velocity(&transform1);
1010 assert!(velocity.is_none());
1011 }
1012
1013 #[test]
1014 fn test_get_transforms_around() {
1015 let buffer = TransformBuffer::<f32>::new();
1016
1017 let transform1 = StampedTransform {
1019 transform: Transform3D::default(),
1020 stamp: CuDuration(1000),
1021 parent_frame: FrameIdString::from("world").unwrap(),
1022 child_frame: FrameIdString::from("robot").unwrap(),
1023 };
1024
1025 let transform2 = StampedTransform {
1026 transform: Transform3D::default(),
1027 stamp: CuDuration(2000),
1028 parent_frame: FrameIdString::from("world").unwrap(),
1029 child_frame: FrameIdString::from("robot").unwrap(),
1030 };
1031
1032 let transform3 = StampedTransform {
1033 transform: Transform3D::default(),
1034 stamp: CuDuration(3000),
1035 parent_frame: FrameIdString::from("world").unwrap(),
1036 child_frame: FrameIdString::from("robot").unwrap(),
1037 };
1038
1039 buffer.add_transform(transform1);
1040 buffer.add_transform(transform2);
1041 buffer.add_transform(transform3);
1042
1043 let transforms = buffer.get_transforms_around(CuDuration(2500));
1045 assert!(transforms.is_some());
1046 let (t1, t2) = transforms.unwrap();
1047 assert_eq!(t1.stamp.as_nanos(), 2000); assert_eq!(t2.stamp.as_nanos(), 3000); let transforms = buffer.get_transforms_around(CuDuration(500));
1052 assert!(transforms.is_some());
1053 let (t1, t2) = transforms.unwrap();
1054 assert_eq!(t1.stamp.as_nanos(), 1000); assert_eq!(t2.stamp.as_nanos(), 2000); let transforms = buffer.get_transforms_around(CuDuration(4000));
1059 assert!(transforms.is_some());
1060 let (t1, t2) = transforms.unwrap();
1061 assert_eq!(t1.stamp.as_nanos(), 2000); assert_eq!(t2.stamp.as_nanos(), 3000); }
1064
1065 #[test]
1066 fn test_compute_velocity_from_buffer() {
1067 let buffer = TransformBuffer::<f32>::new();
1068
1069 let mut transform1 = StampedTransform {
1071 transform: Transform3D::<f32>::default(),
1072 stamp: CuDuration(1_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1074 child_frame: FrameIdString::from("robot").unwrap(),
1075 };
1076
1077 let mut transform2 = StampedTransform {
1078 transform: Transform3D::<f32>::default(),
1079 stamp: CuDuration(2_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1081 child_frame: FrameIdString::from("robot").unwrap(),
1082 };
1083
1084 transform1.transform = Transform3D::from_matrix([
1087 [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], ]);
1092
1093 transform2.transform = Transform3D::from_matrix([
1094 [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], ]);
1099
1100 buffer.add_transform(transform1);
1101 buffer.add_transform(transform2);
1102
1103 let velocity = buffer.compute_velocity_at_time(CuDuration(1_500_000_000)); assert!(velocity.is_some());
1106
1107 let vel = velocity.unwrap();
1108 assert_approx_eq(vel.linear[0], 2.0, 1e-5);
1110 assert_approx_eq(vel.linear[1], 0.0, 1e-5);
1111 assert_approx_eq(vel.linear[2], 0.0, 1e-5);
1112 }
1113
1114 #[test]
1115 fn test_compute_angular_velocity() {
1116 let buffer = TransformBuffer::<f32>::new();
1117
1118 let mut transform1 = StampedTransform {
1120 transform: Transform3D::<f32>::default(),
1121 stamp: CuDuration(1_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1123 child_frame: FrameIdString::from("robot").unwrap(),
1124 };
1125
1126 transform1.transform = Transform3D::from_matrix([
1128 [1.0, 0.0, 0.0, 0.0],
1129 [0.0, 1.0, 0.0, 0.0],
1130 [0.0, 0.0, 1.0, 0.0],
1131 [0.0, 0.0, 0.0, 1.0],
1132 ]);
1133
1134 let mut transform2 = StampedTransform {
1135 transform: Transform3D::<f32>::default(),
1136 stamp: CuDuration(2_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1138 child_frame: FrameIdString::from("robot").unwrap(),
1139 };
1140
1141 transform2.transform = Transform3D::from_matrix([
1145 [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], ]);
1150
1151 buffer.add_transform(transform1);
1152 buffer.add_transform(transform2);
1153
1154 let velocity = buffer.compute_velocity_at_time(CuDuration(1_500_000_000)); assert!(velocity.is_some());
1157
1158 let vel = velocity.unwrap();
1159
1160 assert_approx_eq(vel.angular[0], 0.0, 1e-5);
1163 assert_approx_eq(vel.angular[1], 0.0, 1e-5);
1164 assert_approx_eq(vel.angular[2], 1.0, 1e-5); }
1166
1167 #[test]
1168 fn test_const_transform_buffer_basic() {
1169 let mut buffer = ConstTransformBuffer::<f32, 10>::new();
1170
1171 let transform = StampedTransform {
1172 transform: Transform3D::default(),
1173 stamp: CuDuration(1000),
1174 parent_frame: FrameIdString::from("world").unwrap(),
1175 child_frame: FrameIdString::from("robot").unwrap(),
1176 };
1177
1178 buffer.add_transform(transform);
1179
1180 let latest = buffer.get_latest_transform();
1181 assert!(latest.is_some());
1182 assert_eq!(latest.unwrap().stamp.as_nanos(), 1000);
1183 }
1184
1185 #[test]
1186 fn test_const_transform_buffer_ordering() {
1187 let mut buffer = ConstTransformBuffer::<f32, 10>::new();
1188
1189 let transform1 = StampedTransform {
1190 transform: Transform3D::default(),
1191 stamp: CuDuration(2000),
1192 parent_frame: FrameIdString::from("world").unwrap(),
1193 child_frame: FrameIdString::from("robot").unwrap(),
1194 };
1195
1196 let transform2 = StampedTransform {
1197 transform: Transform3D::default(),
1198 stamp: CuDuration(1000),
1199 parent_frame: FrameIdString::from("world").unwrap(),
1200 child_frame: FrameIdString::from("robot").unwrap(),
1201 };
1202
1203 let transform3 = StampedTransform {
1204 transform: Transform3D::default(),
1205 stamp: CuDuration(3000),
1206 parent_frame: FrameIdString::from("world").unwrap(),
1207 child_frame: FrameIdString::from("robot").unwrap(),
1208 };
1209
1210 buffer.add_transform(transform1);
1211 buffer.add_transform(transform2);
1212 buffer.add_transform(transform3);
1213
1214 let range = buffer.get_time_range().unwrap();
1215 assert_eq!(range.start.as_nanos(), 1000);
1216 assert_eq!(range.end.as_nanos(), 3000);
1217
1218 let transforms = buffer.get_transforms_in_range(CuDuration(1500), CuDuration(2500));
1219 assert_eq!(transforms.len(), 1);
1220 assert_eq!(transforms[0].stamp.as_nanos(), 2000);
1221 }
1222
1223 #[test]
1224 fn test_const_transform_buffer_capacity() {
1225 let mut buffer = ConstTransformBuffer::<f32, 2>::new();
1226
1227 let transform1 = StampedTransform {
1228 transform: Transform3D::default(),
1229 stamp: CuDuration(1000),
1230 parent_frame: FrameIdString::from("world").unwrap(),
1231 child_frame: FrameIdString::from("robot").unwrap(),
1232 };
1233
1234 let transform2 = StampedTransform {
1235 transform: Transform3D::default(),
1236 stamp: CuDuration(2000),
1237 parent_frame: FrameIdString::from("world").unwrap(),
1238 child_frame: FrameIdString::from("robot").unwrap(),
1239 };
1240
1241 let transform3 = StampedTransform {
1242 transform: Transform3D::default(),
1243 stamp: CuDuration(3000),
1244 parent_frame: FrameIdString::from("world").unwrap(),
1245 child_frame: FrameIdString::from("robot").unwrap(),
1246 };
1247
1248 buffer.add_transform(transform1);
1249 buffer.add_transform(transform2);
1250 buffer.add_transform(transform3);
1251
1252 let latest = buffer.get_latest_transform();
1254 assert!(latest.is_some());
1255
1256 let closest = buffer.get_closest_transform(CuDuration(2500));
1258 assert!(closest.is_some());
1259 }
1260
1261 #[test]
1262 fn test_const_transform_buffer_closest() {
1263 let mut buffer = ConstTransformBuffer::<f32, 10>::new();
1264
1265 let transform1 = StampedTransform {
1266 transform: Transform3D::default(),
1267 stamp: CuDuration(1000),
1268 parent_frame: FrameIdString::from("world").unwrap(),
1269 child_frame: FrameIdString::from("robot").unwrap(),
1270 };
1271
1272 let transform2 = StampedTransform {
1273 transform: Transform3D::default(),
1274 stamp: CuDuration(3000),
1275 parent_frame: FrameIdString::from("world").unwrap(),
1276 child_frame: FrameIdString::from("robot").unwrap(),
1277 };
1278
1279 buffer.add_transform(transform1);
1280 buffer.add_transform(transform2);
1281
1282 let closest = buffer.get_closest_transform(CuDuration(1000));
1283 assert!(closest.is_some());
1284 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
1285
1286 let closest = buffer.get_closest_transform(CuDuration(500));
1287 assert!(closest.is_some());
1288 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000);
1289
1290 let closest = buffer.get_closest_transform(CuDuration(4000));
1291 assert!(closest.is_some());
1292 assert_eq!(closest.unwrap().stamp.as_nanos(), 3000);
1293
1294 let closest = buffer.get_closest_transform(CuDuration(1900));
1295 assert!(closest.is_some());
1296 assert_eq!(closest.unwrap().stamp.as_nanos(), 1000); let closest = buffer.get_closest_transform(CuDuration(2100));
1299 assert!(closest.is_some());
1300 assert_eq!(closest.unwrap().stamp.as_nanos(), 3000); }
1302
1303 #[test]
1304 fn test_const_transform_buffer_sync_basic() {
1305 let buffer = ConstTransformBufferSync::<f32, 10>::new();
1306
1307 let transform = StampedTransform {
1308 transform: Transform3D::default(),
1309 stamp: CuDuration(1000),
1310 parent_frame: FrameIdString::from("world").unwrap(),
1311 child_frame: FrameIdString::from("robot").unwrap(),
1312 };
1313
1314 buffer.add_transform(transform);
1315
1316 let latest = buffer.get_latest_transform();
1317 assert!(latest.is_some());
1318 assert_eq!(latest.unwrap().stamp.as_nanos(), 1000);
1319 }
1320
1321 #[test]
1322 fn test_const_transform_buffer_sync_velocity() {
1323 let buffer = ConstTransformBufferSync::<f32, 10>::new();
1324
1325 let mut transform1 = StampedTransform {
1327 transform: Transform3D::<f32>::default(),
1328 stamp: CuDuration(1_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1330 child_frame: FrameIdString::from("robot").unwrap(),
1331 };
1332
1333 let mut transform2 = StampedTransform {
1334 transform: Transform3D::<f32>::default(),
1335 stamp: CuDuration(2_000_000_000), parent_frame: FrameIdString::from("world").unwrap(),
1337 child_frame: FrameIdString::from("robot").unwrap(),
1338 };
1339
1340 transform1.transform = Transform3D::from_matrix([
1343 [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], ]);
1348
1349 transform2.transform = Transform3D::from_matrix([
1350 [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], ]);
1355
1356 buffer.add_transform(transform1);
1357 buffer.add_transform(transform2);
1358
1359 let velocity = buffer.compute_velocity_at_time(CuDuration(1_500_000_000)); assert!(velocity.is_some());
1362
1363 let vel = velocity.unwrap();
1364 assert_approx_eq(vel.linear[0], 3.0, 1e-5);
1366 assert_approx_eq(vel.linear[1], 0.0, 1e-5);
1367 assert_approx_eq(vel.linear[2], 0.0, 1e-5);
1368 }
1369
1370 #[test]
1371 fn test_const_transform_store() {
1372 let store = ConstTransformStore::<f32, 10>::new();
1373
1374 let transform = StampedTransform {
1375 transform: Transform3D::default(),
1376 stamp: CuDuration(1000),
1377 parent_frame: FrameIdString::from("world").unwrap(),
1378 child_frame: FrameIdString::from("robot").unwrap(),
1379 };
1380
1381 store.add_transform(transform.clone());
1382
1383 let buffer = store.get_buffer("world", "robot").unwrap();
1384 let closest = buffer.get_closest_transform(CuDuration(1000));
1385 assert!(closest.is_some());
1386
1387 let missing = store.get_buffer("world", "camera");
1389 assert!(missing.is_none());
1390
1391 let _ = store.get_or_create_buffer("world", "camera");
1393 assert!(store.get_buffer("world", "camera").is_some());
1394 }
1395}