Skip to main content

voirs_spatial/
power.rs

1//! Power Consumption Optimization for VoiRS Spatial Audio
2//!
3//! This module provides power management and optimization strategies for battery-powered devices,
4//! including mobile phones, VR headsets, and other portable spatial audio devices.
5
6use crate::config::SpatialConfig;
7use crate::mobile::{MobileConfig, PowerState, QualityPreset};
8use crate::types::Position3D;
9use crate::{Error, Result};
10use serde::{Deserialize, Serialize};
11use std::collections::VecDeque;
12use std::time::{Duration, Instant};
13
14/// Power management strategy
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16pub enum PowerStrategy {
17    /// Maximum performance, highest power consumption
18    Performance,
19    /// Balanced performance and power consumption
20    Balanced,
21    /// Prioritize battery life over performance
22    PowerSaver,
23    /// Minimum power consumption, basic functionality only
24    UltraLowPower,
25    /// Adaptive strategy based on usage patterns
26    Adaptive,
27}
28
29/// Device type for power optimization
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31pub enum DeviceType {
32    /// Mobile phone or tablet
33    Mobile,
34    /// VR headset
35    VrHeadset,
36    /// AR glasses
37    ArGlasses,
38    /// Gaming handheld
39    GamingHandheld,
40    /// Smart earbuds
41    Earbuds,
42    /// Other portable device
43    Other,
44}
45
46/// Power optimization configuration
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct PowerConfig {
49    /// Power management strategy
50    pub strategy: PowerStrategy,
51    /// Target device type
52    pub device_type: DeviceType,
53    /// Battery capacity (mAh)
54    pub battery_capacity: u32,
55    /// Target battery life (hours)
56    pub target_battery_life: f32,
57    /// Current battery level (0.0 - 1.0)
58    pub current_battery_level: f32,
59    /// Thermal threshold (°C)
60    pub thermal_threshold: f32,
61    /// Enable aggressive power saving
62    pub aggressive_power_saving: bool,
63    /// Minimum quality level to maintain
64    pub min_quality_level: f32,
65    /// Maximum CPU usage percentage
66    pub max_cpu_usage: f32,
67    /// Enable display-off optimizations
68    pub display_off_optimizations: bool,
69    /// Enable background processing optimizations
70    pub background_optimizations: bool,
71}
72
73impl Default for PowerConfig {
74    fn default() -> Self {
75        Self {
76            strategy: PowerStrategy::Balanced,
77            device_type: DeviceType::Mobile,
78            battery_capacity: 3000,
79            target_battery_life: 8.0,
80            current_battery_level: 1.0,
81            thermal_threshold: 40.0,
82            aggressive_power_saving: false,
83            min_quality_level: 0.2,
84            max_cpu_usage: 25.0,
85            display_off_optimizations: true,
86            background_optimizations: true,
87        }
88    }
89}
90
91/// Power consumption profile for different operations
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct PowerProfile {
94    /// Base power consumption (mW)
95    pub base_power: f32,
96    /// CPU processing power per source (mW/source)
97    pub cpu_power_per_source: f32,
98    /// GPU processing power (mW) when enabled
99    pub gpu_power: f32,
100    /// Memory access power (mW/MB)
101    pub memory_power: f32,
102    /// Audio output power (mW)
103    pub audio_output_power: f32,
104    /// Sensor power (IMU, tracking) (mW)
105    pub sensor_power: f32,
106    /// Display power (mW) - for VR/AR
107    pub display_power: f32,
108}
109
110impl PowerProfile {
111    /// Get power profile for device type
112    pub fn for_device_type(device_type: DeviceType) -> Self {
113        match device_type {
114            DeviceType::Mobile => Self {
115                base_power: 200.0,
116                cpu_power_per_source: 15.0,
117                gpu_power: 300.0,
118                memory_power: 0.5,
119                audio_output_power: 50.0,
120                sensor_power: 20.0,
121                display_power: 800.0,
122            },
123            DeviceType::VrHeadset => Self {
124                base_power: 500.0,
125                cpu_power_per_source: 25.0,
126                gpu_power: 2000.0,
127                memory_power: 1.0,
128                audio_output_power: 100.0,
129                sensor_power: 150.0,
130                display_power: 3000.0,
131            },
132            DeviceType::ArGlasses => Self {
133                base_power: 300.0,
134                cpu_power_per_source: 20.0,
135                gpu_power: 800.0,
136                memory_power: 0.7,
137                audio_output_power: 75.0,
138                sensor_power: 100.0,
139                display_power: 500.0,
140            },
141            DeviceType::GamingHandheld => Self {
142                base_power: 400.0,
143                cpu_power_per_source: 20.0,
144                gpu_power: 1500.0,
145                memory_power: 0.8,
146                audio_output_power: 100.0,
147                sensor_power: 50.0,
148                display_power: 1200.0,
149            },
150            DeviceType::Earbuds => Self {
151                base_power: 20.0,
152                cpu_power_per_source: 5.0,
153                gpu_power: 0.0,
154                memory_power: 0.1,
155                audio_output_power: 30.0,
156                sensor_power: 10.0,
157                display_power: 0.0,
158            },
159            DeviceType::Other => Self {
160                base_power: 250.0,
161                cpu_power_per_source: 18.0,
162                gpu_power: 500.0,
163                memory_power: 0.6,
164                audio_output_power: 75.0,
165                sensor_power: 75.0,
166                display_power: 600.0,
167            },
168        }
169    }
170}
171
172/// Power usage metrics
173#[derive(Debug, Clone, Default)]
174pub struct PowerMetrics {
175    /// Current power consumption (mW)
176    pub current_power: f32,
177    /// Average power consumption (mW)
178    pub average_power: f32,
179    /// Peak power consumption (mW)
180    pub peak_power: f32,
181    /// Estimated battery life remaining (hours)
182    pub estimated_battery_life: f32,
183    /// Power efficiency (operations per watt)
184    pub efficiency: f32,
185    /// Thermal state (0.0 = cool, 1.0 = hot)
186    pub thermal_state: f32,
187    /// CPU usage percentage
188    pub cpu_usage: f32,
189    /// GPU usage percentage (if applicable)
190    pub gpu_usage: Option<f32>,
191    /// Memory usage (MB)
192    pub memory_usage: f32,
193}
194
195/// Power optimization history entry
196#[derive(Debug, Clone)]
197struct PowerHistoryEntry {
198    timestamp: Instant,
199    power_consumption: f32,
200    cpu_usage: f32,
201    thermal_state: f32,
202    quality_level: f32,
203    source_count: u32,
204}
205
206/// Power optimization manager
207pub struct PowerOptimizer {
208    config: PowerConfig,
209    profile: PowerProfile,
210    metrics: PowerMetrics,
211    history: VecDeque<PowerHistoryEntry>,
212    adaptive_params: AdaptiveParams,
213    last_optimization: Instant,
214    optimization_interval: Duration,
215}
216
217/// Adaptive power management parameters
218#[derive(Debug, Clone)]
219struct AdaptiveParams {
220    /// Learning rate for adaptive optimization
221    learning_rate: f32,
222    /// Usage pattern weights
223    usage_weights: [f32; 5], // Different usage scenarios
224    /// Quality adjustment factor
225    quality_factor: f32,
226    /// Thermal response factor
227    thermal_factor: f32,
228    /// Battery level response factor
229    battery_factor: f32,
230}
231
232impl Default for AdaptiveParams {
233    fn default() -> Self {
234        Self {
235            learning_rate: 0.1,
236            usage_weights: [0.2, 0.2, 0.2, 0.2, 0.2],
237            quality_factor: 1.0,
238            thermal_factor: 1.0,
239            battery_factor: 1.0,
240        }
241    }
242}
243
244impl PowerOptimizer {
245    /// Create a new power optimizer
246    pub fn new(config: PowerConfig) -> Self {
247        let profile = PowerProfile::for_device_type(config.device_type);
248        let optimization_interval = match config.strategy {
249            PowerStrategy::Performance => Duration::from_secs(10),
250            PowerStrategy::Balanced => Duration::from_secs(5),
251            PowerStrategy::PowerSaver => Duration::from_secs(2),
252            PowerStrategy::UltraLowPower => Duration::from_secs(1),
253            PowerStrategy::Adaptive => Duration::from_secs(3),
254        };
255
256        Self {
257            config,
258            profile,
259            metrics: PowerMetrics::default(),
260            history: VecDeque::with_capacity(3600), // 1 hour of history at 1s intervals
261            adaptive_params: AdaptiveParams::default(),
262            last_optimization: Instant::now(),
263            optimization_interval,
264        }
265    }
266
267    /// Update system state and optimize power consumption
268    #[allow(clippy::too_many_arguments)]
269    pub fn update_state(
270        &mut self,
271        battery_level: f32,
272        thermal_temp: f32,
273        cpu_usage: f32,
274        gpu_usage: Option<f32>,
275        memory_usage: f32,
276        source_count: u32,
277        quality_level: f32,
278    ) {
279        self.config.current_battery_level = battery_level.clamp(0.0, 1.0);
280        self.metrics.thermal_state = (thermal_temp - 20.0) / (self.config.thermal_threshold - 20.0);
281        self.metrics.thermal_state = self.metrics.thermal_state.clamp(0.0, 1.0);
282        self.metrics.cpu_usage = cpu_usage;
283        self.metrics.gpu_usage = gpu_usage;
284        self.metrics.memory_usage = memory_usage;
285
286        // Calculate current power consumption
287        self.metrics.current_power = self.calculate_power_consumption(
288            source_count,
289            quality_level,
290            cpu_usage,
291            gpu_usage.unwrap_or(0.0),
292            memory_usage,
293        );
294
295        // Update history
296        let history_entry = PowerHistoryEntry {
297            timestamp: Instant::now(),
298            power_consumption: self.metrics.current_power,
299            cpu_usage,
300            thermal_state: self.metrics.thermal_state,
301            quality_level,
302            source_count,
303        };
304
305        self.history.push_back(history_entry);
306        if self.history.len() > 3600 {
307            self.history.pop_front();
308        }
309
310        // Update average and peak power
311        self.update_power_statistics();
312
313        // Calculate estimated battery life
314        self.update_battery_estimation();
315
316        // Adaptive learning
317        if self.config.strategy == PowerStrategy::Adaptive {
318            self.update_adaptive_params();
319        }
320    }
321
322    /// Calculate power consumption based on current state
323    fn calculate_power_consumption(
324        &self,
325        source_count: u32,
326        quality_level: f32,
327        cpu_usage: f32,
328        gpu_usage: f32,
329        memory_usage: f32,
330    ) -> f32 {
331        let mut total_power = self.profile.base_power;
332
333        // CPU power scales with source count and quality
334        total_power += self.profile.cpu_power_per_source * source_count as f32 * quality_level;
335
336        // GPU power when enabled
337        if gpu_usage > 0.0 {
338            total_power += self.profile.gpu_power * (gpu_usage / 100.0);
339        }
340
341        // Memory access power
342        total_power += self.profile.memory_power * memory_usage;
343
344        // Audio output power
345        total_power += self.profile.audio_output_power;
346
347        // Sensor power
348        total_power += self.profile.sensor_power;
349
350        // Display power (if applicable)
351        if matches!(
352            self.config.device_type,
353            DeviceType::VrHeadset | DeviceType::ArGlasses | DeviceType::GamingHandheld
354        ) {
355            total_power += self.profile.display_power;
356        }
357
358        // Thermal scaling
359        if self.metrics.thermal_state > 0.8 {
360            total_power *= 1.2; // Thermal throttling increases power
361        }
362
363        total_power
364    }
365
366    /// Update power statistics
367    fn update_power_statistics(&mut self) {
368        if self.history.is_empty() {
369            return;
370        }
371
372        let recent_power: Vec<f32> = self
373            .history
374            .iter()
375            .rev()
376            .take(60) // Last minute
377            .map(|entry| entry.power_consumption)
378            .collect();
379
380        self.metrics.average_power = recent_power.iter().sum::<f32>() / recent_power.len() as f32;
381        self.metrics.peak_power = recent_power.iter().cloned().fold(0.0, f32::max);
382    }
383
384    /// Update battery life estimation
385    fn update_battery_estimation(&mut self) {
386        if self.metrics.average_power > 0.0 {
387            let remaining_capacity_mah =
388                self.config.battery_capacity as f32 * self.config.current_battery_level;
389            let remaining_capacity_mwh = remaining_capacity_mah * 3.7; // Convert mAh to mWh (assuming 3.7V)
390            let hours_remaining = remaining_capacity_mwh / self.metrics.average_power; // mWh / mW = hours
391            self.metrics.estimated_battery_life = hours_remaining;
392        }
393    }
394
395    /// Update adaptive parameters based on usage patterns
396    fn update_adaptive_params(&mut self) {
397        if self.history.len() < 60 {
398            return; // Need at least 1 minute of data
399        }
400
401        // Analyze recent usage patterns
402        let recent_entries: Vec<&PowerHistoryEntry> = self.history.iter().rev().take(300).collect(); // Last 5 minutes
403
404        // Calculate correlation between quality and power consumption
405        let quality_power_correlation = self.calculate_correlation(
406            &recent_entries
407                .iter()
408                .map(|e| e.quality_level)
409                .collect::<Vec<f32>>(),
410            &recent_entries
411                .iter()
412                .map(|e| e.power_consumption)
413                .collect::<Vec<f32>>(),
414        );
415
416        // Adjust adaptive parameters
417        if quality_power_correlation > 0.7 {
418            self.adaptive_params.quality_factor =
419                (self.adaptive_params.quality_factor * 0.95).max(0.5);
420        } else if quality_power_correlation < 0.3 {
421            self.adaptive_params.quality_factor =
422                (self.adaptive_params.quality_factor * 1.05).min(1.5);
423        }
424
425        // Thermal adaptation
426        let avg_thermal = recent_entries.iter().map(|e| e.thermal_state).sum::<f32>()
427            / recent_entries.len() as f32;
428        if avg_thermal > 0.7 {
429            self.adaptive_params.thermal_factor *= 0.9;
430        } else if avg_thermal < 0.3 {
431            self.adaptive_params.thermal_factor *= 1.1;
432        }
433
434        // Battery adaptation
435        if self.config.current_battery_level < 0.2 {
436            self.adaptive_params.battery_factor *= 0.8;
437        } else if self.config.current_battery_level > 0.8 {
438            self.adaptive_params.battery_factor *= 1.1;
439        }
440
441        // Clamp factors
442        self.adaptive_params.quality_factor = self.adaptive_params.quality_factor.clamp(0.3, 2.0);
443        self.adaptive_params.thermal_factor = self.adaptive_params.thermal_factor.clamp(0.5, 1.5);
444        self.adaptive_params.battery_factor = self.adaptive_params.battery_factor.clamp(0.3, 1.5);
445    }
446
447    /// Calculate correlation between two data series
448    fn calculate_correlation(&self, x: &[f32], y: &[f32]) -> f32 {
449        if x.len() != y.len() || x.is_empty() {
450            return 0.0;
451        }
452
453        let n = x.len() as f32;
454        let mean_x = x.iter().sum::<f32>() / n;
455        let mean_y = y.iter().sum::<f32>() / n;
456
457        let mut numerator = 0.0;
458        let mut sum_sq_x = 0.0;
459        let mut sum_sq_y = 0.0;
460
461        for (xi, yi) in x.iter().zip(y.iter()) {
462            let dx = xi - mean_x;
463            let dy = yi - mean_y;
464            numerator += dx * dy;
465            sum_sq_x += dx * dx;
466            sum_sq_y += dy * dy;
467        }
468
469        let denominator = (sum_sq_x * sum_sq_y).sqrt();
470        if denominator == 0.0 {
471            0.0
472        } else {
473            numerator / denominator
474        }
475    }
476
477    /// Get optimized spatial configuration for current power state
478    pub fn get_optimized_config(&self) -> SpatialConfig {
479        let mut config = SpatialConfig::default();
480
481        // Base optimization based on strategy
482        match self.config.strategy {
483            PowerStrategy::Performance => {
484                config.quality_level = 1.0;
485                config.max_sources = 32;
486                config.use_gpu = true;
487                config.buffer_size = 1024;
488            }
489            PowerStrategy::Balanced => {
490                config.quality_level = 0.7 * self.adaptive_params.quality_factor;
491                config.max_sources = 16;
492                config.use_gpu = self.metrics.thermal_state < 0.6;
493                config.buffer_size = 2048;
494            }
495            PowerStrategy::PowerSaver => {
496                config.quality_level = 0.4 * self.adaptive_params.quality_factor;
497                config.max_sources = 8;
498                config.use_gpu = false;
499                config.buffer_size = 4096;
500            }
501            PowerStrategy::UltraLowPower => {
502                config.quality_level = self.config.min_quality_level;
503                config.max_sources = 4;
504                config.use_gpu = false;
505                config.buffer_size = 8192;
506                config.sample_rate = 22050; // Lower sample rate
507            }
508            PowerStrategy::Adaptive => {
509                // Use adaptive parameters
510                config.quality_level = (0.6 * self.adaptive_params.quality_factor)
511                    .clamp(self.config.min_quality_level, 1.0);
512                config.max_sources = if self.config.current_battery_level > 0.5 {
513                    16
514                } else {
515                    8
516                };
517                config.use_gpu =
518                    self.metrics.thermal_state < 0.5 && self.config.current_battery_level > 0.3;
519                config.buffer_size = if self.config.current_battery_level > 0.5 {
520                    2048
521                } else {
522                    4096
523                };
524            }
525        }
526
527        // Additional optimizations based on device state
528        if self.config.current_battery_level < 0.1 {
529            // Critical battery
530            config.quality_level = self.config.min_quality_level;
531            config.max_sources = 2;
532            config.use_gpu = false;
533            config.buffer_size = 8192;
534            config.sample_rate = 16000;
535        } else if self.metrics.thermal_state > 0.8 {
536            // Thermal throttling
537            config.quality_level *= 0.5;
538            config.max_sources = (config.max_sources / 2).max(2);
539            config.use_gpu = false;
540        }
541
542        // Device-specific optimizations
543        match self.config.device_type {
544            DeviceType::Earbuds => {
545                config.max_sources = config.max_sources.min(4);
546                config.buffer_size = config.buffer_size.max(2048);
547            }
548            DeviceType::VrHeadset => {
549                // VR needs lower latency but can use more power
550                config.buffer_size = config.buffer_size.min(2048);
551            }
552            DeviceType::ArGlasses => {
553                // AR needs to balance processing with transparency
554                config.quality_level *= 0.9;
555            }
556            _ => {} // No specific optimizations
557        }
558
559        config
560    }
561
562    /// Get current power metrics
563    pub fn get_metrics(&self) -> PowerMetrics {
564        self.metrics.clone()
565    }
566
567    /// Force a specific power strategy
568    pub fn set_power_strategy(&mut self, strategy: PowerStrategy) {
569        self.config.strategy = strategy;
570    }
571
572    /// Enable/disable aggressive power saving
573    pub fn set_aggressive_power_saving(&mut self, enabled: bool) {
574        self.config.aggressive_power_saving = enabled;
575    }
576
577    /// Check if optimization update is needed
578    pub fn should_optimize(&self) -> bool {
579        self.last_optimization.elapsed() >= self.optimization_interval
580    }
581
582    /// Perform optimization cycle
583    pub fn optimize(&mut self) -> Result<()> {
584        self.last_optimization = Instant::now();
585
586        // Adaptive strategy learns from usage patterns
587        if self.config.strategy == PowerStrategy::Adaptive {
588            self.update_adaptive_params();
589        }
590
591        Ok(())
592    }
593}
594
595#[cfg(test)]
596mod tests {
597    use super::*;
598
599    #[test]
600    fn test_power_config_creation() {
601        let config = PowerConfig::default();
602        assert_eq!(config.strategy, PowerStrategy::Balanced);
603        assert_eq!(config.device_type, DeviceType::Mobile);
604    }
605
606    #[test]
607    fn test_power_profile_device_specific() {
608        let mobile_profile = PowerProfile::for_device_type(DeviceType::Mobile);
609        let vr_profile = PowerProfile::for_device_type(DeviceType::VrHeadset);
610        let earbuds_profile = PowerProfile::for_device_type(DeviceType::Earbuds);
611
612        // VR should consume more power than mobile
613        assert!(vr_profile.base_power > mobile_profile.base_power);
614        assert!(vr_profile.gpu_power > mobile_profile.gpu_power);
615
616        // Earbuds should consume least power
617        assert!(earbuds_profile.base_power < mobile_profile.base_power);
618        assert_eq!(earbuds_profile.gpu_power, 0.0);
619    }
620
621    #[test]
622    fn test_power_optimizer_creation() {
623        let config = PowerConfig::default();
624        let optimizer = PowerOptimizer::new(config);
625
626        assert_eq!(optimizer.config.strategy, PowerStrategy::Balanced);
627        assert_eq!(optimizer.history.len(), 0);
628    }
629
630    #[test]
631    fn test_power_consumption_calculation() {
632        let config = PowerConfig::default();
633        let optimizer = PowerOptimizer::new(config);
634
635        let power = optimizer.calculate_power_consumption(8, 0.8, 50.0, 0.0, 100.0);
636        assert!(power > 0.0);
637
638        // More sources should use more power
639        let power_more_sources = optimizer.calculate_power_consumption(16, 0.8, 50.0, 0.0, 100.0);
640        assert!(power_more_sources > power);
641    }
642
643    #[test]
644    fn test_power_state_updates() {
645        let config = PowerConfig::default();
646        let mut optimizer = PowerOptimizer::new(config);
647
648        optimizer.update_state(0.5, 35.0, 40.0, Some(60.0), 150.0, 12, 0.7);
649
650        assert_eq!(optimizer.config.current_battery_level, 0.5);
651        assert!(optimizer.metrics.current_power > 0.0);
652        assert_eq!(optimizer.history.len(), 1);
653    }
654
655    #[test]
656    fn test_optimized_config_generation() {
657        let config = PowerConfig {
658            strategy: PowerStrategy::PowerSaver,
659            device_type: DeviceType::Mobile,
660            ..Default::default()
661        };
662        let optimizer = PowerOptimizer::new(config);
663
664        let spatial_config = optimizer.get_optimized_config();
665        assert!(spatial_config.quality_level <= 0.5);
666        assert!(!spatial_config.use_gpu);
667        assert!(spatial_config.buffer_size >= 4096);
668    }
669
670    #[test]
671    fn test_critical_battery_optimization() {
672        let config = PowerConfig {
673            current_battery_level: 0.05, // Critical battery
674            ..Default::default()
675        };
676        let optimizer = PowerOptimizer::new(config);
677
678        let spatial_config = optimizer.get_optimized_config();
679        assert_eq!(spatial_config.max_sources, 2);
680        assert!(!spatial_config.use_gpu);
681        assert_eq!(spatial_config.sample_rate, 16000);
682    }
683
684    #[test]
685    fn test_thermal_throttling() {
686        let config = PowerConfig::default();
687        let mut optimizer = PowerOptimizer::new(config);
688
689        // Simulate high thermal state
690        optimizer.metrics.thermal_state = 0.9;
691
692        let spatial_config = optimizer.get_optimized_config();
693        assert!(!spatial_config.use_gpu);
694        assert!(spatial_config.quality_level < 0.5);
695    }
696
697    #[test]
698    fn test_device_specific_optimization() {
699        let earbuds_config = PowerConfig {
700            device_type: DeviceType::Earbuds,
701            ..Default::default()
702        };
703        let earbuds_optimizer = PowerOptimizer::new(earbuds_config);
704
705        let spatial_config = earbuds_optimizer.get_optimized_config();
706        assert!(spatial_config.max_sources <= 4);
707
708        let vr_config = PowerConfig {
709            device_type: DeviceType::VrHeadset,
710            ..Default::default()
711        };
712        let vr_optimizer = PowerOptimizer::new(vr_config);
713
714        let vr_spatial_config = vr_optimizer.get_optimized_config();
715        assert!(vr_spatial_config.buffer_size <= 2048); // VR needs lower latency
716    }
717
718    #[test]
719    fn test_adaptive_strategy() {
720        let config = PowerConfig {
721            strategy: PowerStrategy::Adaptive,
722            ..Default::default()
723        };
724        let mut optimizer = PowerOptimizer::new(config);
725
726        // Simulate usage pattern
727        for i in 0..100 {
728            optimizer.update_state(0.8, 30.0, 20.0, None, 100.0, 8, 0.8);
729            if i % 10 == 0 {
730                optimizer.optimize().expect("Optimization should succeed");
731            }
732        }
733
734        assert!(optimizer.adaptive_params.quality_factor > 0.0);
735    }
736
737    #[test]
738    fn test_correlation_calculation() {
739        let config = PowerConfig::default();
740        let optimizer = PowerOptimizer::new(config);
741
742        let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
743        let y = vec![2.0, 4.0, 6.0, 8.0, 10.0];
744
745        let correlation = optimizer.calculate_correlation(&x, &y);
746        assert!((correlation - 1.0).abs() < 0.01); // Perfect positive correlation
747    }
748
749    #[test]
750    fn test_battery_estimation() {
751        let config = PowerConfig {
752            battery_capacity: 3000,
753            current_battery_level: 0.5,
754            ..Default::default()
755        };
756        let mut optimizer = PowerOptimizer::new(config);
757
758        optimizer.metrics.average_power = 1000.0; // 1W
759        optimizer.update_battery_estimation();
760
761        assert!(optimizer.metrics.estimated_battery_life > 0.0);
762        assert!(optimizer.metrics.estimated_battery_life < 100.0); // Reasonable range (less than 100 hours)
763    }
764}