1use crate::position::{HeadTracker, MotionSnapshot};
8use crate::types::Position3D;
9use crate::{Error, Result};
10use candle_core::{Device, Tensor};
11use candle_nn::{linear, Linear, Module, VarBuilder, VarMap};
12use scirs2_core::ndarray::Array1;
13use serde::{Deserialize, Serialize};
14use std::collections::{HashMap, VecDeque};
15use std::time::{Duration, Instant};
16
17pub struct AdvancedPredictiveTracker {
19 base_tracker: HeadTracker,
21 prediction_models: PredictionModels,
23 pattern_analyzer: MotionPatternAnalyzer,
25 adaptive_controller: AdaptivePredictionController,
27 config: PredictiveTrackingConfig,
29 metrics: PredictionMetrics,
31}
32
33pub struct PredictionModels {
35 linear_model: LinearMotionModel,
37 polynomial_model: PolynomialMotionModel,
39 neural_model: Option<NeuralPredictionModel>,
41 kalman_filter: KalmanMotionFilter,
43 active_model: PredictionModelType,
45}
46
47pub struct MotionPatternAnalyzer {
49 recent_patterns: VecDeque<MotionPattern>,
51 pattern_library: HashMap<String, MotionPatternTemplate>,
53 recognition_state: PatternRecognitionState,
55}
56
57pub struct AdaptivePredictionController {
59 accuracy_history: VecDeque<PredictionAccuracy>,
61 adaptation_state: AdaptationState,
63 learning_rate: f32,
65 min_confidence: f32,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct PredictiveTrackingConfig {
72 pub max_prediction_time: Duration,
74 pub min_samples_for_prediction: usize,
76 pub model_selection_strategy: ModelSelectionStrategy,
78 pub enable_adaptive_learning: bool,
80 pub enable_neural_prediction: bool,
82 pub pattern_recognition: PatternRecognitionConfig,
84 pub performance_optimization: PerformanceOptimizationConfig,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
90pub enum ModelSelectionStrategy {
91 AlwaysLinear,
93 AlwaysPolynomial,
95 AlwaysNeural,
97 Adaptive,
99 Ensemble,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
105pub enum PredictionModelType {
106 Linear,
108 Polynomial,
110 Neural,
112 Kalman,
114 Ensemble,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct MotionPattern {
121 pub pattern_type: MotionPatternType,
123 pub parameters: MotionPatternParameters,
125 pub confidence: f32,
127 pub time_window: Duration,
129 pub sample_count: usize,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
135pub enum MotionPatternType {
136 Static,
138 Linear,
140 Circular,
142 Oscillatory,
144 Jerky,
146 Curved,
148 Complex,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct MotionPatternParameters {
155 pub primary_direction: Position3D,
157 pub frequency: f32,
159 pub amplitude: f32,
161 pub acceleration_profile: AccelerationProfile,
163 pub periodicity: Option<f32>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct AccelerationProfile {
170 pub average_magnitude: f32,
172 pub peak_magnitude: f32,
174 pub jerk: f32,
176 pub smoothness: f32,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct MotionPatternTemplate {
183 pub name: String,
185 pub pattern_type: MotionPatternType,
187 pub expected_parameters: MotionPatternParameters,
189 pub tolerance: f32,
191 pub preferred_model: PredictionModelType,
193}
194
195#[derive(Debug, Clone)]
197pub struct LinearMotionModel {
198 last_velocity: Position3D,
200 smoothing_factor: f32,
202}
203
204#[derive(Debug, Clone)]
206pub struct PolynomialMotionModel {
207 degree: usize,
209 min_samples: usize,
211}
212
213pub struct NeuralPredictionModel {
215 network: PredictionNetwork,
217 training_data: VecDeque<TrainingExample>,
219 config: NeuralModelConfig,
221 device: Device,
223 var_map: VarMap,
225}
226
227#[derive(Debug, Clone)]
229pub struct KalmanMotionFilter {
230 state: [f32; 9], covariance: [f32; 81], process_noise: f32,
236 measurement_noise: f32,
238 dt: f32,
240}
241
242pub struct PredictionNetwork {
244 input_layer: Linear,
246 hidden_layers: Vec<Linear>,
248 output_layer: Linear,
250}
251
252#[derive(Debug, Clone, Default, Serialize, Deserialize)]
254pub struct PredictionMetrics {
255 pub total_predictions: usize,
257 pub successful_predictions: usize,
259 pub average_error: f32,
261 pub peak_error: f32,
263 pub average_latency: f32,
265 pub model_accuracies: HashMap<PredictionModelType, f32>,
267 pub pattern_recognition_accuracy: f32,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct PatternRecognitionConfig {
274 pub enable_recognition: bool,
276 pub min_pattern_duration: Duration,
278 pub matching_threshold: f32,
280 pub analysis_frequency: f32,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct PerformanceOptimizationConfig {
287 pub target_latency: f32,
289 pub max_computation_time: Duration,
291 pub enable_simd: bool,
293 pub enable_gpu: bool,
295}
296
297#[derive(Debug, Clone, Default)]
299pub struct PatternRecognitionState {
300 pub current_pattern: Option<MotionPattern>,
302 pub confidence: f32,
304 pub time_since_detection: Duration,
306 pub stability_score: f32,
308}
309
310#[derive(Debug, Clone, Default)]
312pub struct AdaptationState {
313 pub phase: AdaptationPhase,
315 pub adaptation_rate: f32,
317 pub model_weights: HashMap<PredictionModelType, f32>,
319 pub performance_trend: f32,
321}
322
323#[derive(Debug, Clone)]
325pub struct PredictionAccuracy {
326 pub predicted_position: Position3D,
328 pub actual_position: Position3D,
330 pub error: f32,
332 pub timestamp: Instant,
334 pub model_used: PredictionModelType,
336}
337
338#[derive(Debug, Clone)]
340pub struct TrainingExample {
341 pub input_features: Vec<f32>,
343 pub target_position: Position3D,
345 pub time_delta: f32,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct NeuralModelConfig {
352 pub input_dim: usize,
354 pub hidden_dims: Vec<usize>,
356 pub output_dim: usize,
358 pub learning_rate: f64,
360 pub batch_size: usize,
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
366pub enum AdaptationPhase {
367 #[default]
369 WarmUp,
370 Adapting,
372 Stable,
374 ReAdapting,
376}
377
378impl Default for PredictiveTrackingConfig {
379 fn default() -> Self {
380 Self {
381 max_prediction_time: Duration::from_millis(100),
382 min_samples_for_prediction: 5,
383 model_selection_strategy: ModelSelectionStrategy::Adaptive,
384 enable_adaptive_learning: true,
385 enable_neural_prediction: false, pattern_recognition: PatternRecognitionConfig {
387 enable_recognition: true,
388 min_pattern_duration: Duration::from_millis(200),
389 matching_threshold: 0.8,
390 analysis_frequency: 10.0, },
392 performance_optimization: PerformanceOptimizationConfig {
393 target_latency: 1000.0, max_computation_time: Duration::from_micros(500),
395 enable_simd: true,
396 enable_gpu: false, },
398 }
399 }
400}
401
402impl AdvancedPredictiveTracker {
403 pub fn new(config: PredictiveTrackingConfig) -> Result<Self> {
405 let base_tracker = HeadTracker::new();
406 let prediction_models = PredictionModels::new(&config)?;
407 let pattern_analyzer = MotionPatternAnalyzer::new(&config.pattern_recognition);
408 let adaptive_controller = AdaptivePredictionController::new();
409
410 Ok(Self {
411 base_tracker,
412 prediction_models,
413 pattern_analyzer,
414 adaptive_controller,
415 config,
416 metrics: PredictionMetrics::default(),
417 })
418 }
419
420 pub fn update_position(&mut self, position: Position3D, timestamp: Instant) -> Result<()> {
422 self.base_tracker.update_position(position, timestamp);
424
425 self.analyze_motion_patterns()?;
427
428 self.update_adaptive_controller()?;
430
431 if self.config.enable_neural_prediction {
433 self.update_neural_model()?;
434 }
435
436 Ok(())
437 }
438
439 pub fn predict_position(&self, lookahead_time: Duration) -> Result<PredictedPosition> {
441 let start_time = Instant::now();
442
443 let selected_model = self.select_prediction_model()?;
445
446 let prediction = match selected_model {
448 PredictionModelType::Linear => self
449 .prediction_models
450 .linear_model
451 .predict(self.base_tracker.position_history(), lookahead_time)?,
452 PredictionModelType::Polynomial => self
453 .prediction_models
454 .polynomial_model
455 .predict(self.base_tracker.position_history(), lookahead_time)?,
456 PredictionModelType::Neural => {
457 if let Some(ref neural_model) = self.prediction_models.neural_model {
458 neural_model.predict(self.base_tracker.position_history(), lookahead_time)?
459 } else {
460 self.prediction_models
462 .linear_model
463 .predict(self.base_tracker.position_history(), lookahead_time)?
464 }
465 }
466 PredictionModelType::Kalman => self
467 .prediction_models
468 .kalman_filter
469 .predict(lookahead_time)?,
470 PredictionModelType::Ensemble => self.ensemble_prediction(lookahead_time)?,
471 };
472
473 let computation_time = start_time.elapsed();
474
475 Ok(PredictedPosition {
476 position: prediction.position,
477 confidence: prediction.confidence,
478 model_used: selected_model,
479 computation_time,
480 pattern_type: self
481 .pattern_analyzer
482 .recognition_state
483 .current_pattern
484 .as_ref()
485 .map(|p| p.pattern_type),
486 })
487 }
488
489 pub fn update_accuracy(&mut self, predicted: &PredictedPosition, actual: Position3D) {
491 let error = predicted.position.distance_to(&actual);
492
493 let accuracy = PredictionAccuracy {
494 predicted_position: predicted.position,
495 actual_position: actual,
496 error,
497 timestamp: Instant::now(),
498 model_used: predicted.model_used,
499 };
500
501 self.adaptive_controller
502 .accuracy_history
503 .push_back(accuracy);
504
505 self.metrics.total_predictions += 1;
507 if error < 0.05 {
508 self.metrics.successful_predictions += 1;
510 }
511
512 let total = self.metrics.total_predictions as f32;
514 self.metrics.average_error = (self.metrics.average_error * (total - 1.0) + error) / total;
515 self.metrics.peak_error = self.metrics.peak_error.max(error);
516
517 let model_accuracy = self
519 .metrics
520 .model_accuracies
521 .entry(predicted.model_used)
522 .or_insert(0.0);
523 *model_accuracy =
524 (*model_accuracy * (total - 1.0) + if error < 0.05 { 1.0 } else { 0.0 }) / total;
525
526 if self.adaptive_controller.accuracy_history.len() > 1000 {
528 self.adaptive_controller.accuracy_history.pop_front();
529 }
530 }
531
532 pub fn metrics(&self) -> &PredictionMetrics {
534 &self.metrics
535 }
536
537 pub fn current_pattern(&self) -> Option<&MotionPattern> {
539 self.pattern_analyzer
540 .recognition_state
541 .current_pattern
542 .as_ref()
543 }
544
545 pub fn configure(&mut self, config: PredictiveTrackingConfig) {
547 self.config = config;
548 }
550
551 fn analyze_motion_patterns(&mut self) -> Result<()> {
554 let position_history = self.base_tracker.position_history();
555
556 if position_history.len() < self.config.min_samples_for_prediction {
557 return Ok(());
558 }
559
560 let pattern = self.pattern_analyzer.analyze_motion(position_history)?;
562
563 if let Some(detected_pattern) = pattern {
564 self.pattern_analyzer.recognition_state.current_pattern = Some(detected_pattern);
566 self.pattern_analyzer.recognition_state.confidence = self
567 .pattern_analyzer
568 .recognition_state
569 .current_pattern
570 .as_ref()
571 .map(|p| p.confidence)
572 .unwrap_or(0.0);
573 }
574
575 Ok(())
576 }
577
578 fn update_adaptive_controller(&mut self) -> Result<()> {
579 if !self.config.enable_adaptive_learning {
580 return Ok(());
581 }
582
583 if let Some(recent_accuracy) = self.adaptive_controller.accuracy_history.back() {
585 let current_weight = self
587 .adaptive_controller
588 .adaptation_state
589 .model_weights
590 .entry(recent_accuracy.model_used)
591 .or_insert(1.0);
592
593 let accuracy_factor = if recent_accuracy.error < 0.02 {
595 1.1
596 } else {
597 0.9
598 };
599 *current_weight = (*current_weight * accuracy_factor).clamp(0.1, 2.0);
600 }
601
602 Ok(())
603 }
604
605 fn update_neural_model(&mut self) -> Result<()> {
606 if self.prediction_models.neural_model.is_some() {
607 let position_history = self.base_tracker.position_history();
609
610 if position_history.len() >= 10 {
611 let training_example = self.create_training_example(position_history)?;
612
613 if let Some(ref mut neural_model) = self.prediction_models.neural_model {
614 neural_model.training_data.push_back(training_example);
615
616 if neural_model.training_data.len() > 1000 {
618 neural_model.training_data.pop_front();
619 }
620
621 if neural_model.training_data.len() % 100 == 0 {
623 neural_model.retrain()?;
624 }
625 }
626 }
627 }
628
629 Ok(())
630 }
631
632 fn select_prediction_model(&self) -> Result<PredictionModelType> {
633 match self.config.model_selection_strategy {
634 ModelSelectionStrategy::AlwaysLinear => Ok(PredictionModelType::Linear),
635 ModelSelectionStrategy::AlwaysPolynomial => Ok(PredictionModelType::Polynomial),
636 ModelSelectionStrategy::AlwaysNeural => {
637 if self.prediction_models.neural_model.is_some() {
638 Ok(PredictionModelType::Neural)
639 } else {
640 Ok(PredictionModelType::Linear) }
642 }
643 ModelSelectionStrategy::Adaptive => {
644 if let Some(ref pattern) = self.pattern_analyzer.recognition_state.current_pattern {
646 match pattern.pattern_type {
647 MotionPatternType::Static => Ok(PredictionModelType::Linear),
648 MotionPatternType::Linear => Ok(PredictionModelType::Linear),
649 MotionPatternType::Circular | MotionPatternType::Curved => {
650 Ok(PredictionModelType::Polynomial)
651 }
652 MotionPatternType::Oscillatory => Ok(PredictionModelType::Kalman),
653 MotionPatternType::Complex => {
654 if self.prediction_models.neural_model.is_some() {
655 Ok(PredictionModelType::Neural)
656 } else {
657 Ok(PredictionModelType::Polynomial)
658 }
659 }
660 _ => Ok(PredictionModelType::Linear),
661 }
662 } else {
663 Ok(PredictionModelType::Linear) }
665 }
666 ModelSelectionStrategy::Ensemble => Ok(PredictionModelType::Ensemble),
667 }
668 }
669
670 fn ensemble_prediction(&self, lookahead_time: Duration) -> Result<PredictionResult> {
671 let position_history = self.base_tracker.position_history();
672
673 let linear_pred = self
675 .prediction_models
676 .linear_model
677 .predict(position_history, lookahead_time)?;
678 let poly_pred = self
679 .prediction_models
680 .polynomial_model
681 .predict(position_history, lookahead_time)?;
682 let kalman_pred = self
683 .prediction_models
684 .kalman_filter
685 .predict(lookahead_time)?;
686
687 let weights = &self.adaptive_controller.adaptation_state.model_weights;
689 let linear_weight = weights.get(&PredictionModelType::Linear).unwrap_or(&1.0);
690 let poly_weight = weights
691 .get(&PredictionModelType::Polynomial)
692 .unwrap_or(&1.0);
693 let kalman_weight = weights.get(&PredictionModelType::Kalman).unwrap_or(&1.0);
694
695 let total_weight = linear_weight + poly_weight + kalman_weight;
696
697 let ensemble_position = Position3D::new(
699 (linear_pred.position.x * linear_weight
700 + poly_pred.position.x * poly_weight
701 + kalman_pred.position.x * kalman_weight)
702 / total_weight,
703 (linear_pred.position.y * linear_weight
704 + poly_pred.position.y * poly_weight
705 + kalman_pred.position.y * kalman_weight)
706 / total_weight,
707 (linear_pred.position.z * linear_weight
708 + poly_pred.position.z * poly_weight
709 + kalman_pred.position.z * kalman_weight)
710 / total_weight,
711 );
712
713 let ensemble_confidence = (linear_pred.confidence * linear_weight
715 + poly_pred.confidence * poly_weight
716 + kalman_pred.confidence * kalman_weight)
717 / total_weight;
718
719 Ok(PredictionResult {
720 position: ensemble_position,
721 confidence: ensemble_confidence,
722 })
723 }
724
725 fn create_training_example(
726 &self,
727 position_history: &[crate::position::PositionSnapshot],
728 ) -> Result<TrainingExample> {
729 let mut features = Vec::new();
731
732 let start_idx = position_history.len().saturating_sub(8);
734 for snapshot in &position_history[start_idx..] {
735 features.push(snapshot.position.x);
736 features.push(snapshot.position.y);
737 features.push(snapshot.position.z);
738 features.push(snapshot.velocity.x);
739 features.push(snapshot.velocity.y);
740 features.push(snapshot.velocity.z);
741 }
742
743 while features.len() < 48 {
745 features.push(0.0);
747 }
748
749 let target_position = if let Some(latest) = position_history.last() {
751 latest.position
752 } else {
753 Position3D::default()
754 };
755
756 Ok(TrainingExample {
757 input_features: features,
758 target_position,
759 time_delta: 0.1, })
761 }
762}
763
764#[derive(Debug, Clone)]
766pub struct PredictionResult {
767 pub position: Position3D,
769 pub confidence: f32,
771}
772
773#[derive(Debug, Clone)]
775pub struct PredictedPosition {
776 pub position: Position3D,
778 pub confidence: f32,
780 pub model_used: PredictionModelType,
782 pub computation_time: Duration,
784 pub pattern_type: Option<MotionPatternType>,
786}
787
788impl PredictionModels {
790 fn new(_config: &PredictiveTrackingConfig) -> Result<Self> {
791 Ok(Self {
792 linear_model: LinearMotionModel::new(),
793 polynomial_model: PolynomialMotionModel::new(),
794 neural_model: None, kalman_filter: KalmanMotionFilter::new(),
796 active_model: PredictionModelType::Linear,
797 })
798 }
799}
800
801impl LinearMotionModel {
802 fn new() -> Self {
803 Self {
804 last_velocity: Position3D::default(),
805 smoothing_factor: 0.3,
806 }
807 }
808
809 fn predict(
810 &self,
811 _history: &[crate::position::PositionSnapshot],
812 lookahead: Duration,
813 ) -> Result<PredictionResult> {
814 let dt = lookahead.as_secs_f32();
816 let predicted_pos = Position3D::new(
817 self.last_velocity.x * dt,
818 self.last_velocity.y * dt,
819 self.last_velocity.z * dt,
820 );
821
822 Ok(PredictionResult {
823 position: predicted_pos,
824 confidence: 0.8, })
826 }
827}
828
829impl PolynomialMotionModel {
830 fn new() -> Self {
831 Self {
832 degree: 3,
833 min_samples: 5,
834 }
835 }
836
837 fn predict(
838 &self,
839 history: &[crate::position::PositionSnapshot],
840 lookahead: Duration,
841 ) -> Result<PredictionResult> {
842 if history.len() < self.min_samples {
843 return Err(Error::processing(
844 "Insufficient data for polynomial prediction",
845 ));
846 }
847
848 let last_pos = history
850 .last()
851 .ok_or_else(|| Error::processing("history is empty"))?
852 .position;
853 let dt = lookahead.as_secs_f32();
854
855 let predicted_pos = Position3D::new(
857 last_pos.x + dt * 0.1,
858 last_pos.y + dt * 0.1,
859 last_pos.z + dt * 0.1,
860 );
861
862 Ok(PredictionResult {
863 position: predicted_pos,
864 confidence: 0.7,
865 })
866 }
867}
868
869impl KalmanMotionFilter {
870 fn new() -> Self {
871 Self {
872 state: [0.0; 9],
873 covariance: [0.0; 81],
874 process_noise: 0.01,
875 measurement_noise: 0.1,
876 dt: 0.01,
877 }
878 }
879
880 fn predict(&self, lookahead: Duration) -> Result<PredictionResult> {
881 let dt = lookahead.as_secs_f32();
882
883 let predicted_pos = Position3D::new(
885 self.state[0] + self.state[3] * dt,
886 self.state[1] + self.state[4] * dt,
887 self.state[2] + self.state[5] * dt,
888 );
889
890 Ok(PredictionResult {
891 position: predicted_pos,
892 confidence: 0.9, })
894 }
895}
896
897impl MotionPatternAnalyzer {
898 fn new(_config: &PatternRecognitionConfig) -> Self {
899 Self {
900 recent_patterns: VecDeque::new(),
901 pattern_library: HashMap::new(),
902 recognition_state: PatternRecognitionState::default(),
903 }
904 }
905
906 fn analyze_motion(
907 &mut self,
908 history: &[crate::position::PositionSnapshot],
909 ) -> Result<Option<MotionPattern>> {
910 if history.len() < 5 {
911 return Ok(None);
912 }
913
914 let mut total_velocity = 0.0;
916 let mut direction_changes = 0;
917
918 for window in history.windows(2) {
919 let vel_mag = window[1].velocity.magnitude();
920 total_velocity += vel_mag;
921
922 if window.len() >= 2 {
924 let dot_product = window[0].velocity.dot(&window[1].velocity);
925 if dot_product < 0.0 {
926 direction_changes += 1;
927 }
928 }
929 }
930
931 let avg_velocity = total_velocity / (history.len() - 1) as f32;
932
933 let pattern_type = if avg_velocity < 0.01 {
935 MotionPatternType::Static
936 } else if direction_changes == 0 {
937 MotionPatternType::Linear
938 } else if direction_changes > history.len() / 3 {
939 MotionPatternType::Oscillatory
940 } else {
941 MotionPatternType::Curved
942 };
943
944 let pattern = MotionPattern {
945 pattern_type,
946 parameters: MotionPatternParameters {
947 primary_direction: Position3D::new(1.0, 0.0, 0.0), frequency: 0.0,
949 amplitude: avg_velocity,
950 acceleration_profile: AccelerationProfile {
951 average_magnitude: 0.1,
952 peak_magnitude: 0.2,
953 jerk: 0.05,
954 smoothness: 0.8,
955 },
956 periodicity: None,
957 },
958 confidence: 0.7,
959 time_window: Duration::from_millis(500),
960 sample_count: history.len(),
961 };
962
963 Ok(Some(pattern))
964 }
965}
966
967impl AdaptivePredictionController {
968 fn new() -> Self {
969 Self {
970 accuracy_history: VecDeque::new(),
971 adaptation_state: AdaptationState::default(),
972 learning_rate: 0.01,
973 min_confidence: 0.5,
974 }
975 }
976}
977
978impl NeuralPredictionModel {
979 fn predict(
980 &self,
981 _history: &[crate::position::PositionSnapshot],
982 _lookahead: Duration,
983 ) -> Result<PredictionResult> {
984 Ok(PredictionResult {
986 position: Position3D::default(),
987 confidence: 0.6,
988 })
989 }
990
991 fn retrain(&mut self) -> Result<()> {
992 Ok(())
994 }
995}
996
997#[cfg(test)]
998mod tests {
999 use super::*;
1000
1001 #[test]
1002 fn test_predictive_tracker_creation() {
1003 let config = PredictiveTrackingConfig::default();
1004 let tracker = AdvancedPredictiveTracker::new(config);
1005 assert!(tracker.is_ok());
1006 }
1007
1008 #[test]
1009 fn test_linear_model_prediction() {
1010 let model = LinearMotionModel::new();
1011 let prediction = model.predict(&[], Duration::from_millis(100));
1012 assert!(prediction.is_ok());
1013 }
1014
1015 #[test]
1016 fn test_pattern_analysis() {
1017 let config = PatternRecognitionConfig {
1018 enable_recognition: true,
1019 min_pattern_duration: Duration::from_millis(100),
1020 matching_threshold: 0.8,
1021 analysis_frequency: 10.0,
1022 };
1023 let mut analyzer = MotionPatternAnalyzer::new(&config);
1024 let pattern = analyzer.analyze_motion(&[]);
1025 assert!(pattern.is_ok());
1026 }
1027
1028 #[test]
1029 fn test_model_selection_strategies() {
1030 let config = PredictiveTrackingConfig::default();
1031 let tracker = AdvancedPredictiveTracker::new(config).unwrap();
1032
1033 let linear_model = tracker.select_prediction_model();
1035 assert!(linear_model.is_ok());
1036 }
1037
1038 #[test]
1039 fn test_prediction_metrics() {
1040 let mut metrics = PredictionMetrics::default();
1041 metrics.total_predictions = 100;
1042 metrics.successful_predictions = 85;
1043
1044 let accuracy = metrics.successful_predictions as f32 / metrics.total_predictions as f32;
1045 assert_eq!(accuracy, 0.85);
1046 }
1047
1048 #[test]
1049 fn test_kalman_filter() {
1050 let filter = KalmanMotionFilter::new();
1051 let prediction = filter.predict(Duration::from_millis(50));
1052 assert!(prediction.is_ok());
1053 }
1054
1055 #[test]
1056 fn test_motion_pattern_types() {
1057 let pattern = MotionPattern {
1058 pattern_type: MotionPatternType::Oscillatory,
1059 parameters: MotionPatternParameters {
1060 primary_direction: Position3D::new(1.0, 0.0, 0.0),
1061 frequency: 2.0,
1062 amplitude: 0.1,
1063 acceleration_profile: AccelerationProfile {
1064 average_magnitude: 0.05,
1065 peak_magnitude: 0.1,
1066 jerk: 0.02,
1067 smoothness: 0.9,
1068 },
1069 periodicity: Some(0.5),
1070 },
1071 confidence: 0.8,
1072 time_window: Duration::from_millis(1000),
1073 sample_count: 20,
1074 };
1075
1076 assert_eq!(pattern.pattern_type, MotionPatternType::Oscillatory);
1077 assert_eq!(pattern.parameters.frequency, 2.0);
1078 }
1079}