oxihuman_export/
biometric_export.rs1#![allow(dead_code)]
4
5pub struct BiometricSample {
7 pub time_s: f32,
8 pub heart_rate_bpm: f32,
9 pub spo2_percent: f32,
10 pub skin_temp_c: f32,
11 pub respiratory_rate: f32,
12}
13
14pub fn new_biometric_sample(t: f32, hr: f32, spo2: f32) -> BiometricSample {
15 BiometricSample {
16 time_s: t,
17 heart_rate_bpm: hr,
18 spo2_percent: spo2,
19 skin_temp_c: 36.5,
20 respiratory_rate: 15.0,
21 }
22}
23
24pub fn biometric_to_csv_line(s: &BiometricSample) -> String {
25 format!(
26 "{:.4},{:.2},{:.2},{:.2},{:.2}",
27 s.time_s, s.heart_rate_bpm, s.spo2_percent, s.skin_temp_c, s.respiratory_rate
28 )
29}
30
31pub fn biometric_sequence_to_csv(samples: &[BiometricSample]) -> String {
32 let header = "time_s,heart_rate_bpm,spo2_percent,skin_temp_c,respiratory_rate\n";
33 let rows: Vec<String> = samples.iter().map(biometric_to_csv_line).collect();
34 format!("{}{}", header, rows.join("\n"))
35}
36
37pub fn biometric_average_hr(samples: &[BiometricSample]) -> f32 {
38 if samples.is_empty() {
39 return 0.0;
40 }
41 samples.iter().map(|s| s.heart_rate_bpm).sum::<f32>() / samples.len() as f32
42}
43
44pub fn biometric_min_spo2(samples: &[BiometricSample]) -> f32 {
45 samples
46 .iter()
47 .map(|s| s.spo2_percent)
48 .fold(f32::INFINITY, f32::min)
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn test_new_biometric_sample() {
57 let s = new_biometric_sample(0.0, 72.0, 98.5);
58 assert!((s.heart_rate_bpm - 72.0).abs() < 1e-5);
59 assert!((s.spo2_percent - 98.5).abs() < 1e-5);
60 }
61
62 #[test]
63 fn test_biometric_to_csv_line_fields() {
64 let s = new_biometric_sample(1.0, 80.0, 97.0);
65 let line = biometric_to_csv_line(&s);
66 assert!(line.contains("1.0000"));
67 assert!(line.contains("80.00"));
68 }
69
70 #[test]
71 fn test_biometric_sequence_to_csv_header() {
72 let csv = biometric_sequence_to_csv(&[]);
73 assert!(csv.starts_with("time_s"));
74 }
75
76 #[test]
77 fn test_biometric_average_hr() {
78 let samples = vec![
79 new_biometric_sample(0.0, 60.0, 98.0),
80 new_biometric_sample(1.0, 80.0, 98.0),
81 ];
82 assert!((biometric_average_hr(&samples) - 70.0).abs() < 1e-4);
83 }
84
85 #[test]
86 fn test_biometric_min_spo2() {
87 let samples = vec![
88 new_biometric_sample(0.0, 70.0, 99.0),
89 new_biometric_sample(1.0, 70.0, 95.0),
90 new_biometric_sample(2.0, 70.0, 97.0),
91 ];
92 assert!((biometric_min_spo2(&samples) - 95.0).abs() < 1e-4);
93 }
94
95 #[test]
96 fn test_biometric_average_hr_empty() {
97 assert!((biometric_average_hr(&[]) - 0.0).abs() < 1e-6);
98 }
99
100 #[test]
101 fn test_biometric_sequence_csv_row_count() {
102 let samples = vec![
103 new_biometric_sample(0.0, 70.0, 98.0),
104 new_biometric_sample(1.0, 75.0, 99.0),
105 ];
106 let csv = biometric_sequence_to_csv(&samples);
107 let lines: Vec<&str> = csv.lines().collect();
108 assert_eq!(lines.len(), 3); }
110}