second commit
This commit is contained in:
parent
3854ac2aa3
commit
e161dd7319
|
|
@ -4,5 +4,7 @@
|
||||||
class FileManager {
|
class FileManager {
|
||||||
public:
|
public:
|
||||||
static std::vector<uint8_t> readBinaryFile(const std::string& filename); // 读取二进制文件
|
static std::vector<uint8_t> readBinaryFile(const std::string& filename); // 读取二进制文件
|
||||||
|
|
||||||
};
|
};
|
||||||
|
void save_to_csv(const std::vector<SensorData>& all_data, const std::string& filename);//将数据写入csv
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -21,4 +21,5 @@ SensorData parse_snore(const uint8_t* data);
|
||||||
SensorData parse_respiration(const uint8_t* data);
|
SensorData parse_respiration(const uint8_t* data);
|
||||||
std::vector<SensorData> parse_device_data(const std::vector<uint8_t>& file_data);
|
std::vector<SensorData> parse_device_data(const std::vector<uint8_t>& file_data);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -140,6 +140,7 @@ public:
|
||||||
double sample_rate,
|
double sample_rate,
|
||||||
double low_cutoff,
|
double low_cutoff,
|
||||||
double high_cutoff);
|
double high_cutoff);
|
||||||
|
void normalize_amplitude(std::vector<float>& signal) ;
|
||||||
|
|
||||||
// 添加通用预处理辅助函数
|
// 添加通用预处理辅助函数
|
||||||
std::vector<float> remove_dc_offset(const std::vector<float>& signal);
|
std::vector<float> remove_dc_offset(const std::vector<float>& signal);
|
||||||
|
|
|
||||||
14
main.cpp
14
main.cpp
|
|
@ -22,20 +22,21 @@ void print_channel_data(const std::variant<std::vector<float>, std::vector<std::
|
||||||
void test_try() {
|
void test_try() {
|
||||||
try {
|
try {
|
||||||
// 1. 读取原始二进制文件
|
// 1. 读取原始二进制文件
|
||||||
std::vector<uint8_t> file_content = FileManager::readBinaryFile("C:/Users/29096/Desktop/work/data1.dat");
|
std::vector<uint8_t> file_content = FileManager::readBinaryFile("C:/Users/29096/Documents/WeChat Files/wxid_sh93l5lycr8b22/FileStorage/File/2025-07/ecg_data_raw.dat");
|
||||||
Mapper mapper;
|
Mapper mapper;
|
||||||
// 2. 解析设备数据包 - 需要实现此函数
|
// 2. 解析设备数据包 - 需要实现此函数
|
||||||
std::vector<SensorData> all_data = parse_device_data(file_content);
|
std::vector<SensorData> all_data = parse_device_data(file_content);
|
||||||
|
save_to_csv(all_data, "channel_data_transered_.csv");
|
||||||
// 3. 创建信号处理器实例
|
// 3. 创建信号处理器实例
|
||||||
SignalProcessor processor;
|
/* SignalProcessor processor;
|
||||||
// 4. 遍历所有数据包
|
// 4. 遍历所有数据包
|
||||||
for (auto& data : all_data) {
|
for (auto& data : all_data) {
|
||||||
// 打印原始数据信息
|
// 打印原始数据信息
|
||||||
// print_parsed_result(data); // 需要实现此函数
|
// print_parsed_result(data); // 需要实现此函数
|
||||||
std::cout << "Before mapping (ECG-12):" << std::endl;
|
std::cout << "Before mapping (ECG-12):" << std::endl;
|
||||||
std::cout << "Packet SN: " << data.packet_sn << ", Data Type: " << static_cast<int>(data.data_type) << std::endl;
|
std::cout << "Packet SN: " << data.packet_sn << ", Data Type: " << static_cast<int>(data.data_type) << std::endl;*/
|
||||||
|
// print_channel_data(data.channel_data);
|
||||||
SensorData mapped_ecg_12lead = mapper.DataMapper(data);
|
/* SensorData mapped_ecg_12lead = mapper.DataMapper(data);
|
||||||
SensorData processed_ecg_12_lead = processor.preprocess_signals(mapped_ecg_12lead);
|
SensorData processed_ecg_12_lead = processor.preprocess_signals(mapped_ecg_12lead);
|
||||||
std::cout << "After mapping (ECG-12):" << std::endl;
|
std::cout << "After mapping (ECG-12):" << std::endl;
|
||||||
std::cout << "Packet SN: " << mapped_ecg_12lead.packet_sn << ", Data Type: " << static_cast<int>(mapped_ecg_12lead.data_type) << std::endl;
|
std::cout << "Packet SN: " << mapped_ecg_12lead.packet_sn << ", Data Type: " << static_cast<int>(mapped_ecg_12lead.data_type) << std::endl;
|
||||||
|
|
@ -43,7 +44,7 @@ void test_try() {
|
||||||
std::cout << "After processed (ECG-12):" << std::endl;
|
std::cout << "After processed (ECG-12):" << std::endl;
|
||||||
std::cout << "Packet SN: " << mapped_ecg_12lead.packet_sn << ", Data Type: " << static_cast<int>(mapped_ecg_12lead.data_type) << std::endl;
|
std::cout << "Packet SN: " << mapped_ecg_12lead.packet_sn << ", Data Type: " << static_cast<int>(mapped_ecg_12lead.data_type) << std::endl;
|
||||||
print_channel_data(processed_ecg_12_lead.channel_data);
|
print_channel_data(processed_ecg_12_lead.channel_data);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
}catch (const std::exception& e) {
|
}catch (const std::exception& e) {
|
||||||
std::cerr << "解析错误: " << e.what() << std::endl;
|
std::cerr << "解析错误: " << e.what() << std::endl;
|
||||||
|
|
@ -59,3 +60,4 @@ int main() {
|
||||||
test_try();
|
test_try();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,3 +18,69 @@ std::vector<uint8_t> FileManager::readBinaryFile(const std::string& filename) {
|
||||||
// 返回整个vector而不是裸指针
|
// 返回整个vector而不是裸指针
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
// 保存通道数据到CSV文件
|
||||||
|
void save_to_csv(const std::vector<SensorData>& all_data, const std::string& filename) {
|
||||||
|
std::ofstream outfile(filename);
|
||||||
|
if (!outfile.is_open()) {
|
||||||
|
std::cerr << "无法打开文件: " << filename << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找最大通道数
|
||||||
|
size_t max_channels = 0;
|
||||||
|
for (const auto& data : all_data) {
|
||||||
|
if (std::holds_alternative<std::vector<std::vector<float>>>(data.channel_data)) {
|
||||||
|
const auto& channels = std::get<std::vector<std::vector<float>>>(data.channel_data);
|
||||||
|
if (channels.size() > max_channels) {
|
||||||
|
max_channels = channels.size();
|
||||||
|
}
|
||||||
|
} else if (std::holds_alternative<std::vector<float>>(data.channel_data)) {
|
||||||
|
if (1 > max_channels) { // 单通道设备
|
||||||
|
max_channels = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入表头
|
||||||
|
outfile << "SN";
|
||||||
|
for (size_t ch = 1; ch <= max_channels; ++ch) {
|
||||||
|
outfile << ",Channel" << ch;
|
||||||
|
}
|
||||||
|
outfile << "\n";
|
||||||
|
|
||||||
|
// 写入数据
|
||||||
|
for (const auto& data : all_data) {
|
||||||
|
// 处理单通道设备 (如SNORE)
|
||||||
|
if (std::holds_alternative<std::vector<float>>(data.channel_data)) {
|
||||||
|
const auto& samples = std::get<std::vector<float>>(data.channel_data);
|
||||||
|
for (const auto& sample : samples) {
|
||||||
|
outfile << data.packet_sn << "," << std::fixed << std::setprecision(4) << sample;
|
||||||
|
// 补充空值使列数一致
|
||||||
|
for (size_t i = 1; i < max_channels; ++i) {
|
||||||
|
outfile << ",";
|
||||||
|
}
|
||||||
|
outfile << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理多通道设备 (如EEG, ECG)
|
||||||
|
else if (std::holds_alternative<std::vector<std::vector<float>>>(data.channel_data)) {
|
||||||
|
const auto& channels = std::get<std::vector<std::vector<float>>>(data.channel_data);
|
||||||
|
const size_t num_samples = channels.empty() ? 0 : channels[0].size();
|
||||||
|
|
||||||
|
// 按采样点写入数据
|
||||||
|
for (size_t sample_idx = 0; sample_idx < num_samples; sample_idx++) {
|
||||||
|
outfile << data.packet_sn;
|
||||||
|
for (size_t ch_idx = 0; ch_idx < max_channels; ch_idx++) {
|
||||||
|
outfile << ",";
|
||||||
|
if (ch_idx < channels.size() && sample_idx < channels[ch_idx].size()) {
|
||||||
|
outfile << std::fixed << std::setprecision(4) << channels[ch_idx][sample_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outfile << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outfile.close();
|
||||||
|
std::cout << "数据已保存至: " << filename << std::endl;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "headfile.h"
|
#include "device_praser.h"
|
||||||
#include <cstring> // 添加 memcpy 头文件
|
#include <cstring> // 添加 memcpy 头文件
|
||||||
|
|
||||||
// 辅助函数:从字节数组读取小端整数
|
// 辅助函数:从字节数组读取小端整数
|
||||||
|
|
@ -203,7 +203,7 @@ SensorData parse_12lead_ecg(const uint8_t* data) {
|
||||||
for (int i = 0; i < 14; ++i) {
|
for (int i = 0; i < 14; ++i) {
|
||||||
int16_t adc_value = read_le<int16_t>(payload);
|
int16_t adc_value = read_le<int16_t>(payload);
|
||||||
payload += 2;
|
payload += 2;
|
||||||
channels[ch].push_back(adc_value); // raw_data
|
channels[ch].push_back(adc_value * 0.318f); //原始数据进行量纲转换
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,51 +307,139 @@ SensorData parse_respiration(const uint8_t* data) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统一解析入口函数 - 修改为处理多个数据包
|
// 统一解析入口函数 - 支持多个帧头和数据包组
|
||||||
std::vector<SensorData> parse_device_data(const std::vector<uint8_t>& file_data) {
|
std::vector<SensorData> parse_device_data(const std::vector<uint8_t>& file_data) {
|
||||||
const size_t PACKET_SIZE = 238; // 每个数据包固定大小
|
const size_t PACKET_SIZE = 238; // 每个数据包固定大小
|
||||||
const size_t file_size = file_data.size();
|
const size_t RESPONSE_HEADER_SIZE = 10; // 响应帧头大小 (2功能码 + 2数据长度 + 4实际采集点数+crc校验)
|
||||||
|
const uint16_t FUNCTION_CODE = 0x0010; // 获取数据功能码
|
||||||
|
|
||||||
if (file_size % PACKET_SIZE != 0) {
|
const size_t file_size = file_data.size();
|
||||||
throw std::runtime_error("文件大小不是238字节的整数倍,可能已损坏");
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t packet_count = file_size / PACKET_SIZE;
|
|
||||||
std::vector<SensorData> results;
|
|
||||||
results.reserve(packet_count);
|
|
||||||
|
|
||||||
const uint8_t* ptr = file_data.data();
|
const uint8_t* ptr = file_data.data();
|
||||||
for (size_t i = 0; i < packet_count; i++) {
|
const uint8_t* end_ptr = ptr + file_size;
|
||||||
// 读取数据类型标识
|
|
||||||
const uint16_t data_type = read_le<uint16_t>(ptr + 2);
|
std::vector<SensorData> results;
|
||||||
|
|
||||||
|
while (ptr < end_ptr) {
|
||||||
|
// 检查是否有响应帧头
|
||||||
|
bool has_response_header = false;
|
||||||
|
size_t remaining = end_ptr - ptr;
|
||||||
|
|
||||||
switch (data_type) {
|
// 确保有足够的空间检查帧头
|
||||||
case 0x4230:
|
if (remaining >= RESPONSE_HEADER_SIZE) {
|
||||||
results.push_back(parse_eeg(ptr));
|
// 读取功能码 (小端)
|
||||||
break;
|
uint16_t func_code = read_le<uint16_t>(ptr);
|
||||||
case 0x4211:
|
// 读取数据长度 (小端)
|
||||||
results.push_back(parse_ecg_emg(ptr));
|
uint16_t data_len = read_le<uint16_t>(ptr + 2);
|
||||||
break;
|
|
||||||
case 0x4212:
|
// 检查是否符合0x0010响应帧特征
|
||||||
results.push_back(parse_snore(ptr));
|
if (func_code == FUNCTION_CODE && data_len == 0x0004) {
|
||||||
break;
|
has_response_header = true;
|
||||||
case 0x4213:
|
|
||||||
results.push_back(parse_respiration(ptr));
|
// 读取实际数据点数 (小端)
|
||||||
break;
|
uint32_t actual_points = read_le<uint32_t>(ptr + 4);
|
||||||
case 0x4402:
|
|
||||||
results.push_back(parse_12lead_ecg(ptr));
|
// 计算期望的数据长度 (实际点数 * 包大小)
|
||||||
break;
|
size_t expected_data_size = actual_points * PACKET_SIZE;
|
||||||
case 0x4302:
|
size_t remaining_after_header = remaining - RESPONSE_HEADER_SIZE;
|
||||||
results.push_back(parse_ppg(ptr));
|
|
||||||
break;
|
// 验证数据长度是否匹配
|
||||||
case 0x1102:
|
if (remaining_after_header < expected_data_size) {
|
||||||
results.push_back(parse_stethoscope(ptr));
|
std::string error = "帧头数据不足: 预期 " +
|
||||||
// 其他设备类型可以继续添加
|
std::to_string(expected_data_size) +
|
||||||
default:
|
" 字节, 实际 " +
|
||||||
throw std::runtime_error("未知设备类型");
|
std::to_string(remaining_after_header) +
|
||||||
|
" 字节";
|
||||||
|
throw std::runtime_error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过帧头
|
||||||
|
ptr += RESPONSE_HEADER_SIZE;
|
||||||
|
|
||||||
|
// 处理帧头后的数据包
|
||||||
|
for (uint32_t i = 0; i < actual_points; i++) {
|
||||||
|
if (end_ptr - ptr < static_cast<ptrdiff_t>(PACKET_SIZE)) {
|
||||||
|
throw std::runtime_error("数据包不完整");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取数据类型标识
|
||||||
|
const uint16_t data_type = read_le<uint16_t>(ptr + 2);
|
||||||
|
|
||||||
|
switch (data_type) {
|
||||||
|
case 0x4230:
|
||||||
|
results.push_back(parse_eeg(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4211:
|
||||||
|
results.push_back(parse_ecg_emg(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4212:
|
||||||
|
results.push_back(parse_snore(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4213:
|
||||||
|
results.push_back(parse_respiration(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4402:
|
||||||
|
results.push_back(parse_12lead_ecg(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4302:
|
||||||
|
results.push_back(parse_ppg(ptr));
|
||||||
|
break;
|
||||||
|
case 0x1102:
|
||||||
|
results.push_back(parse_stethoscope(ptr));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("未知设备类型: 0x" +
|
||||||
|
to_hex_string(data_type));
|
||||||
|
}
|
||||||
|
ptr += PACKET_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有检测到响应帧头,尝试按纯数据包处理
|
||||||
|
if (!has_response_header) {
|
||||||
|
// 检查是否有完整的数据包
|
||||||
|
if (remaining >= PACKET_SIZE) {
|
||||||
|
// 读取数据类型标识
|
||||||
|
const uint16_t data_type = read_le<uint16_t>(ptr + 2);
|
||||||
|
|
||||||
|
switch (data_type) {
|
||||||
|
case 0x4230:
|
||||||
|
results.push_back(parse_eeg(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4211:
|
||||||
|
results.push_back(parse_ecg_emg(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4212:
|
||||||
|
results.push_back(parse_snore(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4213:
|
||||||
|
results.push_back(parse_respiration(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4402:
|
||||||
|
results.push_back(parse_12lead_ecg(ptr));
|
||||||
|
break;
|
||||||
|
case 0x4302:
|
||||||
|
results.push_back(parse_ppg(ptr));
|
||||||
|
break;
|
||||||
|
case 0x1102:
|
||||||
|
results.push_back(parse_stethoscope(ptr));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 如果不是已知类型,跳过这个包继续处理
|
||||||
|
ptr += PACKET_SIZE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ptr += PACKET_SIZE;
|
||||||
|
} else {
|
||||||
|
// 剩余数据不足一个包,但大于0,发出警告
|
||||||
|
if (remaining > 0) {
|
||||||
|
std::cerr << "警告: 文件包含 " << remaining
|
||||||
|
<< " 字节额外数据,已忽略" << std::endl;
|
||||||
|
}
|
||||||
|
break; // 结束处理
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ptr += PACKET_SIZE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
@ -47,18 +47,91 @@ SensorData SignalProcessor::preprocess_signals(const SensorData& raw_data ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SensorData SignalProcessor::preprocess_eeg(const SensorData& data) {
|
SensorData SignalProcessor::preprocess_eeg(const SensorData& data) {
|
||||||
|
const double SAMPLE_RATE = 250.0; // 脑电标准采样率250Hz
|
||||||
|
|
||||||
SensorData processed = data;
|
SensorData processed = data;
|
||||||
|
|
||||||
|
// 获取通道数据
|
||||||
|
auto& channels = std::get<std::vector<std::vector<float>>>(processed.channel_data);
|
||||||
|
if (channels.size() < 8) {
|
||||||
|
throw std::runtime_error("Invalid channel count for EEG");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分离EEG和EOG通道
|
||||||
|
std::vector<std::vector<float>> eeg_channels(channels.begin(), channels.begin() + 6);
|
||||||
|
std::vector<std::vector<float>> eog_channels(channels.begin() + 6, channels.end());
|
||||||
|
|
||||||
|
// 处理EEG通道
|
||||||
|
for (auto& channel : eeg_channels) {
|
||||||
|
// 1. 眼电伪迹补偿(使用EOG通道)
|
||||||
|
if (eog_channels.size() >= 2) {
|
||||||
|
channel = compensate_eog_artifact(channel, eog_channels[0], eog_channels[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 50Hz自适应陷波滤波 (去除工频干扰)
|
||||||
|
channel = adaptive_notch_filter(channel, SAMPLE_RATE, 50.0, 5.0);
|
||||||
|
|
||||||
|
// 3. 0.5-45Hz带通滤波 (保留有效频段)
|
||||||
|
channel = bandpass_filter(channel, SAMPLE_RATE, 0.5, 45.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理EOG通道
|
||||||
|
for (auto& channel : eog_channels) {
|
||||||
|
// 0.5-30Hz带通滤波
|
||||||
|
channel = bandpass_filter(channel, SAMPLE_RATE, 0.5, 30.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并处理后的通道
|
||||||
|
channels.clear();
|
||||||
|
channels.insert(channels.end(), eeg_channels.begin(), eeg_channels.end());
|
||||||
|
channels.insert(channels.end(), eog_channels.begin(), eog_channels.end());
|
||||||
|
|
||||||
|
// 计算并存储信号质量指数
|
||||||
|
float avg_sqi = 0.0f;
|
||||||
|
for (const auto& channel : eeg_channels) {
|
||||||
|
avg_sqi += calculate_snr(channel);
|
||||||
|
}
|
||||||
|
processed.sqi = avg_sqi / eeg_channels.size();
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
SensorData SignalProcessor::preprocess_ecg_2lead(const SensorData& data)
|
SensorData SignalProcessor::preprocess_ecg_2lead(const SensorData& data)
|
||||||
{
|
{
|
||||||
|
const double SAMPLE_RATE = 250.0; // 2导联心电标准采样率500Hz
|
||||||
|
|
||||||
SensorData processed = data;
|
SensorData processed = data;
|
||||||
|
|
||||||
|
// 获取通道数据
|
||||||
|
auto& channels = std::get<std::vector<std::vector<float>>>(processed.channel_data);
|
||||||
|
if (channels.size() < 2) {
|
||||||
|
throw std::runtime_error("Invalid channel count for 2-lead ECG");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对每个导联独立进行信号处理
|
||||||
|
for (auto& channel : channels) {
|
||||||
|
// 1. 0.5Hz高通滤波 (去除基线漂移)
|
||||||
|
channel = Highpass_filter(channel, SAMPLE_RATE, 0.5);
|
||||||
|
|
||||||
|
// 2. 50Hz自适应陷波滤波 (去除工频干扰)
|
||||||
|
channel = adaptive_notch_filter(channel, SAMPLE_RATE, 50.0, 5.0);
|
||||||
|
|
||||||
|
// 3. 25-40Hz带阻滤波 (去除肌电干扰)
|
||||||
|
channel = bandstop_filter(channel, SAMPLE_RATE, 25.0, 40.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算并存储信号质量指数
|
||||||
|
float avg_sqi = 0.0f;
|
||||||
|
for (const auto& channel : channels) {
|
||||||
|
avg_sqi += calculate_ecg_sqi(channel, SAMPLE_RATE);
|
||||||
|
}
|
||||||
|
processed.sqi = avg_sqi / channels.size();
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
|
|
||||||
}
|
}
|
||||||
// 12导联心电预处理函数
|
// 12导联心电预处理函数
|
||||||
SensorData SignalProcessor::preprocess_ecg_12lead(const SensorData& data) {
|
SensorData SignalProcessor::preprocess_ecg_12lead(const SensorData& data) {
|
||||||
const double SAMPLE_RATE = 250.0; // 12导联心电标准采样率500Hz
|
const double SAMPLE_RATE = 250.0; // 12导联心电标准采样率250.0Hz
|
||||||
|
|
||||||
// 创建处理后的数据结构
|
// 创建处理后的数据结构
|
||||||
SensorData processed = data;
|
SensorData processed = data;
|
||||||
|
|
@ -92,6 +165,7 @@ SensorData SignalProcessor::preprocess_ecg_12lead(const SensorData& data) {
|
||||||
}
|
}
|
||||||
SensorData SignalProcessor::preprocess_ppg(const SensorData& data) {
|
SensorData SignalProcessor::preprocess_ppg(const SensorData& data) {
|
||||||
// 1. 创建处理后的数据结构
|
// 1. 创建处理后的数据结构
|
||||||
|
double SAMPLE_RATE = 50;
|
||||||
SensorData processed = data;
|
SensorData processed = data;
|
||||||
|
|
||||||
// 2. 获取通道数据(红光和红外光)
|
// 2. 获取通道数据(红光和红外光)
|
||||||
|
|
@ -133,17 +207,81 @@ SensorData SignalProcessor::preprocess_ppg(const SensorData& data) {
|
||||||
}
|
}
|
||||||
SensorData SignalProcessor::preprocess_respiration(const SensorData& data)
|
SensorData SignalProcessor::preprocess_respiration(const SensorData& data)
|
||||||
{
|
{
|
||||||
|
const double SAMPLE_RATE = 100.0; // 呼吸信号标准采样率100Hz
|
||||||
|
|
||||||
SensorData processed = data;
|
SensorData processed = data;
|
||||||
|
|
||||||
|
// 获取通道数据
|
||||||
|
auto& channels = std::get<std::vector<std::vector<float>>>(processed.channel_data);
|
||||||
|
if (channels.empty()) {
|
||||||
|
throw std::runtime_error("No channel data for respiration");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对每个通道进行处理
|
||||||
|
for (auto& channel : channels) {
|
||||||
|
// 1. 0.1Hz高通滤波 (去除基线漂移)
|
||||||
|
channel = filter(channel, SAMPLE_RATE,0, 0.1,filtertype::highpass);
|
||||||
|
|
||||||
|
// 2. 50Hz陷波滤波 (去除工频干扰)
|
||||||
|
channel = adaptive_notch_filter(channel, SAMPLE_RATE, 50.0, 5.0);
|
||||||
|
|
||||||
|
// 3. 振幅归一化 (归一化到-1到1之间)
|
||||||
|
normalize_amplitude(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算并存储信号质量指数
|
||||||
|
processed.sqi = calculate_snr(channels[0]);
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
SensorData SignalProcessor::preprocess_snore(const SensorData& data)
|
SensorData SignalProcessor::preprocess_snore(const SensorData& data)
|
||||||
{
|
{
|
||||||
|
const double SAMPLE_RATE = 2000.0; // 鼾声信号标准采样率2000Hz
|
||||||
|
|
||||||
SensorData processed = data;
|
SensorData processed = data;
|
||||||
|
|
||||||
|
// 获取通道数据
|
||||||
|
auto& channel = std::get<std::vector<float>>(processed.channel_data);
|
||||||
|
|
||||||
|
// 1. 50-2000Hz带通滤波 (保留有效频段)
|
||||||
|
std::vector<float> filtered = bandpass_filter(channel, SAMPLE_RATE, 50.0, 2000.0);
|
||||||
|
|
||||||
|
// 2. 振幅归一化
|
||||||
|
normalize_amplitude(filtered);
|
||||||
|
|
||||||
|
processed.channel_data = filtered;
|
||||||
|
processed.sqi = calculate_snr(filtered);
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
SensorData SignalProcessor::preprocess_stethoscope(const SensorData& data)
|
SensorData SignalProcessor::preprocess_stethoscope(const SensorData& data)
|
||||||
{
|
{
|
||||||
|
const double SAMPLE_RATE = 4000.0; // 听诊信号标准采样率4000Hz
|
||||||
|
|
||||||
SensorData processed = data;
|
SensorData processed = data;
|
||||||
|
|
||||||
|
// 获取通道数据
|
||||||
|
auto& channels = std::get<std::vector<std::vector<float>>>(processed.channel_data);
|
||||||
|
if (channels.size() < 2) {
|
||||||
|
throw std::runtime_error("Invalid channel count for stethoscope");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对每个通道进行处理
|
||||||
|
for (auto& channel : channels) {
|
||||||
|
// 1. 20-2000Hz带通滤波 (保留有效频段)
|
||||||
|
channel = bandpass_filter(channel, SAMPLE_RATE, 20.0, 2000.0);
|
||||||
|
|
||||||
|
// 2. 振幅归一化
|
||||||
|
normalize_amplitude(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算并存储信号质量指数
|
||||||
|
float avg_sqi = 0.0f;
|
||||||
|
for (const auto& channel : channels) {
|
||||||
|
avg_sqi += calculate_snr(channel);
|
||||||
|
}
|
||||||
|
processed.sqi = avg_sqi / channels.size();
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
// 添加预处理辅助函数
|
// 添加预处理辅助函数
|
||||||
|
|
@ -233,6 +371,7 @@ std::vector<float> SignalProcessor::filter(const std::vector<float>& input,
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//低通滤波器
|
||||||
std::vector<float> SignalProcessor::Lowpass_filter(const std::vector<float>& input,
|
std::vector<float> SignalProcessor::Lowpass_filter(const std::vector<float>& input,
|
||||||
double sample_rate,
|
double sample_rate,
|
||||||
double low_cutoff){
|
double low_cutoff){
|
||||||
|
|
@ -249,6 +388,7 @@ std::vector<float> SignalProcessor::Lowpass_filter(const std::vector<float>& inp
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
//高通滤波器
|
||||||
std::vector<float> SignalProcessor::Highpass_filter(const std::vector<float>& input,
|
std::vector<float> SignalProcessor::Highpass_filter(const std::vector<float>& input,
|
||||||
double sample_rate,
|
double sample_rate,
|
||||||
double high_cutoff){
|
double high_cutoff){
|
||||||
|
|
@ -378,14 +518,36 @@ float SignalProcessor::calculate_PPG_sqi(const std::vector<float>& red_channel,
|
||||||
|
|
||||||
// 辅助函数:计算信号的信噪比(SNR)
|
// 辅助函数:计算信号的信噪比(SNR)
|
||||||
float SignalProcessor::calculate_snr(const std::vector<float>& signal) {
|
float SignalProcessor::calculate_snr(const std::vector<float>& signal) {
|
||||||
return 0.0f;
|
if (signal.size() < 2) return 0.0f;
|
||||||
|
|
||||||
|
// 计算信号功率
|
||||||
|
float signal_power = 0.0f;
|
||||||
|
for (float s : signal) {
|
||||||
|
signal_power += s * s;
|
||||||
|
}
|
||||||
|
signal_power /= signal.size();
|
||||||
|
|
||||||
|
// 计算噪声功率(通过差分近似)
|
||||||
|
float noise_power = 0.0f;
|
||||||
|
for (size_t i = 1; i < signal.size(); ++i) {
|
||||||
|
float diff = signal[i] - signal[i-1];
|
||||||
|
noise_power += diff * diff;
|
||||||
|
}
|
||||||
|
noise_power /= (signal.size() - 1);
|
||||||
|
|
||||||
|
// 计算SNR (dB)
|
||||||
|
if (noise_power < 1e-6f) return 1.0f; // 避免除以零
|
||||||
|
float snr_db = 10.0f * std::log10(signal_power / noise_power);
|
||||||
|
|
||||||
|
// 将SNR转换为0-1的质量指数
|
||||||
|
return std::clamp(snr_db / 40.0f, 0.0f, 1.0f); // 假设40dB为最大质量
|
||||||
}
|
}
|
||||||
// 辅助函数:计算两个信号的相关系数
|
// 辅助函数:计算两个信号的相关系数
|
||||||
float SignalProcessor::calculate_correlation(const std::vector<float>& x,
|
float SignalProcessor::calculate_correlation(const std::vector<float>& x,
|
||||||
const std::vector<float>& y) {
|
const std::vector<float>& y) {
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
// 在 signal_processor.cpp 文件中添加以下代码
|
//ecg sqi
|
||||||
float SignalProcessor::calculate_ecg_sqi(const std::vector<float>& signal, double sample_rate) {
|
float SignalProcessor::calculate_ecg_sqi(const std::vector<float>& signal, double sample_rate) {
|
||||||
// 1. 检查输入有效性
|
// 1. 检查输入有效性
|
||||||
if (signal.empty()) return 0.0f;
|
if (signal.empty()) return 0.0f;
|
||||||
|
|
@ -439,4 +601,24 @@ float SignalProcessor::calculate_ecg_sqi(const std::vector<float>& signal, doubl
|
||||||
|
|
||||||
// 确保在[0,1]范围内
|
// 确保在[0,1]范围内
|
||||||
return std::clamp(sqi, 0.0f, 1.0f);
|
return std::clamp(sqi, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
void SignalProcessor::normalize_amplitude(std::vector<float>& signal) {
|
||||||
|
if (signal.empty()) return;
|
||||||
|
|
||||||
|
// 找到最大绝对值
|
||||||
|
float max_val = 0.0f;
|
||||||
|
for (float value : signal) {
|
||||||
|
float abs_val = std::abs(value);
|
||||||
|
if (abs_val > max_val) {
|
||||||
|
max_val = abs_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 归一化处理
|
||||||
|
if (max_val > 0.0f) {
|
||||||
|
float scale = 1.0f / max_val;
|
||||||
|
for (float& value : signal) {
|
||||||
|
value *= scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue