-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdata_manager.rs
300 lines (251 loc) · 12 KB
/
data_manager.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
use rodio::{Decoder, OutputStream, Sink, Source};
use std::collections::VecDeque;
use std::fs::File;
use std::io::{self, BufReader, Write};
use std::path::Path;
use std::time::Instant;
const AUDIO_PATH: &str = "src/src/dida.mp3";
// 引入信号处理相关的模块
use crate::signal_process::{detect_peaks, ButterworthFilter, HighPassFilter, MovingAverageFilter};
// 新增配置结构体,用于存储数据管理器的配置参数
pub struct DataManagerConfig {
max_points: usize, // 最大数据点数
sampling_rate: f32, // 采样率
filter_cutoff_freq: f32, // 滤波器截止频率
peak_threshold: f32, // 峰值检测阈值
max_display_points: usize, // 最大显示数据点数
moving_average_window: usize, // 移动平均窗口大小
down_sample_factor: usize, // 降采样因子
time_window: f32, // 时间窗口配置(秒)
display_window: f32, // 显示窗口时间(秒)
min_calc_window: f32, // 最小计算窗口时间(秒)
}
// 为 DataManagerConfig 结构体实现默认值
impl Default for DataManagerConfig {
fn default() -> Self {
Self {
max_points: 5000, // 最大数据点数
sampling_rate: 1000.0, // 采样率
filter_cutoff_freq: 10.0, // 滤波器截止频率
peak_threshold: 1500.0, // 峰值检测阈值
max_display_points: 1000, // 最大显示数据点数
moving_average_window: 6, // 移动平均窗口大小
down_sample_factor: 10, // 降采样因子
time_window: 10.0, // 时间窗口(10秒)
display_window: 10.0, // 显示窗口(10秒)
min_calc_window: 3.0, // 最小计算窗口(3秒)
}
}
}
// 数据管理器结构体,用于管理数据的存储和处理
pub struct DataManager {
buffer: VecDeque<(Instant, f32)>, // 原始数据缓冲区,存储时间戳和数据点
filtered_buffer: VecDeque<(Instant, f32)>, // 过滤后的数据缓冲区
config: DataManagerConfig, // 数据管理器配置
pulse: u32, // 当前心率
high_pass_filter: HighPassFilter, // 高通滤波器
butterworth_filter: ButterworthFilter, // 巴特沃斯滤波器
moving_average_filter: MovingAverageFilter, // 移动平均滤波器
last_pulse_calculation: Instant, // 上次心率计算时间
last_sample_time: Instant, // 最后采样时间
audio_manager: AudioManager,
}
impl DataManager {
pub fn new(config: DataManagerConfig) -> Self {
let now = Instant::now(); // 获取当前时间
// 从配置中提取参数
let sampling_rate = config.sampling_rate;
let filter_cutoff_freq = config.filter_cutoff_freq;
let moving_average_window = config.moving_average_window;
Self {
buffer: VecDeque::with_capacity(config.max_points), // 初始化原始数据缓冲区
filtered_buffer: VecDeque::with_capacity(config.max_points), // 初始化过滤后的数据缓冲区
config, // 设置配置
pulse: 0, // 初始化心率为0
high_pass_filter: HighPassFilter::new(0.2, sampling_rate), // 初始化高通滤波器
butterworth_filter: ButterworthFilter::new(filter_cutoff_freq, sampling_rate), // 初始化巴特沃斯滤波器
moving_average_filter: MovingAverageFilter::new(moving_average_window), // 初始化移动平均滤波器
last_pulse_calculation: now, // 初始化上次心率计算时间
last_sample_time: now, // 初始化最后采样时间
audio_manager: AudioManager::new(AUDIO_PATH).unwrap(),
}
}
// 处理单个数据点
fn process_point(&mut self, point: f32, timestamp: Instant) -> f32 {
let high_passed_point = self.high_pass_filter.filter(point); // 经过高通滤波器处理
let filtered_point = self.butterworth_filter.filter(high_passed_point); // 经过巴特沃斯滤波器处理
let smoothed_point = self.moving_average_filter.filter(filtered_point); // 经过移动平均滤波器处理
self.filtered_buffer.push_back((timestamp, smoothed_point)); // 将处理后的数据点添加到过滤后的缓冲区
// 控制过滤后的缓冲区大小
while self.filtered_buffer.len() > self.config.max_points {
self.filtered_buffer.pop_front(); // 如果超出最大点数,移除最旧的数据点
}
smoothed_point // 返回平滑后的数据点
}
// 添加新的数据点
pub fn add_point(&mut self, point: f32) -> Result<(), String> {
let now = Instant::now();
// 基于时间间隔的降采样
let sample_interval = std::time::Duration::from_secs_f32(
1.0 / (self.config.sampling_rate / self.config.down_sample_factor as f32),
);
if now.duration_since(self.last_sample_time) >= sample_interval {
// 添加降采样后的数据点
self.buffer.push_back((now, point));
// 控制缓冲区大小
while self.buffer.len() > self.config.max_points {
self.buffer.pop_front();
}
// 处理数据点
self.process_point(point, now);
self.calculate_pulse()?;
self.last_sample_time = now;
}
Ok(())
}
fn calculate_pulse(&mut self) -> Result<(), String> {
let now = Instant::now(); // 获取当前时间
// 获取时间窗口内的数据
let window_duration = std::time::Duration::from_secs_f32(self.config.time_window); // 时间窗口
let start_time = now - window_duration; // 窗口开始时间
// 过滤出在时间窗口内的过滤数据
let window_data: Vec<f32> = self
.filtered_buffer
.iter()
.filter(|(t, _)| *t >= start_time) // 过滤出在时间窗口内的数据点
.map(|&(_, v)| v) // 提取数据点值
.collect();
// 检查是否有足够的数据进行计算
let min_duration = std::time::Duration::from_secs_f32(self.config.min_calc_window); // 最小计算窗口
if self.filtered_buffer.len() < 2
|| self
.filtered_buffer
.back()
.unwrap()
.0
.duration_since(self.filtered_buffer.front().unwrap().0)
< min_duration
{
return Ok(()); // 如果数据点不足,返回
}
// 检测峰值
let (count, is_latest_peak_max) = detect_peaks(&window_data, self.config.peak_threshold, 5);
if is_latest_peak_max {
self.audio_manager.play_audio(); // 播放音频
}
// 计算心率(每分钟心跳次数)
let time_span_minutes = self.config.time_window / 60.0; // 时间窗口转换为分钟
let new_pulse = (count as f32 / time_span_minutes) as u32; // 计算新的心率
println!("Peak count: {}", count);
// 心率合理性检查
if !(40..=200).contains(&new_pulse) {
return Ok(()); // 忽略明显不合理的心率值
}
// 心率平滑处理
if new_pulse > 0 {
let alpha = 0.3; // 平滑因子
self.pulse = ((1.0 - alpha) * self.pulse as f32 + alpha * new_pulse as f32) as u32;
} else {
self.pulse = 0; // 如果当前心率小于0,直接赋值为0
}
self.last_pulse_calculation = now; // 更新最后心率计算时间
Ok(()) // 返回成功
}
// 获取显示数据
pub fn get_display_data(&self) -> Vec<[f64; 2]> {
if self.filtered_buffer.is_empty() {
return vec![]; // 如果过滤后的缓冲区为空,返回空向量
}
let now = Instant::now(); // 获取当前时间
let display_duration = std::time::Duration::from_secs_f32(self.config.display_window); // 显示窗口
let start_time = now - display_duration; // 窗口开始时间
// 只获取显示窗口内的数据
let window_data: Vec<_> = self
.filtered_buffer
.iter()
.filter(|(t, _)| *t >= start_time) // 过滤出在显示窗口内的数据点
.collect();
if window_data.is_empty() {
return vec![]; // 如果窗口内没有数据,返回空向量
}
let first_time = window_data[0].0; // 窗口内第一个数据点的时间
let total_points = window_data.len(); // 窗口内数据点总数
let target_points = self.config.max_display_points; // 目标显示数据点数
let step = (total_points / target_points).max(1); // 计算步长
let mut result = Vec::with_capacity(target_points); // 初始化结果向量
let mut i = 0; // 初始化索引
while i < total_points {
let window_end = (i + step).min(total_points); // 计算窗口结束索引
let points_slice = &window_data[i..window_end]; // 获取当前窗口的数据片段
if !points_slice.is_empty() {
// 计算当前窗口的平均值和时间
let avg_value =
points_slice.iter().map(|(_, v)| v).sum::<f32>() / points_slice.len() as f32; // 计算平均值
let avg_time = points_slice[points_slice.len() / 2]
.0
.duration_since(first_time)
.as_secs_f64(); // 计算平均时间
result.push([avg_time, avg_value as f64]); // 将结果添加到结果向量
}
i += step; // 更新索引
}
result // 返回结果
}
// 获取当前心率
pub fn get_pulse(&self) -> u32 {
self.pulse // 返回当前心率
}
// 清空数据
pub fn clear_data(&mut self) {
self.buffer.clear(); // 清空原始数据缓冲区
self.filtered_buffer.clear(); // 清空过滤后的数据缓冲区
self.pulse = 0; // 重置心率
}
// 导出数据到文件
pub fn export_data<P: AsRef<Path>>(&self, file_path: P) -> io::Result<()> {
let mut file = File::create(format!(
"{}-{}",
file_path.as_ref().display(), // 文件路径
chrono::Local::now().format("%Y-%m-%d-%H-%M-%S") // 当前时间戳
))?;
// 写入CSV头部
writeln!(file, "timestamp, value")?;
if self.filtered_buffer.is_empty() {
return Ok(()); // 如果过滤后的缓冲区为空,返回
}
let start_time = self.filtered_buffer.front().unwrap().0; // 获取开始时间
for (timestamp, value) in &self.filtered_buffer {
let elapsed = timestamp.duration_since(start_time).as_secs_f64(); // 计算时间差
writeln!(file, "{},{}", elapsed, value)?; // 写入数据
}
Ok(()) // 返回成功
}
}
pub struct AudioManager {
sink: Sink,
buffered_source: rodio::source::Buffered<Decoder<BufReader<File>>>,
_stream: OutputStream,
}
impl AudioManager {
pub fn new(audio_path: &str) -> Result<Self, String> {
let (stream, stream_handle) =
OutputStream::try_default().map_err(|_| "Failed to create output stream")?;
let sink = Sink::try_new(&stream_handle).map_err(|_| "Failed to create sink")?;
// 打开并缓冲音频文件
let file = File::open(audio_path).map_err(|_| "Failed to open audio file")?;
let buffer_reader = BufReader::new(file);
let buffered_source = Decoder::new(buffer_reader)
.map_err(|_| "Failed to decode audio file")?
.buffered();
Ok(Self {
sink,
buffered_source,
_stream: stream, // 保存stream
})
}
pub fn play_audio(&self) {
if self.sink.empty() {
self.sink.append(self.buffered_source.clone());
}
}
}