1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16pub enum PowerStrategy {
17 Performance,
19 Balanced,
21 PowerSaver,
23 UltraLowPower,
25 Adaptive,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31pub enum DeviceType {
32 Mobile,
34 VrHeadset,
36 ArGlasses,
38 GamingHandheld,
40 Earbuds,
42 Other,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct PowerConfig {
49 pub strategy: PowerStrategy,
51 pub device_type: DeviceType,
53 pub battery_capacity: u32,
55 pub target_battery_life: f32,
57 pub current_battery_level: f32,
59 pub thermal_threshold: f32,
61 pub aggressive_power_saving: bool,
63 pub min_quality_level: f32,
65 pub max_cpu_usage: f32,
67 pub display_off_optimizations: bool,
69 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#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct PowerProfile {
94 pub base_power: f32,
96 pub cpu_power_per_source: f32,
98 pub gpu_power: f32,
100 pub memory_power: f32,
102 pub audio_output_power: f32,
104 pub sensor_power: f32,
106 pub display_power: f32,
108}
109
110impl PowerProfile {
111 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#[derive(Debug, Clone, Default)]
174pub struct PowerMetrics {
175 pub current_power: f32,
177 pub average_power: f32,
179 pub peak_power: f32,
181 pub estimated_battery_life: f32,
183 pub efficiency: f32,
185 pub thermal_state: f32,
187 pub cpu_usage: f32,
189 pub gpu_usage: Option<f32>,
191 pub memory_usage: f32,
193}
194
195#[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
206pub 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#[derive(Debug, Clone)]
219struct AdaptiveParams {
220 learning_rate: f32,
222 usage_weights: [f32; 5], quality_factor: f32,
226 thermal_factor: f32,
228 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 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), adaptive_params: AdaptiveParams::default(),
262 last_optimization: Instant::now(),
263 optimization_interval,
264 }
265 }
266
267 #[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 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 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 self.update_power_statistics();
312
313 self.update_battery_estimation();
315
316 if self.config.strategy == PowerStrategy::Adaptive {
318 self.update_adaptive_params();
319 }
320 }
321
322 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 total_power += self.profile.cpu_power_per_source * source_count as f32 * quality_level;
335
336 if gpu_usage > 0.0 {
338 total_power += self.profile.gpu_power * (gpu_usage / 100.0);
339 }
340
341 total_power += self.profile.memory_power * memory_usage;
343
344 total_power += self.profile.audio_output_power;
346
347 total_power += self.profile.sensor_power;
349
350 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 if self.metrics.thermal_state > 0.8 {
360 total_power *= 1.2; }
362
363 total_power
364 }
365
366 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) .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 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; let hours_remaining = remaining_capacity_mwh / self.metrics.average_power; self.metrics.estimated_battery_life = hours_remaining;
392 }
393 }
394
395 fn update_adaptive_params(&mut self) {
397 if self.history.len() < 60 {
398 return; }
400
401 let recent_entries: Vec<&PowerHistoryEntry> = self.history.iter().rev().take(300).collect(); 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 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 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 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 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 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 pub fn get_optimized_config(&self) -> SpatialConfig {
479 let mut config = SpatialConfig::default();
480
481 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; }
508 PowerStrategy::Adaptive => {
509 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 if self.config.current_battery_level < 0.1 {
529 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 config.quality_level *= 0.5;
538 config.max_sources = (config.max_sources / 2).max(2);
539 config.use_gpu = false;
540 }
541
542 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 config.buffer_size = config.buffer_size.min(2048);
551 }
552 DeviceType::ArGlasses => {
553 config.quality_level *= 0.9;
555 }
556 _ => {} }
558
559 config
560 }
561
562 pub fn get_metrics(&self) -> PowerMetrics {
564 self.metrics.clone()
565 }
566
567 pub fn set_power_strategy(&mut self, strategy: PowerStrategy) {
569 self.config.strategy = strategy;
570 }
571
572 pub fn set_aggressive_power_saving(&mut self, enabled: bool) {
574 self.config.aggressive_power_saving = enabled;
575 }
576
577 pub fn should_optimize(&self) -> bool {
579 self.last_optimization.elapsed() >= self.optimization_interval
580 }
581
582 pub fn optimize(&mut self) -> Result<()> {
584 self.last_optimization = Instant::now();
585
586 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 assert!(vr_profile.base_power > mobile_profile.base_power);
614 assert!(vr_profile.gpu_power > mobile_profile.gpu_power);
615
616 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 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, ..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 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); }
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 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); }
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; optimizer.update_battery_estimation();
760
761 assert!(optimizer.metrics.estimated_battery_life > 0.0);
762 assert!(optimizer.metrics.estimated_battery_life < 100.0); }
764}