diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..53f1e2c 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,6 +4,14 @@ diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/JNI_FIX_SUMMARY.md b/JNI_FIX_SUMMARY.md new file mode 100644 index 0000000..d99a981 --- /dev/null +++ b/JNI_FIX_SUMMARY.md @@ -0,0 +1,141 @@ +# JNI函数名不匹配问题修复说明 + +## 问题描述 + +在代码重构过程中,将原生方法从`MainActivity`移动到`DataManager`类后,出现了以下错误: + +``` +Cannot resolve corresponding JNI function Java_com_example_cmake_1project_1test_DataManager_createStreamParser +``` + +## 问题原因 + +### JNI函数命名规则 + +JNI函数名遵循以下格式: +``` +Java_{包名}_{类名}_{方法名} +``` + +其中: +- 包名中的点(.)用下划线(_)替换 +- 类名中的下划线用`_1`替换 + +### 具体变化 + +**重构前的JNI函数名**(在MainActivity中): +``` +Java_com_example_cmake_1project_1test_MainActivity_createStreamParser +Java_com_example_cmake_1project_1test_MainActivity_destroyStreamParser +Java_com_example_cmake_1project_1test_MainActivity_streamParserAppend +Java_com_example_cmake_1project_1test_MainActivity_streamParserDrainPackets +``` + +**重构后的JNI函数名**(在DataManager中): +``` +Java_com_example_cmake_1project_1test_DataManager_createStreamParser +Java_com_example_cmake_1project_1test_DataManager_destroyStreamParser +Java_com_example_cmake_1project_1test_DataManager_streamParserAppend +Java_com_example_cmake_1project_1test_DataManager_streamParserDrainPackets +``` + +## 解决方案 + +### 方案1:回调接口模式(已实现) + +通过创建回调接口,让`DataManager`通过`MainActivity`来调用原生方法,保持原有的JNI函数名。 + +#### 实现步骤 + +1. **创建回调接口** +```kotlin +interface NativeMethodCallback { + fun createStreamParser(): Long + fun destroyStreamParser(handle: Long) + fun streamParserAppend(handle: Long, chunk: ByteArray) + fun streamParserDrainPackets(handle: Long): List? +} +``` + +2. **修改DataManager构造函数** +```kotlin +class DataManager(private val nativeCallback: NativeMethodCallback) +``` + +3. **MainActivity实现接口** +```kotlin +class MainActivity : AppCompatActivity(), DataManager.NativeMethodCallback +``` + +4. **保持原生方法在MainActivity中** +```kotlin +// 原生方法声明 - 保持原来的JNI函数名 +external fun createStreamParser(): Long +external fun destroyStreamParser(handle: Long) +external fun streamParserAppend(handle: Long, chunk: ByteArray) +external fun streamParserDrainPackets(handle: Long): List? +``` + +#### 优势 +- 保持原有的JNI函数名,无需修改C++代码 +- 维持了代码重构的架构优势 +- 清晰的职责分离 + +#### 劣势 +- 增加了接口依赖 +- 稍微增加了代码复杂度 + +### 方案2:修改C++代码中的JNI函数名 + +如果您有权限修改C++代码,可以将C++中的JNI函数名改为新的名称。 + +#### 需要修改的C++函数名 +```cpp +// 原来的函数名 +JNIEXPORT jlong JNICALL Java_com_example_cmake_1project_1test_MainActivity_createStreamParser + +// 改为 +JNIEXPORT jlong JNICALL Java_com_example_cmake_1project_1test_DataManager_createStreamParser +``` + +#### 优势 +- 完全符合重构后的架构 +- 无需额外的接口层 + +#### 劣势 +- 需要修改C++代码 +- 可能影响其他依赖项目 + +## 推荐方案 + +**推荐使用方案1(回调接口模式)**,原因如下: + +1. **无需修改C++代码**:保持现有C++代码的稳定性 +2. **维持重构优势**:仍然保持了代码的职责分离 +3. **向后兼容**:如果将来需要,可以轻松切换到方案2 +4. **风险较低**:不会引入新的编译或链接问题 + +## 修复后的架构 + +``` +MainActivity (实现NativeMethodCallback) + ↓ +DataManager (通过回调调用原生方法) + ↓ +原生C++库 (保持原有JNI函数名) +``` + +## 注意事项 + +1. **JNI函数名一致性**:确保Kotlin中的`external`方法名与C++中的JNI函数名完全匹配 +2. **库加载**:确保在`MainActivity`的`companion object`中正确加载原生库 +3. **接口实现**:MainActivity必须实现`NativeMethodCallback`接口的所有方法 +4. **依赖注入**:DataManager的构造函数现在需要传入回调接口实例 + +## 验证修复 + +修复完成后,您应该能够: +1. 成功编译项目 +2. 不再看到JNI函数名不匹配的错误 +3. 保持原有的功能正常运行 +4. 享受重构后的代码结构优势 diff --git a/README_REFACTOR.md b/README_REFACTOR.md new file mode 100644 index 0000000..e5d7da7 --- /dev/null +++ b/README_REFACTOR.md @@ -0,0 +1,136 @@ +# 代码重构说明 + +## 重构目标 +将原本堆在一个MainActivity.kt文件中的所有逻辑分解为多个职责明确的Kotlin文件,提高代码的可读性和可维护性。 + +## 重构后的文件结构 + +### 1. **MainActivity.kt** - 主界面控制器 +**职责**: 主界面逻辑和生命周期管理 +- 初始化各个管理器 +- 处理按钮点击事件 +- 管理Activity生命周期 +- 协调各个管理器之间的交互 + +**主要方法**: +- `onCreate()`: 初始化UI和管理器 +- `onBleNotify()`: 蓝牙数据通知处理 +- `resetData()`: 重置数据 +- `reloadData()`: 重新加载数据 + +### 2. **Constants.kt** - 常量定义 +**职责**: 集中管理应用中的所有常量 +- UI更新间隔 +- 数据分块大小 +- 缓冲区管理阈值 +- 显示限制参数 + +**主要常量**: +- `UPDATE_INTERVAL`: UI更新间隔(500ms) +- `CHUNK_SIZE`: 数据分块大小(64字节) +- `BUFFER_CLEANUP_THRESHOLD`: 缓冲区清理阈值(50) +- `BUFFER_KEEP_COUNT`: 缓冲区保留数量(30) + +### 3. **DeviceTypeHelper.kt** - 设备类型工具 +**职责**: 设备类型相关的工具方法 +- 设备类型名称映射 +- 通道数据详情构建 + +**主要方法**: +- `getDeviceName()`: 根据数据类型获取设备名称 +- `buildChannelDetails()`: 构建通道数据详情字符串 + +### 4. **DataManager.kt** - 数据管理器 +**职责**: 数据解析、缓冲管理和原生方法调用 +- 管理流式解析器 +- 处理数据块 +- 管理数据缓冲区 +- 调用原生C++方法 + +**主要方法**: +- `ensureParser()`: 确保解析器已创建 +- `onBleNotify()`: 处理蓝牙通知数据块 +- `processFileData()`: 处理文件数据 +- `cleanupBuffer()`: 智能清理缓冲区 +- `resetData()`: 重置所有数据 + +**原生方法**: +- `createStreamParser()`: 创建流式解析器 +- `streamParserAppend()`: 向解析器追加数据 +- `streamParserDrainPackets()`: 从解析器拉取数据包 +- `destroyStreamParser()`: 销毁解析器 + +### 5. **UiManager.kt** - UI管理器 +**职责**: UI更新、统计信息构建和显示逻辑 +- 管理UI更新调度 +- 构建统计信息 +- 构建显示内容 +- 触发UI更新 + +**主要方法**: +- `scheduleUiUpdate()`: 计划UI更新,避免频繁刷新 +- `buildStatisticsString()`: 构建统计信息字符串 +- `buildDisplayContent()`: 构建完整的显示内容 +- `updateDisplay()`: 更新UI显示内容 + +### 6. **FileHelper.kt** - 文件帮助类 +**职责**: 文件读取操作 +- 从assets文件夹读取文件 + +**主要方法**: +- `readAssetFile()`: 读取assets文件到字节数组 + +## 重构优势 + +### 1. **职责分离** +- 每个类都有明确的单一职责 +- 降低了类之间的耦合度 +- 提高了代码的可测试性 + +### 2. **代码复用** +- 工具方法可以在多个地方复用 +- 减少了重复代码 +- 提高了开发效率 + +### 3. **维护性提升** +- 修改某个功能只需要修改对应的类 +- 代码结构更清晰,易于理解 +- 降低了修改的风险 + +### 4. **可扩展性** +- 新增功能可以创建新的类 +- 现有类可以独立扩展 +- 支持团队协作开发 + +## 数据流向 + +``` +MainActivity → DataManager → 原生解析器 + ↓ +UiManager ← DataManager ← 数据缓冲区 + ↓ + UI更新 +``` + +## 使用说明 + +1. **MainActivity**: 作为入口点,协调各个管理器 +2. **DataManager**: 处理所有数据相关的操作 +3. **UiManager**: 负责所有UI相关的更新 +4. **DeviceTypeHelper**: 提供设备类型相关的工具方法 +5. **Constants**: 集中管理所有常量 +6. **FileHelper**: 处理文件读取操作 + +## 注意事项 + +1. **原生方法**: 所有原生C++方法现在都在DataManager中声明 +2. **依赖关系**: 各个类之间有明确的依赖关系,避免循环依赖 +3. **线程安全**: UI更新始终在主线程进行,数据操作在后台线程进行 +4. **错误处理**: 每个类都有适当的错误处理和日志记录 + +## 后续优化建议 + +1. **依赖注入**: 可以考虑使用Dagger或Koin进行依赖注入 +2. **响应式编程**: 可以考虑使用RxJava或Flow进行数据流处理 +3. **单元测试**: 重构后的代码更容易进行单元测试 +4. **文档完善**: 可以添加更详细的API文档和示例代码 diff --git a/SIGNAL_PROCESSOR_README.md b/SIGNAL_PROCESSOR_README.md new file mode 100644 index 0000000..9732d20 --- /dev/null +++ b/SIGNAL_PROCESSOR_README.md @@ -0,0 +1,271 @@ +# 信号处理JNI封装使用说明 + +## 概述 + +本项目已将C++信号处理功能封装成JNI接口,可以在Android应用中直接调用。主要功能包括: + +- 各种数字滤波器(带通、低通、高通、陷波) +- 信号质量评估 +- ECG信号质量指数计算 +- 信号特征提取 +- 信号预处理(归一化、去直流等) +- 实时信号处理 + +## 文件结构 + +``` +app/src/main/ +├── cpp/ +│ ├── include/cpp/ +│ │ └── signal_processor.h # C++头文件 +│ ├── src/ +│ │ └── signal_processor.cpp # C++实现文件(需要修复乱码) +│ ├── jni/ +│ │ └── jni_bridge.cpp # 统一的JNI桥接文件(包含所有JNI函数) +│ └── CMakeLists.txt # 构建配置 +└── java/ + └── com/example/cmake_project_test/ + ├── SignalProcessorJNI.kt # Java JNI接口类 + └── SignalProcessorExample.kt # 使用示例类 +``` + +## 使用方法 + +### 1. 基本使用 + +```kotlin +// 创建信号处理器实例 +val signalProcessor = SignalProcessorJNI() +if (signalProcessor.createProcessor()) { + // 使用各种信号处理功能 + val filteredSignal = signalProcessor.bandpassFilter( + inputSignal, + sampleRate = 1000.0, + lowFreq = 40.0, + highFreq = 60.0 + ) + + // 清理资源 + signalProcessor.destroyProcessor() +} +``` + +### 2. 主要功能 + +#### 数字滤波 + +```kotlin +// 带通滤波 +val bandpassResult = signalProcessor.bandpassFilter( + signal, sampleRate, lowFreq, highFreq +) + +// 低通滤波 +val lowpassResult = signalProcessor.lowpassFilter( + signal, sampleRate, cutoffFreq +) + +// 高通滤波 +val highpassResult = signalProcessor.highpassFilter( + signal, sampleRate, cutoffFreq +) + +// 陷波滤波(去除工频干扰) +val notchResult = signalProcessor.notchFilter( + signal, sampleRate, notchFreq, qFactor +) +``` + +#### 信号质量评估 + +```kotlin +// 计算信号质量指数 +val quality = signalProcessor.calculateSignalQuality(signal) + +// 计算ECG信号质量指数 +val ecgSQI = signalProcessor.calculateECGSQI(ecgSignal, sampleRate) + +// 计算两个信号的相关性 +val correlation = signalProcessor.calculateCorrelation(signal1, signal2) +``` + +#### 信号预处理 + +```kotlin +// 归一化信号幅度 +signalProcessor.normalizeAmplitude(signal) + +// 提取信号特征 +val features = signalProcessor.extractFeatures(signal, sampleRate) + +// 重置滤波器状态 +signalProcessor.resetFilters() +``` + +#### 实时处理 + +```kotlin +// 实时处理数据块 +val processedChunk = signalProcessor.processRealtimeChunk( + chunk, sampleRate +) +``` + +### 3. 完整示例 + +参考 `SignalProcessorExample.kt` 文件,其中包含了所有功能的演示代码: + +```kotlin +val example = SignalProcessorExample() + +// 运行所有演示 +example.runAllDemonstrations() + +// 清理资源 +example.cleanup() +``` + +## 数据格式 + +### 输入数据 + +- 所有信号数据使用 `FloatArray` 格式 +- 采样率使用 `Double` 类型 +- 频率参数使用 `Double` 类型 + +### 输出数据 + +- 滤波结果返回 `FloatArray?`(可能为null表示处理失败) +- 质量指数返回 `Float` 类型(0.0-1.0) +- 相关性返回 `Float` 类型(-1.0到1.0) + +### 内部转换 + +- Java端使用 `ByteBuffer` 进行 `FloatArray` 和 `ByteArray` 的转换 +- C++端使用 `std::vector` 处理数据 +- 字节序使用小端序(Little Endian) + +## 注意事项 + +### 1. 资源管理 + +```kotlin +// 必须在使用前创建处理器 +if (!signalProcessor.createProcessor()) { + // 处理创建失败的情况 + return +} + +// 使用完毕后必须销毁处理器 +signalProcessor.destroyProcessor() +``` + +### 2. 错误处理 + +```kotlin +val result = signalProcessor.bandpassFilter(signal, sampleRate, lowFreq, highFreq) +if (result != null) { + // 处理成功 + processResult(result) +} else { + // 处理失败 + Log.e("SignalProcessor", "滤波处理失败") +} +``` + +### 3. 性能考虑 + +- 滤波器初始化有一定开销,建议复用处理器实例 +- 大数据量处理时考虑分块处理 +- 实时处理时注意内存分配 + +### 4. 线程安全 + +- JNI函数不是线程安全的 +- 多线程使用时需要适当的同步机制 +- 建议在主线程或专用工作线程中使用 + +## 构建配置 + +### CMakeLists.txt + +确保在 `CMakeLists.txt` 中包含了信号处理库: + +```cmake +# Signal processor static library +add_library(signal_processor STATIC + src/signal_processor.cpp) + +target_include_directories(signal_processor PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +# 链接到主库 +target_link_libraries(${CMAKE_PROJECT_NAME} + core_math + data_parser + signal_processor + android + log) +``` + +**注意**:信号处理的JNI函数现在已整合到 `jni_bridge.cpp` 中,不再需要单独的 `signal_processor_jni.cpp` 文件。 + +### 库加载 + +在Java代码中确保正确加载原生库: + +```kotlin +companion object { + init { + System.loadLibrary("cmake_project_test") + } +} +``` + +## 故障排除 + +### 1. 编译错误 + +- 检查 `CMakeLists.txt` 配置 +- 确保所有源文件路径正确 +- 检查头文件包含路径 + +### 2. 运行时错误 + +- 检查Logcat中的错误信息 +- 确保处理器已正确创建 +- 检查输入数据格式和大小 + +### 3. 性能问题 + +- 使用Android Profiler分析性能瓶颈 +- 考虑减少数据转换开销 +- 优化滤波器参数 + +## 扩展功能 + +### 1. 添加新的滤波器 + +1. 在 `signal_processor.h` 中声明新方法 +2. 在 `signal_processor.cpp` 中实现 +3. 在 `signal_processor_jni.cpp` 中添加JNI封装 +4. 在 `SignalProcessorJNI.kt` 中添加Java接口 + +### 2. 自定义信号处理 + +可以基于现有的JNI接口构建更高级的信号处理功能: + +```kotlin +class AdvancedSignalProcessor(private val jni: SignalProcessorJNI) { + fun adaptiveFilter(signal: FloatArray, sampleRate: Double): FloatArray? { + // 实现自适应滤波算法 + // 使用JNI接口调用底层功能 + } +} +``` + +## 总结 + +通过JNI封装,您现在可以在Android应用中直接使用C++的高性能信号处理功能。这种架构既保持了C++的性能优势,又提供了Java/Kotlin的易用性。 + +建议在使用前先运行示例代码,熟悉各种功能的使用方法,然后根据具体需求进行定制化开发。 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b2584ef..dd61251 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -32,11 +32,11 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "11" + jvmTarget = "17" } externalNativeBuild { cmake { diff --git a/app/src/main/assets/ecg_data_raw.dat b/app/src/main/assets/ecg_data_raw.dat new file mode 100644 index 0000000..00e9d9f Binary files /dev/null and b/app/src/main/assets/ecg_data_raw.dat differ diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 0989916..e5fa74b 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -39,6 +39,27 @@ add_library(data_parser STATIC target_include_directories(data_parser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +# Signal processor static library +add_library(signal_processor STATIC + src/signal_processor.cpp) + +target_include_directories(signal_processor PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +# Data mapper static library +add_library(data_mapper STATIC + src/data_mapper.cpp) + +target_include_directories(data_mapper PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +# Indicator calculator static library +add_library(indicator_calculator STATIC + src/indicator_cal.cpp) + +target_include_directories(indicator_calculator PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + # JNI shared library: expose only JNI bridge, link against core add_library(${CMAKE_PROJECT_NAME} SHARED jni/jni_bridge.cpp) @@ -49,5 +70,8 @@ add_library(${CMAKE_PROJECT_NAME} SHARED target_link_libraries(${CMAKE_PROJECT_NAME} core_math data_parser + signal_processor + data_mapper + indicator_calculator android log) \ No newline at end of file diff --git a/app/src/main/cpp/include/cpp/data_mapper.h b/app/src/main/cpp/include/cpp/data_mapper.h new file mode 100644 index 0000000..3af87a9 --- /dev/null +++ b/app/src/main/cpp/include/cpp/data_mapper.h @@ -0,0 +1,58 @@ +#ifndef _DATA_MAPPER_H +#define _DATA_MAPPER_H + +#include "cpp/data_praser.h" +#include +#include +#include +#include +#include +#include + +// 数据映射器类 +class Mapper +{ +private: + // 私有成员变量(如果需要的话) + +public: + // 构造函数和析构函数 + Mapper(); + ~Mapper(); + + // 主要映射函数 + SensorData DataMapper(SensorData& data); + + // 各种设备类型的映射函数 + SensorData EEG_Data_Mapper(SensorData& data); + SensorData ECG_2LEAD_Data_Mapper(SensorData& data); + SensorData ECG_12LEAD_Data_Mapper(SensorData& data); + SensorData PPG_Data_Mapper(SensorData& data); + SensorData Respiration_Data_Mapper(SensorData& data); + SensorData Snore_Data_Mapper(SensorData& data); + SensorData Stethoscope_Data_Mapper(SensorData& data); +}; + +// JNI函数声明 +extern "C" { + // 创建数据映射器实例 + JNIEXPORT jlong JNICALL Java_com_example_cmake_1project_1test_DataMapper_createDataMapper(JNIEnv* env, jobject thiz); + + // 销毁数据映射器实例 + JNIEXPORT void JNICALL Java_com_example_cmake_1project_1test_DataMapper_destroyDataMapper(JNIEnv* env, jobject thiz, jlong mapperHandle); + + // 映射单个数据包 + JNIEXPORT jobject JNICALL Java_com_example_cmake_1project_1test_DataMapper_mapSensorData(JNIEnv* env, jobject thiz, jlong mapperHandle, jobject sensorData); + + // 批量映射数据包 + JNIEXPORT jobject JNICALL Java_com_example_cmake_1project_1test_DataMapper_mapSensorDataList(JNIEnv* env, jobject thiz, jlong mapperHandle, jobject sensorDataList); +} + +// 辅助函数声明 +jobject convertSensorDataToJava(JNIEnv* env, const SensorData& sensorData); +std::vector convertJavaSensorDataListToVector(JNIEnv* env, jobject sensorDataList); +jobject convertSensorDataVectorToJavaList(JNIEnv* env, const std::vector& sensorDataVector); +SensorData convertJavaSensorDataToCpp(JNIEnv* env, jobject javaSensorData); +std::vector convertJavaFloatListToVector(JNIEnv* env, jobject floatList); + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/include/cpp/data_praser.h b/app/src/main/cpp/include/cpp/data_praser.h index 549912e..b075354 100644 --- a/app/src/main/cpp/include/cpp/data_praser.h +++ b/app/src/main/cpp/include/cpp/data_praser.h @@ -87,6 +87,9 @@ SensorData parse_respiration(const uint8_t* data); // 统一解析入口函数 - 支持多个包头和数据包格式 std::vector parse_device_data(const std::vector& file_data); +// CRC16校验函数 +uint16_t calculate_crc16(const uint8_t* data, size_t length); + // 工具函数:将数值转换为十六进制字符串 std::string to_hex_string(uint16_t value); diff --git a/app/src/main/cpp/include/cpp/indicator_cal.h b/app/src/main/cpp/include/cpp/indicator_cal.h new file mode 100644 index 0000000..2bd7955 --- /dev/null +++ b/app/src/main/cpp/include/cpp/indicator_cal.h @@ -0,0 +1,74 @@ +#ifndef _INDICATOR_CAL_H +#define _INDICATOR_CAL_H + +#include "cpp/data_praser.h" +#include +#include +#include +#include +#include +#include + +// 指标计算器类 +class MetricsCalculator +{ +public: + // 构造函数和析构函数 + MetricsCalculator(); + ~MetricsCalculator(); + + // ECG相关指标 + float calculate_heart_rate_ecg(const SensorData& ecg_signal, float sample_rate); + float calculate_t_wave_amplitude(const std::vector& ecg_signal); + float calculate_qrs_width(const std::vector& ecg_signal, float sample_rate); + float calculate_st_offset(const std::vector& ecg_signal, float sample_rate); + + // PPG相关指标 + float calculate_heart_rate_ppg(const std::vector& ppg_signal, float sample_rate); + float calculate_spo2(const SensorData& ppg_data); + float calculate_perfusion_index(const SensorData& ppg_data); + float calculate_pulse_width(const std::vector& ppg_signal); + float calculate_amplitude_ratio(const SensorData& ppg_data); + + // HRV相关指标 + float calculate_sdnn(const std::vector& rr_intervals); + float calculate_rmssd(const std::vector& rr_intervals); + float calculate_pnn50(const std::vector& rr_intervals); + float calculate_triangular_index(const std::vector& rr_intervals); + + // 信号处理 + std::vector detect_r_peaks(const std::vector& ecg_signal, float sample_rate); + std::vector detect_pulse_peaks(const std::vector& ppg_signal, float sample_rate); + float calculate_signal_quality(const std::vector& signal); + + // 综合指标计算 + std::map calculate_all_ecg_metrics(const SensorData& ecg_data, float sample_rate); + std::map calculate_all_ppg_metrics(const SensorData& ppg_data, float sample_rate); + std::map calculate_all_hrv_metrics(const std::vector& rr_intervals); + + // 完整数据处理流程 + std::map process_complete_pipeline(const SensorData& raw_data, float sample_rate); +}; + +// JNI函数声明 +extern "C" { + // 创建指标计算器实例 + JNIEXPORT jlong JNICALL Java_com_example_cmake_1project_1test_IndicatorCalculator_createIndicatorCalculator(JNIEnv* env, jobject thiz); + + // 销毁指标计算器实例 + JNIEXPORT void JNICALL Java_com_example_cmake_1project_1test_IndicatorCalculator_destroyIndicatorCalculator(JNIEnv* env, jobject thiz, jlong calculatorHandle); + + // 完整数据处理流程 + JNIEXPORT jobject JNICALL Java_com_example_cmake_1project_1test_IndicatorCalculator_processCompletePipeline(JNIEnv* env, jobject thiz, jlong calculatorHandle, jobject sensorData, jfloat sampleRate); + + // 单独指标计算 + JNIEXPORT jobject JNICALL Java_com_example_cmake_1project_1test_IndicatorCalculator_calculateECGMetrics(JNIEnv* env, jobject thiz, jlong calculatorHandle, jobject sensorData, jfloat sampleRate); + JNIEXPORT jobject JNICALL Java_com_example_cmake_1project_1test_IndicatorCalculator_calculatePPGMetrics(JNIEnv* env, jobject thiz, jlong calculatorHandle, jobject sensorData, jfloat sampleRate); + JNIEXPORT jobject JNICALL Java_com_example_cmake_1project_1test_IndicatorCalculator_calculateHRVMetrics(JNIEnv* env, jobject thiz, jlong calculatorHandle, jobject rrIntervals); +} + +// 辅助函数声明 +jobject convertMetricsMapToJava(JNIEnv* env, const std::map& metrics); +std::vector convertJavaFloatListToVector(JNIEnv* env, jobject floatList); + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/include/cpp/signal_processor.h b/app/src/main/cpp/include/cpp/signal_processor.h new file mode 100644 index 0000000..2163c7e --- /dev/null +++ b/app/src/main/cpp/include/cpp/signal_processor.h @@ -0,0 +1,179 @@ +#ifndef _SIGNAL_PROCESSOR_H_ +#define _SIGNAL_PROCESSOR_H_ +#include "data_praser.h" +#include +#include +#include +#include +// 滤波类型 +enum class filtertype +{ + lowpass,highpass,notchpass,bandpass,bandstop +}; +// 特征数据结构 +struct ECGChannelFeatures { + std::vector r_peaks; // R波位置 + float sdnn = 0.0f; // RR间期标准差 + float rmssd = 0.0f; // RR间期差值的均方根 +}; + +struct FeatureSet { + std::vector ecg_features; + // 其他设备的特征结构... +}; + +// 生理指标数据结构 +struct ECGIndicators { + float heart_rate = 0.0f; // 心率 (bpm) + float sdnn = 0.0f; // 心率变异性指标 + float rmssd = 0.0f; // 心率变异性指标 + float st_elevation = 0.0f; // ST段抬高 (mV) + float st_depression = 0.0f; // ST段压低 (mV) +}; + +struct PPGIndicators { + float spo2 = 0.0f; // 血氧饱和度 + float pi = 0.0f; // 灌注指数 + float pulse_rate = 0.0f; // 脉率 +}; + +struct EEGIndicators { + float alpha_power = 0.0f; // Alpha波功率 + float beta_power = 0.0f; // Beta波功率 + float attention = 0.0f; // 注意力指数 +}; + +struct IndicatorSet { + float sqi = 0.0f; // 信号质量指数 (0.0-1.0) + std::vector ecg_indicators; + std::vector ppg_indicators; + std::vector eeg_indicators; +}; + +// 滤波器配置 +struct FilterConfig { + std::string type; + double cutoff_frequency = 0.0; + double Q_factor = 0.0; + double low_cutoff = 0.0; + double high_cutoff = 0.0; +}; + +class SignalProcessor { +public: + // 预处理信号 + SensorData preprocess_signals(const SensorData& raw_data); // 使用DataType枚举 + SensorData preprocess_generic(const SensorData& data); + // 新增:简化版通道级滤波处理(测试版本) + std::vector process_channel_based_filtering_simple( + const std::vector& data_packets); + + // 特征提取 + FeatureSet extract_signal_features(const SensorData& processed_data, + const std::vector& features = {}); + + // 计算生理指标 + IndicatorSet compute_physiological_indicators(const FeatureSet& features, + const std::vector& indicators = {}); + + // 获取处理流水线配置 + std::string getProcessingPipeline(DataType device_type); // 使用DataType枚举 + + // 设置滤波器参数 + void setFilterParams(const std::string& filter_type, const FilterConfig& params); + + + // 修改为public访问权限 + public: + SensorData preprocess_ecg_12lead(const SensorData& data); + SensorData preprocess_eeg(const SensorData& data); + SensorData preprocess_ecg_2lead(const SensorData& data); + SensorData preprocess_ppg(const SensorData& data); + SensorData preprocess_respiration(const SensorData& data); + SensorData preprocess_snore(const SensorData& data); + SensorData preprocess_stethoscope(const SensorData& data); + + // 设备特定的特征提取 + FeatureSet extract_eeg_features(const SensorData& data); + FeatureSet extract_ecg_features(const SensorData& data); + FeatureSet extract_ppg_features(const SensorData& data); + FeatureSet extract_respiration_features(const SensorData& data); + FeatureSet extract_snore_features(const SensorData& data); + FeatureSet extract_stethoscope_features(const SensorData& data); + + // 滤波器配置 + std::map filter_configs_; + + // 滤波器状态 + struct FilterState { + std::vector x_history; + std::vector y_history; + }; + std::map filter_states_; + + // 在SignalProcessor类中添加 +public: +// 带通滤波器实现 + std::vector bandpass_filter(const std::vector& input, + double sample_rate, + double low_cutoff, + double high_cutoff); + std::vector Lowpass_filter(const std::vector& input, + double sample_rate, + double low_cutoff) ; + + std::vector filter(const std::vector& input, + double sample_rate, + double low_cutoff, + double high_cutoff, + filtertype type); + std::vector Highpass_filter(const std::vector& input, + double sample_rate, + double high_cutoff); + std::vector compensate_motion_artifact(const std::vector& ppg, + const std::vector& motion); + std::vector compensate_eog_artifact(const std::vector& eeg, + const std::vector& eog1, + const std::vector& eog2) ; + // 添加自适应陷波滤波器声明 + std::vector adaptive_notch_filter(const std::vector& input, + double sample_rate, + double target_freq, + double bandwidth) ; + std::vector bandstop_filter(const std::vector& input, + double sample_rate, + double low_cutoff, + double high_cutoff); + void normalize_amplitude(std::vector& signal) ; + + // 添加通用预处理辅助函数 + std::vector remove_dc_offset(const std::vector& signal); + std::vector apply_gain(const std::vector& signal, float gain); + float calculate_correlation(const std::vector& x, const std::vector& y); + float calculate_snr(const std::vector& signal); + float calculate_PPG_sqi(const std::vector& red_channel, + const std::vector& ir_channel); + float calculate_ecg_sqi(const std::vector& signal, double sample_rate); + std::vector remove_motion_artifacts(const std::vector& signal, double sample_rate); + + // 新增:通道级滤波辅助方法 +private: + std::vector> apply_channel_filters( + const std::vector>& channels, DataType data_type); + std::vector> apply_eeg_filters( + const std::vector>& channels); + std::vector> apply_ecg_filters( + const std::vector>& channels); + std::vector> apply_ppg_filters( + const std::vector>& channels); + std::vector> apply_respiration_filters( + const std::vector>& channels); + std::vector> apply_snore_filters( + const std::vector>& channels); + std::vector> apply_stethoscope_filters( + const std::vector>& channels); +}; + + +#endif + \ No newline at end of file diff --git a/app/src/main/cpp/jni/jni_bridge.cpp b/app/src/main/cpp/jni/jni_bridge.cpp index 8890820..6464b3e 100644 --- a/app/src/main/cpp/jni/jni_bridge.cpp +++ b/app/src/main/cpp/jni/jni_bridge.cpp @@ -1,10 +1,15 @@ -#include + #include #include #include #include #include +#include +#include #include "../include/cpp/add.h" #include "../include/cpp/data_praser.h" +#include "../include/cpp/signal_processor.h" +#include "../include/cpp/data_mapper.h" +#include "../include/cpp/indicator_cal.h" using core::math::add; @@ -88,8 +93,12 @@ Java_com_example_cmake_1project_1test_MainActivity_destroyStreamParser( jobject /* this */, jlong handle) { (void)env; + if (handle == 0) return; // 避免删除空指针 + auto* parser = reinterpret_cast(handle); - delete parser; + if (parser) { + delete parser; + } } // 追加一段字节流数据 @@ -99,8 +108,10 @@ Java_com_example_cmake_1project_1test_MainActivity_streamParserAppend( jobject /* this */, jlong handle, jbyteArray chunk) { + if (handle == 0 || !chunk) return; // 检查句柄和输入 + auto* parser = reinterpret_cast(handle); - if (!parser || !chunk) return; + if (!parser) return; jsize len = env->GetArrayLength(chunk); if (len <= 0) return; @@ -115,19 +126,323 @@ Java_com_example_cmake_1project_1test_MainActivity_streamParserDrainPackets( JNIEnv* env, jobject /* this */, jlong handle) { + if (handle == 0) return nullptr; // 检查句柄 + auto* parser = reinterpret_cast(handle); if (!parser) return nullptr; - std::vector packets = parser->getAllPackets(); - return convertSensorDataVectorToJavaList(env, packets); + + try { + std::vector packets = parser->getAllPackets(); + return convertSensorDataVectorToJavaList(env, packets); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "StreamParser", "Exception in drainPackets: %s", e.what()); + return nullptr; + } catch (...) { + __android_log_print(ANDROID_LOG_ERROR, "StreamParser", "Unknown exception in drainPackets"); + return nullptr; + } +} + + +// ================= 信号处理 JNI 接口 ================= + +// 全局变量存储SignalProcessor实例 +static std::map> processor_instances; +static jlong next_processor_id = 1; + +// 辅助函数:将Java字节数组转换为C++向量 +std::vector java_byte_array_to_vector(JNIEnv* env, jbyteArray java_array) { + if (java_array == nullptr) return {}; + + jsize length = env->GetArrayLength(java_array); + jbyte* bytes = env->GetByteArrayElements(java_array, nullptr); + + std::vector result; + result.reserve(length / sizeof(float)); + + for (int i = 0; i < length; i += sizeof(float)) { + float value; + memcpy(&value, &bytes[i], sizeof(float)); + result.push_back(value); + } + + env->ReleaseByteArrayElements(java_array, bytes, JNI_ABORT); + return result; +} + +// 辅助函数:将C++向量转换为Java字节数组 +jbyteArray vector_to_java_byte_array(JNIEnv* env, const std::vector& vec) { + jsize length = vec.size() * sizeof(float); + jbyteArray result = env->NewByteArray(length); + + if (result != nullptr) { + jbyte* bytes = env->GetByteArrayElements(result, nullptr); + for (size_t i = 0; i < vec.size(); ++i) { + memcpy(&bytes[i * sizeof(float)], &vec[i], sizeof(float)); + } + env->ReleaseByteArrayElements(result, bytes, 0); + } + + return result; +} + +// JNI函数:创建SignalProcessor实例 +extern "C" JNIEXPORT jlong JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_createSignalProcessor(JNIEnv* env, jobject thiz) { + try { + auto processor = std::make_unique(); + jlong id = next_processor_id++; + processor_instances[id] = std::move(processor); + return id; + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Failed to create processor: %s", e.what()); + return -1; + } +} + +// JNI函数:销毁SignalProcessor实例 +extern "C" JNIEXPORT void JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_destroySignalProcessor(JNIEnv* env, jobject thiz, jlong processor_id) { + auto it = processor_instances.find(processor_id); + if (it != processor_instances.end()) { + processor_instances.erase(it); + } +} + +// JNI函数:带通滤波 +extern "C" JNIEXPORT jbyteArray JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_bandpassFilter( + JNIEnv* env, jobject thiz, jlong processor_id, + jbyteArray signal, jdouble sample_rate, jdouble low_freq, jdouble high_freq) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return nullptr; + + try { + std::vector signal_vec = java_byte_array_to_vector(env, signal); + std::vector result = it->second->bandpass_filter(signal_vec, sample_rate, low_freq, high_freq); + return vector_to_java_byte_array(env, result); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Bandpass filter error: %s", e.what()); + return nullptr; + } +} + +// JNI函数:低通滤波 +extern "C" JNIEXPORT jbyteArray JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_lowpassFilter( + JNIEnv* env, jobject thiz, jlong processor_id, + jbyteArray signal, jdouble sample_rate, jdouble cutoff_freq) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return nullptr; + + try { + std::vector signal_vec = java_byte_array_to_vector(env, signal); + std::vector result = it->second->Lowpass_filter(signal_vec, sample_rate, cutoff_freq); + return vector_to_java_byte_array(env, result); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Lowpass filter error: %s", e.what()); + return nullptr; + } +} + +// JNI函数:高通滤波 +extern "C" JNIEXPORT jbyteArray JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_highpassFilter( + JNIEnv* env, jobject thiz, jlong processor_id, + jbyteArray signal, jdouble sample_rate, jdouble cutoff_freq) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return nullptr; + + try { + std::vector signal_vec = java_byte_array_to_vector(env, signal); + std::vector result = it->second->Highpass_filter(signal_vec, sample_rate, cutoff_freq); + return vector_to_java_byte_array(env, result); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Highpass filter error: %s", e.what()); + return nullptr; + } +} + +// JNI函数:陷波滤波 +extern "C" JNIEXPORT jbyteArray JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_notchFilter( + JNIEnv* env, jobject thiz, jlong processor_id, + jbyteArray signal, jdouble sample_rate, jdouble notch_freq, jdouble q_factor) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return nullptr; + + try { + std::vector signal_vec = java_byte_array_to_vector(env, signal); + // 使用自适应陷波滤波器 + std::vector result = it->second->adaptive_notch_filter(signal_vec, sample_rate, notch_freq, q_factor); + return vector_to_java_byte_array(env, result); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Notch filter error: %s", e.what()); + return nullptr; + } +} + +// JNI函数:计算信号质量 +extern "C" JNIEXPORT jfloat JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_calculateSignalQuality( + JNIEnv* env, jobject thiz, jlong processor_id, jbyteArray signal) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return 0.0f; + + try { + std::vector signal_vec = java_byte_array_to_vector(env, signal); + // 使用SNR作为信号质量指标 + return it->second->calculate_snr(signal_vec); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Signal quality calculation error: %s", e.what()); + return 0.0f; + } +} + +// JNI函数:计算ECG信号质量指数 +extern "C" JNIEXPORT jfloat JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_calculateECGSQI( + JNIEnv* env, jobject thiz, jlong processor_id, + jbyteArray signal, jdouble sample_rate) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return 0.0f; + + try { + std::vector signal_vec = java_byte_array_to_vector(env, signal); + return it->second->calculate_ecg_sqi(signal_vec, sample_rate); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "ECG SQI calculation error: %s", e.what()); + return 0.0f; + } +} + +// JNI函数:计算相关性 +extern "C" JNIEXPORT jfloat JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_calculateCorrelation( + JNIEnv* env, jobject thiz, jlong processor_id, + jbyteArray x, jbyteArray y) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return 0.0f; + + try { + std::vector x_vec = java_byte_array_to_vector(env, x); + std::vector y_vec = java_byte_array_to_vector(env, y); + return it->second->calculate_correlation(x_vec, y_vec); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Correlation calculation error: %s", e.what()); + return 0.0f; + } +} + +// JNI函数:归一化幅度 +extern "C" JNIEXPORT void JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_normalizeAmplitude( + JNIEnv* env, jobject thiz, jlong processor_id, jbyteArray signal) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return; + + try { + std::vector signal_vec = java_byte_array_to_vector(env, signal); + it->second->normalize_amplitude(signal_vec); + + // 将处理后的数据写回Java数组 + jsize length = env->GetArrayLength(signal); + jbyte* bytes = env->GetByteArrayElements(signal, nullptr); + for (size_t i = 0; i < signal_vec.size(); ++i) { + memcpy(&bytes[i * sizeof(float)], &signal_vec[i], sizeof(float)); + } + env->ReleaseByteArrayElements(signal, bytes, 0); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Normalize amplitude error: %s", e.what()); + } +} + +// JNI函数:提取特征 +extern "C" JNIEXPORT jbyteArray JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_extractFeatures( + JNIEnv* env, jobject thiz, jlong processor_id, + jbyteArray signal, jdouble sample_rate) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return nullptr; + + try { + std::vector signal_vec = java_byte_array_to_vector(env, signal); + // 创建临时的SensorData对象来调用特征提取 + SensorData temp_data; + temp_data.data_type = DataType::EEG; // 默认类型 + temp_data.channel_data = std::vector>{signal_vec}; + + FeatureSet features = it->second->extract_signal_features(temp_data); + + // 将特征转换为float数组(简化处理) + std::vector feature_vector; + // 这里可以根据实际需要提取具体的特征值 + // 暂时返回原始信号作为特征 + feature_vector = signal_vec; + + return vector_to_java_byte_array(env, feature_vector); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Feature extraction error: %s", e.what()); + return nullptr; + } +} + +// JNI函数:重置滤波器 +extern "C" JNIEXPORT void JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_resetFilters( + JNIEnv* env, jobject thiz, jlong processor_id) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return; + + try { + // 清除滤波器状态 + it->second->filter_states_.clear(); + __android_log_print(ANDROID_LOG_INFO, "SignalProcessor", "Filters reset successfully"); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Reset filters error: %s", e.what()); + } +} + +// JNI函数:实时处理数据块 +extern "C" JNIEXPORT jbyteArray JNICALL +Java_com_example_cmake_1project_1test_SignalProcessorJNI_processRealtimeChunk( + JNIEnv* env, jobject thiz, jlong processor_id, + jbyteArray chunk, jdouble sample_rate) { + + auto it = processor_instances.find(processor_id); + if (it == processor_instances.end()) return nullptr; + + try { + std::vector chunk_vec = java_byte_array_to_vector(env, chunk); + + // 应用基本的预处理 + std::vector processed = it->second->remove_dc_offset(chunk_vec); + processed = it->second->remove_motion_artifacts(processed, sample_rate); + + return vector_to_java_byte_array(env, processed); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "SignalProcessor", "Real-time processing error: %s", e.what()); + return nullptr; + } } // 便捷方法:从整块数据按固定块大小模拟流式解析,返回解析到的所有包 extern "C" JNIEXPORT jobject JNICALL Java_com_example_cmake_1project_1test_MainActivity_parseStreamFromBytes( - JNIEnv* env, - jobject /* this */, - jbyteArray data, - jint chunkSize) { + JNIEnv* env, + jobject /* this */, + jbyteArray data, + jint chunkSize) { if (data == nullptr || chunkSize <= 0) return nullptr; std::vector src = convertJavaByteArrayToVector(env, data); @@ -143,4 +458,512 @@ Java_com_example_cmake_1project_1test_MainActivity_parseStreamFromBytes( return convertSensorDataVectorToJavaList(env, packets); } +// ============================================================================ +// DataMapper JNI函数实现 +// ============================================================================ + +// 创建数据映射器实例 +extern "C" JNIEXPORT jlong JNICALL +Java_com_example_cmake_1project_1test_DataMapper_createDataMapper( + JNIEnv* env, jobject thiz) { + try { + Mapper* mapper = new Mapper(); + jlong mapperId = reinterpret_cast(mapper); + + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "Created DataMapper instance with ID: %lld", mapperId); + return mapperId; + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Failed to create DataMapper: %s", e.what()); + return 0; + } +} + +// 销毁数据映射器实例 +extern "C" JNIEXPORT void JNICALL +Java_com_example_cmake_1project_1test_DataMapper_destroyDataMapper( + JNIEnv* env, jobject thiz, jlong mapperHandle) { + try { + if (mapperHandle != 0) { + Mapper* mapper = reinterpret_cast(mapperHandle); + delete mapper; + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "Destroyed DataMapper instance with ID: %lld", mapperHandle); + } else { + __android_log_print(ANDROID_LOG_WARN, "DataMapper", "Attempted to destroy null DataMapper handle"); + } + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Failed to destroy DataMapper: %s", e.what()); + } +} + +// 映射单个数据包 +extern "C" JNIEXPORT jobject JNICALL +Java_com_example_cmake_1project_1test_DataMapper_mapSensorData( + JNIEnv* env, jobject thiz, jlong mapperHandle, jobject sensorData) { + try { + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "=== JNI mapSensorData被调用 ==="); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "mapperHandle: %ld", mapperHandle); + + if (mapperHandle == 0) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Invalid mapper handle: 0"); + return nullptr; + } + + Mapper* mapper = reinterpret_cast(mapperHandle); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "Mapper指针获取成功"); + + // 将Java的SensorData转换为C++的SensorData + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "开始转换Java SensorData到C++"); + SensorData cppSensorData = convertJavaSensorDataToCpp(env, sensorData); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "Java到C++转换完成"); + + // 执行映射 + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "开始调用C++ DataMapper函数"); + SensorData mappedData = mapper->DataMapper(cppSensorData); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "C++ DataMapper函数调用完成"); + + // 将C++的SensorData转换回Java对象 + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "开始转换C++ SensorData到Java"); + jobject result = convertSensorDataToJava(env, mappedData); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "C++到Java转换完成"); + + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "Successfully mapped sensor data"); + return result; + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Failed to map sensor data: %s", e.what()); + return nullptr; + } +} + +// 批量映射数据包 +extern "C" JNIEXPORT jobject JNICALL +Java_com_example_cmake_1project_1test_DataMapper_mapSensorDataList( + JNIEnv* env, jobject thiz, jlong mapperHandle, jobject sensorDataList) { + try { + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "=== JNI mapSensorDataList被调用 ==="); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "mapperHandle: %lld", mapperHandle); + + if (mapperHandle == 0) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Invalid mapper handle: 0"); + return nullptr; + } + + Mapper* mapper = reinterpret_cast(mapperHandle); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "Mapper指针获取成功"); + + // 将Java的List转换为C++的vector + std::vector cppSensorDataList = convertJavaSensorDataListToVector(env, sensorDataList); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "转换后的数据包数量: %zu", cppSensorDataList.size()); + + // 批量映射 + std::vector mappedDataList; + mappedDataList.reserve(cppSensorDataList.size()); + + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "开始批量映射,数据包数量: %zu", cppSensorDataList.size()); + + for (size_t i = 0; i < cppSensorDataList.size(); ++i) { + try { + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "处理第 %zu 个数据包", i + 1); + SensorData mappedData = mapper->DataMapper(cppSensorDataList[i]); + mappedDataList.push_back(mappedData); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "第 %zu 个数据包映射完成", i + 1); + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_WARN, "DataMapper", "Failed to map individual sensor data: %s", e.what()); + // 如果单个数据包映射失败,保留原始数据 + mappedDataList.push_back(cppSensorDataList[i]); + } + } + + // 将C++的vector转换回Java的List + jobject result = convertSensorDataVectorToJavaList(env, mappedDataList); + + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "Successfully mapped %zu sensor data packets", mappedDataList.size()); + return result; + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Failed to map sensor data list: %s", e.what()); + return nullptr; + } +} + +// ============================================================================ +// 辅助函数实现 +// ============================================================================ + +// 将Java的SensorData转换为C++的SensorData +SensorData convertJavaSensorDataToCpp(JNIEnv* env, jobject javaSensorData) { + SensorData cppSensorData; + + // 获取Java类的引用 + jclass sensorDataClass = env->GetObjectClass(javaSensorData); + + // 获取字段ID + jfieldID dataTypeField = env->GetFieldID(sensorDataClass, "dataType", "Ltype/SensorData$DataType;"); + jfieldID packetSnField = env->GetFieldID(sensorDataClass, "packetSn", "I"); + jfieldID leadStatusField = env->GetFieldID(sensorDataClass, "leadStatus", "[B"); + jfieldID timestampField = env->GetFieldID(sensorDataClass, "timestamp", "J"); + jfieldID channelDataField = env->GetFieldID(sensorDataClass, "channelData", "Ljava/util/List;"); + jfieldID additionalDataField = env->GetFieldID(sensorDataClass, "additionalData", "Ltype/SensorData$AdditionalData;"); + jfieldID rawDataField = env->GetFieldID(sensorDataClass, "rawData", "[B"); + + // 读取基本字段 + cppSensorData.packet_sn = env->GetIntField(javaSensorData, packetSnField); + cppSensorData.timestamp = env->GetLongField(javaSensorData, timestampField); + + // 读取数据类型 + jobject dataTypeObj = env->GetObjectField(javaSensorData, dataTypeField); + if (dataTypeObj != nullptr) { + jclass dataTypeClass = env->GetObjectClass(dataTypeObj); + jmethodID nameMethod = env->GetMethodID(dataTypeClass, "name", "()Ljava/lang/String;"); + jstring nameStr = (jstring)env->CallObjectMethod(dataTypeObj, nameMethod); + const char* name = env->GetStringUTFChars(nameStr, nullptr); + + if (strcmp(name, "EEG") == 0) cppSensorData.data_type = DataType::EEG; + else if (strcmp(name, "ECG_2LEAD") == 0) cppSensorData.data_type = DataType::ECG_2LEAD; + else if (strcmp(name, "ECG_12LEAD") == 0) cppSensorData.data_type = DataType::ECG_12LEAD; + else if (strcmp(name, "PPG") == 0) cppSensorData.data_type = DataType::PPG; + else if (strcmp(name, "RESPIRATION") == 0) cppSensorData.data_type = DataType::RESPIRATION; + else if (strcmp(name, "SNORE") == 0) cppSensorData.data_type = DataType::SNORE; + else if (strcmp(name, "STETHOSCOPE") == 0) cppSensorData.data_type = DataType::STETHOSCOPE; + else cppSensorData.data_type = DataType::EEG; // 默认值 + + env->ReleaseStringUTFChars(nameStr, name); + } + + // 读取导联状态 + jbyteArray leadStatusArray = (jbyteArray)env->GetObjectField(javaSensorData, leadStatusField); + if (leadStatusArray != nullptr) { + jbyte* elements = env->GetByteArrayElements(leadStatusArray, nullptr); + std::memcpy(cppSensorData.lead_status.status, elements, 2); + env->ReleaseByteArrayElements(leadStatusArray, elements, JNI_ABORT); + } + + // 读取通道数据 + jobject channelDataList = env->GetObjectField(javaSensorData, channelDataField); + if (channelDataList != nullptr) { + jclass listClass = env->GetObjectClass(channelDataList); + jmethodID sizeMethod = env->GetMethodID(listClass, "size", "()I"); + jmethodID getMethod = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;"); + + jint listSize = env->CallIntMethod(channelDataList, sizeMethod); + + if (listSize > 0) { + try { + // 检查第一个元素是List还是Float + jobject firstElement = env->CallObjectMethod(channelDataList, getMethod, 0); + if (firstElement != nullptr) { + jclass elementClass = env->GetObjectClass(firstElement); + jclass listClassRef = env->FindClass("java/util/List"); + + if (listClassRef != nullptr && env->IsInstanceOf(firstElement, listClassRef)) { + // 多通道数据 (List>) + std::vector> channels; + channels.reserve(listSize); + + for (int i = 0; i < listSize; i++) { + jobject channelList = env->CallObjectMethod(channelDataList, getMethod, i); + if (channelList != nullptr) { + std::vector channel = convertJavaFloatListToVector(env, channelList); + channels.push_back(channel); + } + } + + cppSensorData.channel_data = channels; + } else { + // 单通道数据 (List) + std::vector channel = convertJavaFloatListToVector(env, channelDataList); + cppSensorData.channel_data = channel; + } + } else { + // 第一个元素为空,假设是单通道数据 + std::vector channel = convertJavaFloatListToVector(env, channelDataList); + cppSensorData.channel_data = channel; + } + } catch (...) { + // 如果转换失败,创建一个空的通道数据 + std::vector emptyChannel; + cppSensorData.channel_data = emptyChannel; + } + } else { + // 空列表,创建空的通道数据 + std::vector emptyChannel; + cppSensorData.channel_data = emptyChannel; + } + } else { + // 通道数据为空,创建空的通道数据 + std::vector emptyChannel; + cppSensorData.channel_data = emptyChannel; + } + + // 读取原始数据 + jbyteArray rawDataArray = (jbyteArray)env->GetObjectField(javaSensorData, rawDataField); + if (rawDataArray != nullptr) { + jsize length = env->GetArrayLength(rawDataArray); + jbyte* elements = env->GetByteArrayElements(rawDataArray, nullptr); + cppSensorData.raw_data.assign(elements, elements + length); + env->ReleaseByteArrayElements(rawDataArray, elements, JNI_ABORT); + } + + return cppSensorData; +} + +// 将Java的Float List转换为C++的vector +std::vector convertJavaFloatListToVector(JNIEnv* env, jobject floatList) { + std::vector result; + + try { + jclass listClass = env->GetObjectClass(floatList); + jmethodID sizeMethod = env->GetMethodID(listClass, "size", "()I"); + jmethodID getMethod = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;"); + + jint listSize = env->CallIntMethod(floatList, sizeMethod); + result.reserve(listSize); + + for (int i = 0; i < listSize; i++) { + jobject floatObj = env->CallObjectMethod(floatList, getMethod, i); + if (floatObj != nullptr) { + // 尝试使用Float.floatValue()方法 + jclass floatClass = env->GetObjectClass(floatObj); + jmethodID floatValueMethod = env->GetMethodID(floatClass, "floatValue", "()F"); + if (floatValueMethod != nullptr) { + float value = env->CallFloatMethod(floatObj, floatValueMethod); + result.push_back(value); + } else { + // 如果floatValue方法不存在,尝试直接访问value字段 + jfieldID valueField = env->GetFieldID(floatClass, "value", "F"); + if (valueField != nullptr) { + float value = env->GetFloatField(floatObj, valueField); + result.push_back(value); + } else { + // 如果都失败了,使用默认值 + result.push_back(0.0f); + } + } + } else { + // 如果对象为null,使用默认值 + result.push_back(0.0f); + } + } + } catch (...) { + // 如果转换失败,返回空向量 + result.clear(); + } + + return result; +} + +// 将Java的List转换为C++的vector +std::vector convertJavaSensorDataListToVector(JNIEnv* env, jobject sensorDataList) { + std::vector result; + + jclass listClass = env->GetObjectClass(sensorDataList); + jmethodID sizeMethod = env->GetMethodID(listClass, "size", "()I"); + jmethodID getMethod = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;"); + + jint listSize = env->CallIntMethod(sensorDataList, sizeMethod); + result.reserve(listSize); + + for (int i = 0; i < listSize; i++) { + jobject sensorDataObj = env->CallObjectMethod(sensorDataList, getMethod, i); + SensorData cppSensorData = convertJavaSensorDataToCpp(env, sensorDataObj); + result.push_back(cppSensorData); + } + + return result; +} + +// ============================================================================ +// IndicatorCalculator JNI函数实现 +// ============================================================================ + +// 创建指标计算器实例 +extern "C" JNIEXPORT jlong JNICALL +Java_com_example_cmake_1project_1test_IndicatorCalculator_createIndicatorCalculator( + JNIEnv* env, jobject thiz) { + try { + MetricsCalculator* calculator = new MetricsCalculator(); + jlong calculatorId = reinterpret_cast(calculator); + + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "Created IndicatorCalculator instance with ID: %lld", calculatorId); + return calculatorId; + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Failed to create IndicatorCalculator: %s", e.what()); + return 0; + } +} + +// 销毁指标计算器实例 +extern "C" JNIEXPORT void JNICALL +Java_com_example_cmake_1project_1test_IndicatorCalculator_destroyIndicatorCalculator( + JNIEnv* env, jobject thiz, jlong calculatorHandle) { + try { + if (calculatorHandle != 0) { + MetricsCalculator* calculator = reinterpret_cast(calculatorHandle); + delete calculator; + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "Destroyed IndicatorCalculator instance with ID: %lld", calculatorHandle); + } else { + __android_log_print(ANDROID_LOG_WARN, "IndicatorCalculator", "Attempted to destroy null IndicatorCalculator handle"); + } + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Failed to destroy IndicatorCalculator: %s", e.what()); + } +} + +// 完整数据处理流程 +extern "C" JNIEXPORT jobject JNICALL +Java_com_example_cmake_1project_1test_IndicatorCalculator_processCompletePipeline( + JNIEnv* env, jobject thiz, jlong calculatorHandle, jobject sensorData, jfloat sampleRate) { + try { + if (calculatorHandle == 0) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Invalid calculator handle: 0"); + return nullptr; + } + + MetricsCalculator* calculator = reinterpret_cast(calculatorHandle); + + // 将Java的SensorData转换为C++的SensorData + SensorData cppSensorData = convertJavaSensorDataToCpp(env, sensorData); + + // 执行完整的数据处理流程 + std::map metrics = calculator->process_complete_pipeline(cppSensorData, sampleRate); + + // 将C++的map转换为Java的Map + jobject result = convertMetricsMapToJava(env, metrics); + + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "Successfully processed complete pipeline, calculated %zu metrics", metrics.size()); + return result; + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Failed to process complete pipeline: %s", e.what()); + return nullptr; + } +} + +// 计算ECG指标 +extern "C" JNIEXPORT jobject JNICALL +Java_com_example_cmake_1project_1test_IndicatorCalculator_calculateECGMetrics( + JNIEnv* env, jobject thiz, jlong calculatorHandle, jobject sensorData, jfloat sampleRate) { + try { + if (calculatorHandle == 0) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Invalid calculator handle: 0"); + return nullptr; + } + + MetricsCalculator* calculator = reinterpret_cast(calculatorHandle); + + // 将Java的SensorData转换为C++的SensorData + SensorData cppSensorData = convertJavaSensorDataToCpp(env, sensorData); + + // 计算ECG指标 + std::map metrics = calculator->calculate_all_ecg_metrics(cppSensorData, sampleRate); + + // 将C++的map转换为Java的Map + jobject result = convertMetricsMapToJava(env, metrics); + + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "Successfully calculated ECG metrics, %zu metrics", metrics.size()); + return result; + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Failed to calculate ECG metrics: %s", e.what()); + return nullptr; + } +} + +// 计算PPG指标 +extern "C" JNIEXPORT jobject JNICALL +Java_com_example_cmake_1project_1test_IndicatorCalculator_calculatePPGMetrics( + JNIEnv* env, jobject thiz, jlong calculatorHandle, jobject sensorData, jfloat sampleRate) { + try { + if (calculatorHandle == 0) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Invalid calculator handle: 0"); + return nullptr; + } + + MetricsCalculator* calculator = reinterpret_cast(calculatorHandle); + + // 将Java的SensorData转换为C++的SensorData + SensorData cppSensorData = convertJavaSensorDataToCpp(env, sensorData); + + // 计算PPG指标 + std::map metrics = calculator->calculate_all_ppg_metrics(cppSensorData, sampleRate); + + // 将C++的map转换为Java的Map + jobject result = convertMetricsMapToJava(env, metrics); + + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "Successfully calculated PPG metrics, %zu metrics", metrics.size()); + return result; + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Failed to calculate PPG metrics: %s", e.what()); + return nullptr; + } +} + +// 计算HRV指标 +extern "C" JNIEXPORT jobject JNICALL +Java_com_example_cmake_1project_1test_IndicatorCalculator_calculateHRVMetrics( + JNIEnv* env, jobject thiz, jlong calculatorHandle, jobject rrIntervals) { + try { + if (calculatorHandle == 0) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Invalid calculator handle: 0"); + return nullptr; + } + + MetricsCalculator* calculator = reinterpret_cast(calculatorHandle); + + // 将Java的List转换为C++的vector + std::vector cppRrIntervals = convertJavaFloatListToVector(env, rrIntervals); + + // 计算HRV指标 + std::map metrics = calculator->calculate_all_hrv_metrics(cppRrIntervals); + + // 将C++的map转换为Java的Map + jobject result = convertMetricsMapToJava(env, metrics); + + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "Successfully calculated HRV metrics, %zu metrics", metrics.size()); + return result; + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "Failed to calculate HRV metrics: %s", e.what()); + return nullptr; + } +} + +// ============================================================================ +// 新增辅助函数实现 +// ============================================================================ + +// 将C++的map转换为Java的Map +jobject convertMetricsMapToJava(JNIEnv* env, const std::map& metrics) { + // 创建HashMap + jclass hashMapClass = env->FindClass("java/util/HashMap"); + jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "", "()V"); + jobject hashMap = env->NewObject(hashMapClass, hashMapConstructor); + + // 获取put方法 + jmethodID putMethod = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + // 获取Float类 + jclass floatClass = env->FindClass("java/lang/Float"); + jmethodID floatConstructor = env->GetMethodID(floatClass, "", "(F)V"); + + // 遍历C++ map并添加到Java HashMap + for (const auto& pair : metrics) { + // 创建Java String + jstring key = env->NewStringUTF(pair.first.c_str()); + + // 创建Java Float + jobject value = env->NewObject(floatClass, floatConstructor, pair.second); + + // 添加到HashMap + env->CallObjectMethod(hashMap, putMethod, key, value); + + // 释放局部引用 + env->DeleteLocalRef(key); + env->DeleteLocalRef(value); + } + + return hashMap; +} + diff --git a/app/src/main/cpp/src/data_mapper.cpp b/app/src/main/cpp/src/data_mapper.cpp new file mode 100644 index 0000000..c29a450 --- /dev/null +++ b/app/src/main/cpp/src/data_mapper.cpp @@ -0,0 +1,531 @@ +#include "cpp/data_mapper.h" +#include +#include +#include +#include + +// 构造函数 +Mapper::Mapper() { + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DataMapper instance created"); +} + +// 析构函数 +Mapper::~Mapper() { + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DataMapper instance destroyed"); +} + +SensorData Mapper::DataMapper(SensorData& data) +{ + // 添加空指针检查 + if (data.channel_data.valueless_by_exception()) { + throw std::runtime_error("Channel data is in invalid state"); + } + + SensorData data_mapped; + data_mapped = data; + + // 添加调试信息 + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "=== 开始数据映射 ==="); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "数据类型: %d", static_cast(data_mapped.data_type)); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DEBUG: 数据类型值 = %d", static_cast(data_mapped.data_type)); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "数据类型详细: "); + switch(data_mapped.data_type) { + case DataType::EEG: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "EEG (0)"); break; + case DataType::ECG_2LEAD: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "ECG_2LEAD (1)"); break; + case DataType::ECG_12LEAD: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "ECG_12LEAD (2)"); break; + case DataType::PPG: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "PPG (3)"); break; + case DataType::RESPIRATION: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "RESPIRATION (4)"); break; + case DataType::SNORE: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "SNORE (5)"); break; + case DataType::STETHOSCOPE: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "STETHOSCOPE (6)"); break; + case DataType::MIT_BIH: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "MIT_BIH (7)"); break; + default: __android_log_print(ANDROID_LOG_INFO, "DataMapper", "未知类型"); break; + } + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "包序号: %d", data_mapped.packet_sn); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "时间戳: %lld", data_mapped.timestamp); + + // 检查通道数据类型并安全处理 + try { + switch(data_mapped.data_type){ + case DataType::EEG : + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "调用EEG数据映射器"); + data_mapped = EEG_Data_Mapper(data_mapped);break; + case DataType::ECG_2LEAD: + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "调用ECG_2LEAD数据映射器"); + data_mapped = ECG_2LEAD_Data_Mapper(data_mapped);break; + case DataType::ECG_12LEAD: + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "调用ECG_12LEAD数据映射器"); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DEBUG: 确认进入ECG_12LEAD分支"); + data_mapped = ECG_12LEAD_Data_Mapper(data_mapped); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DEBUG: ECG_12LEAD映射完成"); + break; + case DataType::PPG: + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "调用PPG数据映射器"); + data_mapped = PPG_Data_Mapper(data_mapped);break; + case DataType::RESPIRATION: + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "调用RESPIRATION数据映射器"); + data_mapped = Respiration_Data_Mapper(data_mapped);break; + case DataType::SNORE: + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "调用SNORE数据映射器"); + data_mapped = Snore_Data_Mapper(data_mapped);break; + case DataType::STETHOSCOPE: + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "调用STETHOSCOPE数据映射器"); + data_mapped = Stethoscope_Data_Mapper(data_mapped);break; + default: + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "未知数据类型,跳过映射"); + // 对于未知类型,直接返回原始数据 + break; + } + + // 检查映射后的通道数量 + if (std::holds_alternative>>(data_mapped.channel_data)) { + auto& output_channels = std::get>>(data_mapped.channel_data); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "映射后通道数量: %zu", output_channels.size()); + } else if (std::holds_alternative>(data_mapped.channel_data)) { + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "映射后为单通道数据"); + } + + } catch (const std::exception& e) { + // 如果映射失败,记录错误并返回原始数据 + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Data mapping failed: %s", e.what()); + return data; // 返回原始数据而不是抛出异常 + } + + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "=== 数据映射完成 ==="); + return data_mapped; +} + +// EEG数据映射器 - 处理已解析的通道数据 +SensorData Mapper::EEG_Data_Mapper(SensorData& data) +{ + SensorData processed = data; + + try { + // 检查通道数据类型 - 支持多种格式 + if (std::holds_alternative>>(data.channel_data)) { + // 多通道格式 + auto& input_channels = std::get>>(data.channel_data); + + if (input_channels.empty()) { + __android_log_print(ANDROID_LOG_WARN, "DataMapper", "Warning: Input channel data for EEG is empty"); + return data; // 返回原始数据 + } + + // 创建输出通道数据 + std::vector> output_channels; + output_channels.reserve(input_channels.size()); + + // 复制并应用校准系数 (0.318 μV/unit) + for (const auto& input_channel : input_channels) { + std::vector output_channel; + output_channel.reserve(input_channel.size()); + + for (float value : input_channel) { + output_channel.push_back(value * 0.318f); // 转换为μV + } + output_channels.push_back(output_channel); + } + + // 更新处理后的通道数据 + processed.channel_data = output_channels; + + } else if (std::holds_alternative>(data.channel_data)) { + // 单通道格式 + auto& input_channel = std::get>(data.channel_data); + + if (input_channel.empty()) { + __android_log_print(ANDROID_LOG_WARN, "DataMapper", "Warning: Input channel data for EEG is empty"); + return data; // 返回原始数据 + } + + // 创建输出通道数据 + std::vector output_channel; + output_channel.reserve(input_channel.size()); + + // 应用校准系数 + for (float value : input_channel) { + output_channel.push_back(value * 0.318f); // 转换为μV + } + + // 更新处理后的通道数据 + processed.channel_data = output_channel; + + } else { + __android_log_print(ANDROID_LOG_WARN, "DataMapper", "Warning: Unknown channel data format for EEG"); + return data; // 返回原始数据 + } + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Error in EEG_Data_Mapper: %s", e.what()); + return data; // 返回原始数据 + } + + return processed; +} + +// 胸腹设备数据映射器 - 处理已解析的通道数据 +SensorData Mapper::ECG_2LEAD_Data_Mapper(SensorData& data) { + SensorData processed = data; + + try { + // 检查通道数据类型 - 支持多种格式 + if (std::holds_alternative>>(data.channel_data)) { + // 多通道格式 + auto& input_channels = std::get>>(data.channel_data); + + if (input_channels.empty()) { + __android_log_print(ANDROID_LOG_WARN, "DataMapper", "Warning: Input channel data for ECG_2LEAD is empty"); + return data; // 返回原始数据 + } + + // 创建输出通道数据 + std::vector> output_channels; + output_channels.reserve(input_channels.size()); + + // 复制并应用校准系数 (0.5 mV/unit) + for (const auto& input_channel : input_channels) { + std::vector output_channel; + output_channel.reserve(input_channel.size()); + + for (float value : input_channel) { + output_channel.push_back(value * 0.5f); // 转换为mV + } + output_channels.push_back(output_channel); + } + + // 更新处理后的通道数据 + processed.channel_data = output_channels; + + } else if (std::holds_alternative>(data.channel_data)) { + // 单通道格式 + auto& input_channel = std::get>(data.channel_data); + + if (input_channel.empty()) { + __android_log_print(ANDROID_LOG_WARN, "DataMapper", "Warning: Input channel data for ECG_2LEAD is empty"); + return data; // 返回原始数据 + } + + // 创建输出通道数据 + std::vector output_channel; + output_channel.reserve(input_channel.size()); + + // 应用校准系数 + for (float value : input_channel) { + output_channel.push_back(value * 0.5f); // 转换为mV + } + + // 更新处理后的通道数据 + processed.channel_data = output_channel; + + } else { + __android_log_print(ANDROID_LOG_WARN, "DataMapper", "Warning: Unknown channel data format for ECG_2LEAD"); + return data; // 返回原始数据 + } + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "Error in ECG_2LEAD_Data_Mapper: %s", e.what()); + return data; // 返回原始数据 + } + + return processed; +} + +// 12导联心电数据映射器 - 处理已解析的通道数据 +SensorData Mapper::ECG_12LEAD_Data_Mapper(SensorData& data) +{ + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "=== 开始ECG_12LEAD通道映射 ==="); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "数据类型: %d", static_cast(data.data_type)); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "包序号: %d", data.packet_sn); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "时间戳: %lld", data.timestamp); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DEBUG: ECG_12LEAD_Data_Mapper函数被调用"); + + SensorData processed = data; + + try { + + // 检查通道数据类型 + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "检查通道数据类型..."); + if (!std::holds_alternative>>(data.channel_data)) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "错误:通道数据格式不正确,期望std::vector>"); + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "实际类型索引: %zu", data.channel_data.index()); + throw std::runtime_error("Invalid channel data format for ECG_12LEAD"); + } + + // 获取已解析的通道数据 + auto& input_channels = std::get>>(data.channel_data); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "输入通道数量: %zu", input_channels.size()); + + // 检查输入通道是否为空 + if (input_channels.empty()) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "错误:输入通道数据为空"); + throw std::runtime_error("Input channel data for ECG_12LEAD is empty"); + } + + // 确保有足够的通道 (至少需要8个通道:V1, LA-RA, LL-RA, V2, V3, V4, V5, V6) + if (input_channels.size() < 8) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "错误:输入通道数量不足,需要至少8个通道,实际只有 %zu 个", input_channels.size()); + throw std::runtime_error("Input channel data for ECG_12LEAD has less than 8 channels"); + } + + // 检查关键通道是否有数据 + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "检查关键通道数据..."); + for (size_t i = 0; i < 8; ++i) { + if (input_channels[i].empty()) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "错误:通道 %zu 为空", i); + throw std::runtime_error("Channel " + std::to_string(i) + " for ECG_12LEAD is empty"); + } + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "通道 %zu 采样点数: %zu", i, input_channels[i].size()); + } + + // 获取采样点数 + size_t num_samples = input_channels[0].size(); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "基准采样点数: %zu", num_samples); + + // 验证所有关键通道都有相同的采样点数 + for (size_t i = 0; i < 8; ++i) { + if (input_channels[i].size() != num_samples) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "错误:通道 %zu 采样点数不一致,期望 %zu,实际 %zu", + i, num_samples, input_channels[i].size()); + throw std::runtime_error("Channel " + std::to_string(i) + " has different sample count: " + + std::to_string(input_channels[i].size()) + " vs " + std::to_string(num_samples)); + } + } + + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "数据验证通过,开始通道映射..."); + + // 创建12导联输出结构,并初始化所有通道的大小 + std::vector> output_channels(12); + for (int ch = 0; ch < 12; ++ch) { + output_channels[ch].resize(num_samples, 0.0f); // 初始化为0 + } + + + // 通道0: V1电极信号 (直接复制) + output_channels[0] = input_channels[0]; + + // 通道1: Lead I = LA - RA (直接复制) + output_channels[1] = input_channels[1]; + + // 通道2: Lead II = LL - RA (直接复制) + output_channels[2] = input_channels[2]; + + // 通道3: Lead III = LL - LA = Lead II - Lead I + for (size_t sample = 0; sample < num_samples; ++sample) { + output_channels[3][sample] = output_channels[2][sample] - output_channels[1][sample]; + } + + // 胸导联 V2-V6 (直接复制) + output_channels[4] = input_channels[3]; // V2 + output_channels[5] = input_channels[4]; // V3 + output_channels[6] = input_channels[5]; // V4 + output_channels[7] = input_channels[6]; // V5 + output_channels[8] = input_channels[7]; // V6 + + // 计算加压肢体导联 (aVR, aVL, aVF) + // 根据用户提供的新公式: + // aVR = -1/2 × (Lead I + Lead II) + // aVL = -1/2 × (Lead I - Lead III) + // aVF = -1/2 × (Lead II + Lead III) + + for (size_t sample = 0; sample < num_samples; ++sample) { + float lead_I = output_channels[1][sample]; // Lead I = LA - RA + float lead_II = output_channels[2][sample]; // Lead II = LL - RA + float lead_III = output_channels[3][sample]; // Lead III = LL - LA + + // 使用新公式计算加压肢体导联 + output_channels[9][sample] = -0.5f * (lead_I + lead_II); // aVR = -1/2 × (I + II) + output_channels[10][sample] = -0.5f * (lead_I - lead_III); // aVL = -1/2 × (I - III) + output_channels[11][sample] = -0.5f * (lead_II + lead_III); // aVF = -1/2 × (II + III) + } + + // 验证输出通道的完整性 + for (size_t ch = 0; ch < output_channels.size(); ++ch) { + if (output_channels[ch].size() != num_samples) { + throw std::runtime_error("Output channel " + std::to_string(ch) + " has incorrect size: " + + std::to_string(output_channels[ch].size()) + " vs expected " + std::to_string(num_samples)); + } + } + + // 更新处理后的通道数据 + processed.channel_data = output_channels; + + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "ECG_12LEAD通道映射完成!"); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "输出通道数量: %zu", output_channels.size()); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "每个通道采样点数: %zu", num_samples); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "=== ECG_12LEAD通道映射结束 ==="); + + // 验证映射结果 + if (std::holds_alternative>>(processed.channel_data)) { + auto& final_channels = std::get>>(processed.channel_data); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "最终验证 - 映射后通道数量: %zu", final_channels.size()); + } else { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "最终验证失败 - 通道数据类型错误"); + } + + return processed; + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "ECG_12LEAD映射异常: %s", e.what()); + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "包序号: %d", data.packet_sn); + __android_log_print(ANDROID_LOG_ERROR, "DataMapper", "返回原始数据(8通道)"); + return data; // 返回原始数据 + } +} + +// PPG数据映射器 - 处理已解析的通道数据 +SensorData Mapper::PPG_Data_Mapper(SensorData& data) +{ + SensorData processed = data; + + // 检查通道数据类型 + if (!std::holds_alternative>>(data.channel_data)) { + throw std::runtime_error("Invalid channel data format for PPG"); + } + + // 获取已解析的通道数据 + auto& input_channels = std::get>>(data.channel_data); + + // 检查输入通道是否为空 + if (input_channels.empty()) { + throw std::runtime_error("Input channel data for PPG is empty"); + } + + // 确保有足够的通道 + if (input_channels.size() < 2) { + throw std::runtime_error("Input channel data for PPG has less than 2 channels"); + } + + // 检查每个通道是否有数据 + for (size_t i = 0; i < 2; ++i) { + if (input_channels[i].empty()) { + throw std::runtime_error("Channel " + std::to_string(i) + " for PPG is empty"); + } + } + + // 创建输出通道数据 + std::vector> output(2); + + // 通道0: 红光数据 + output[0] = input_channels[0]; + + // 通道1: 红外光数据 + output[1] = input_channels[1]; + + // 更新处理后的通道数据 + processed.channel_data = output; + + return processed; +} + +// 呼吸数据映射器 - 处理已解析的通道数据 +SensorData Mapper::Respiration_Data_Mapper(SensorData& data) +{ + SensorData processed = data; + + // 检查通道数据类型 + if (!std::holds_alternative>>(data.channel_data)) { + throw std::runtime_error("Invalid channel data format for Respiration"); + } + + // 获取已解析的通道数据 + auto& input_channels = std::get>>(data.channel_data); + + // 检查输入通道是否为空 + if (input_channels.empty()) { + throw std::runtime_error("Input channel data for Respiration is empty"); + } + + // 确保有足够的通道 + if (input_channels.size() < 1) { + throw std::runtime_error("Input channel data for Respiration has no channels"); + } + + // 检查第一个通道是否有数据 + if (input_channels[0].empty()) { + throw std::runtime_error("Channel 0 for Respiration is empty"); + } + + // 创建输出通道数据 + std::vector> output(1); + output[0] = input_channels[0]; + + // 更新处理后的通道数据 + processed.channel_data = output; + + return processed; +} + +// 打鼾数据映射器 - 处理已解析的通道数据 +SensorData Mapper::Snore_Data_Mapper(SensorData& data) +{ + SensorData processed = data; + + // 检查通道数据类型 + if (!std::holds_alternative>(data.channel_data)) { + throw std::runtime_error("Invalid channel data format for SNORE"); + } + + // 获取已解析的通道数据 + auto& raw_samples = std::get>(data.channel_data); + + // 检查输入数据是否为空 + if (raw_samples.empty()) { + throw std::runtime_error("Input channel data for SNORE is empty"); + } + + // 应用校准系数 (0.146 mV/unit) + std::vector calibrated; + calibrated.reserve(raw_samples.size()); + + for (float sample : raw_samples) { + calibrated.push_back(sample * 0.146f); + } + + // 更新处理后的通道数据 + processed.channel_data = calibrated; + + return processed; +} + +// 听诊器数据映射器 - 处理已解析的通道数据 +SensorData Mapper::Stethoscope_Data_Mapper(SensorData& data) +{ + SensorData processed = data; + + // 检查通道数据类型 + if (!std::holds_alternative>>(data.channel_data)) { + throw std::runtime_error("Invalid channel data format for Stethoscope"); + } + + // 获取已解析的通道数据 + auto& input_channels = std::get>>(data.channel_data); + + // 检查输入通道是否为空 + if (input_channels.empty()) { + throw std::runtime_error("Input channel data for Stethoscope is empty"); + } + + // 确保有足够的通道 + if (input_channels.size() < 2) { + throw std::runtime_error("Input channel data for Stethoscope has less than 2 channels"); + } + + // 检查每个通道是否有数据 + for (size_t i = 0; i < 2; ++i) { + if (input_channels[i].empty()) { + throw std::runtime_error("Channel " + std::to_string(i) + " for Stethoscope is empty"); + } + } + + // 创建输出通道数据 + std::vector> output(2); + output[0] = input_channels[0]; + output[1] = input_channels[1]; + + // 更新处理后的通道数据 + processed.channel_data = output; + + return processed; +} + +// End of data_mapper.cpp + diff --git a/app/src/main/cpp/src/data_praser.cpp b/app/src/main/cpp/src/data_praser.cpp index f6e92ee..88956d9 100644 --- a/app/src/main/cpp/src/data_praser.cpp +++ b/app/src/main/cpp/src/data_praser.cpp @@ -2,6 +2,7 @@ #include // 添加 memcpy 头文件 #include // 添加 stringstream 头文件 #include // 添加 iomanip 头文件 +#include // 添加 iostream 头文件 #include #include #include @@ -316,7 +317,7 @@ SensorData parse_respiration(const uint8_t* data) { // 统一解析入口函数 - 支持多个包头和数据包格式 std::vector parse_device_data(const std::vector& file_data) { const size_t PACKET_SIZE = 238; // 每个数据包固定大小 - const size_t RESPONSE_HEADER_SIZE = 10; // 响应包头大小 (2功能码+ 2数据长度 + 4实际采集点数+crc校验) + const size_t RESPONSE_HEADER_SIZE = 10; // 响应包头大小 (2功能码+ 2数据长度 + 4实际采集点数+2CRC校验) const uint16_t FUNCTION_CODE = 0x0010; // 获取数据功能码 const size_t file_size = file_data.size(); @@ -324,6 +325,7 @@ std::vector parse_device_data(const std::vector& file_data) const uint8_t* end_ptr = ptr + file_size; std::map> grouped_data; std::vector results; + while (ptr < end_ptr) { // 检查是否有响应包头 bool has_response_header = false; @@ -339,9 +341,23 @@ std::vector parse_device_data(const std::vector& file_data) // 检查是否符合0x0010响应包特征 if (func_code == FUNCTION_CODE && data_len == 0x0004) { has_response_header = true; + std::cout << "检测到响应帧头: 功能码=0x" << std::hex << func_code + << ", 数据长度=0x" << data_len << std::dec << std::endl; // 读取实际数据点数 (小端) uint32_t actual_points = read_le(ptr + 4); + std::cout << "实际数据点数: " << actual_points << std::endl; + + // 读取CRC校验 (小端) + uint16_t crc = read_le(ptr + 8); + + // 验证CRC校验 + uint16_t calculated_crc = calculate_crc16(ptr, 8); + if (crc != calculated_crc) { + std::cerr << "警告: CRC校验失败,预期: 0x" << std::hex << calculated_crc + << ", 实际: 0x" << crc << std::dec << std::endl; + // 可以选择跳过这个响应帧或继续处理 + } // 计算期望的数据长度(实际点数 * 包大小) size_t expected_data_size = actual_points * PACKET_SIZE; @@ -392,8 +408,11 @@ std::vector parse_device_data(const std::vector& file_data) grouped_data[DataType::STETHOSCOPE].push_back(parse_stethoscope(ptr)); break; default: - throw std::runtime_error("未知设备类型: 0x" + - to_hex_string(data_type)); + // 对于未知类型,记录警告但继续处理 + std::cerr << "警告: 未知设备类型: 0x" << std::hex << data_type + << std::dec << ", 跳过此数据包" << std::endl; + ptr += PACKET_SIZE; + continue; } ptr += PACKET_SIZE; } @@ -506,6 +525,24 @@ std::vector parse_device_data(const std::vector& file_data) return final_results; } +// CRC16校验函数 (Modbus CRC16) +uint16_t calculate_crc16(const uint8_t* data, size_t length) { + uint16_t crc = 0xFFFF; + + for (size_t i = 0; i < length; i++) { + crc ^= data[i]; + for (int j = 0; j < 8; j++) { + if (crc & 0x0001) { + crc = (crc >> 1) ^ 0xA001; + } else { + crc = crc >> 1; + } + } + } + + return crc; +} + // 工具函数:将数值转换为十六进制字符串 std::string to_hex_string(uint16_t value) { std::stringstream ss; @@ -802,6 +839,9 @@ jobject convertSensorDataToJava(JNIEnv* env, const SensorData& sensorData) { // 设置通道数据(处理两种类型:vector> 和 vector) if (auto* multiChannels = std::get_if>>(&sensorData.channel_data)) { // 处理多通道数据 (vector>) + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DEBUG: C++到Java转换 - 多通道数据,通道数量: %zu", multiChannels->size()); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DEBUG: C++到Java转换 - 数据类型: %d", static_cast(sensorData.data_type)); + jclass arrayListClass = env->FindClass("java/util/ArrayList"); jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "", "()V"); jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); @@ -829,6 +869,7 @@ jobject convertSensorDataToJava(JNIEnv* env, const SensorData& sensorData) { } env->SetObjectField(javaSensorData, channelDataField, outerList); + __android_log_print(ANDROID_LOG_INFO, "DataMapper", "DEBUG: C++到Java转换 - 多通道数据设置完成"); } else if (auto* singleChannel = std::get_if>(&sensorData.channel_data)) { // 处理单通道数据 (vector) - 将其包装为单通道的多通道格式 diff --git a/app/src/main/cpp/src/indicator_cal.cpp b/app/src/main/cpp/src/indicator_cal.cpp new file mode 100644 index 0000000..b6665cb --- /dev/null +++ b/app/src/main/cpp/src/indicator_cal.cpp @@ -0,0 +1,1064 @@ +// metrics_calculator.cpp +#include "cpp/indicator_cal.h" +#include +#include +#include +#include +#include + +// 构造函数 +MetricsCalculator::MetricsCalculator() { + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "MetricsCalculator instance created"); +} + +// 析构函数 +MetricsCalculator::~MetricsCalculator() { + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "MetricsCalculator instance destroyed"); +} + +// 完整数据处理流程 +std::map MetricsCalculator::process_complete_pipeline(const SensorData& raw_data, float sample_rate) { + std::map all_metrics; + + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "开始完整数据处理流程"); + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "数据类型: %d, 采样率: %.2f Hz", static_cast(raw_data.data_type), sample_rate); + + try { + // 根据数据类型计算相应的指标 + switch (raw_data.data_type) { + case DataType::ECG_2LEAD: + case DataType::ECG_12LEAD: { + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "处理ECG数据"); + auto ecg_metrics = calculate_all_ecg_metrics(raw_data, sample_rate); + all_metrics.insert(ecg_metrics.begin(), ecg_metrics.end()); + break; + } + case DataType::PPG: { + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "处理PPG数据"); + auto ppg_metrics = calculate_all_ppg_metrics(raw_data, sample_rate); + all_metrics.insert(ppg_metrics.begin(), ppg_metrics.end()); + break; + } + case DataType::EEG: { + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "处理EEG数据"); + // 获取单通道数据 + std::vector signal; + if (std::holds_alternative>(raw_data.channel_data)) { + signal = std::get>(raw_data.channel_data); + } else { + const auto& channels = std::get>>(raw_data.channel_data); + if (!channels.empty()) { + signal = channels[0]; + } + } + + if (!signal.empty()) { + all_metrics["signal_quality"] = calculate_signal_quality(signal); + all_metrics["mean"] = std::accumulate(signal.begin(), signal.end(), 0.0f) / signal.size(); + all_metrics["std_dev"] = std::sqrt(std::accumulate(signal.begin(), signal.end(), 0.0f, + [&](float acc, float val) { return acc + (val - all_metrics["mean"]) * (val - all_metrics["mean"]); }) / signal.size()); + } + break; + } + case DataType::RESPIRATION: { + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "处理呼吸数据"); + // 获取单通道数据 + std::vector signal; + if (std::holds_alternative>(raw_data.channel_data)) { + signal = std::get>(raw_data.channel_data); + } else { + const auto& channels = std::get>>(raw_data.channel_data); + if (!channels.empty()) { + signal = channels[0]; + } + } + + if (!signal.empty()) { + all_metrics["signal_quality"] = calculate_signal_quality(signal); + all_metrics["mean"] = std::accumulate(signal.begin(), signal.end(), 0.0f) / signal.size(); + all_metrics["peak_to_peak"] = *std::max_element(signal.begin(), signal.end()) - *std::min_element(signal.begin(), signal.end()); + } + break; + } + default: { + __android_log_print(ANDROID_LOG_WARN, "IndicatorCalculator", "未知数据类型: %d", static_cast(raw_data.data_type)); + // 获取单通道数据 + std::vector signal; + if (std::holds_alternative>(raw_data.channel_data)) { + signal = std::get>(raw_data.channel_data); + } else { + const auto& channels = std::get>>(raw_data.channel_data); + if (!channels.empty()) { + signal = channels[0]; + } + } + + if (!signal.empty()) { + all_metrics["signal_quality"] = calculate_signal_quality(signal); + } + break; + } + } + + // 添加通用指标 + all_metrics["data_type"] = static_cast(raw_data.data_type); + all_metrics["sample_rate"] = sample_rate; + all_metrics["timestamp"] = static_cast(raw_data.timestamp); + all_metrics["packet_sn"] = static_cast(raw_data.packet_sn); + + __android_log_print(ANDROID_LOG_INFO, "IndicatorCalculator", "完整数据处理流程完成,计算了 %zu 个指标", all_metrics.size()); + + } catch (const std::exception& e) { + __android_log_print(ANDROID_LOG_ERROR, "IndicatorCalculator", "数据处理流程异常: %s", e.what()); + all_metrics["error"] = 1.0f; + all_metrics["error_message"] = 0.0f; // 这里可以编码错误信息 + } + + return all_metrics; +} + +// 自定义clamp函数,替代std::clamp(C++17特性) +template +T clamp(T value, T min_val, T max_val) { + if (value < min_val) return min_val; + if (value > max_val) return max_val; + return value; +} + +// 辅助函数:从SensorData获取单通道数据 +static const std::vector& get_single_channel(const SensorData& data) { + if (std::holds_alternative>(data.channel_data)) { + return std::get>(data.channel_data); + } else { + const auto& channels = std::get>>(data.channel_data); + if (!channels.empty()) { + return channels[0]; // 返回第一通道 + } + static const std::vector empty; + return empty; + } +} + +// 辅助函数:从SensorData获取多通道数据 +static const std::vector>& get_multi_channel(const SensorData& data) { + if (std::holds_alternative>>(data.channel_data)) { + return std::get>>(data.channel_data); + } else { + static const std::vector> empty; + return empty; + } +} + +// 辅助函数:计算分位数(q 取值范围 0.0-1.0) +static float compute_percentile(const std::vector& data, float q) { + if (data.empty()) return 0.0f; + float qq = q; + if (qq < 0.0f) qq = 0.0f; + if (qq > 1.0f) qq = 1.0f; + std::vector sorted = data; + std::sort(sorted.begin(), sorted.end()); + if (sorted.size() == 1) return sorted[0]; + float pos = qq * (sorted.size() - 1); + size_t idx_low = static_cast(std::floor(pos)); + size_t idx_high = static_cast(std::ceil(pos)); + if (idx_low == idx_high) return sorted[idx_low]; + float w = pos - idx_low; + return sorted[idx_low] * (1.0f - w) + sorted[idx_high] * w; +} + +// 辅助函数:稳健估计AC/DC(使用分位数和中位数,抗异常) +static std::pair robust_ac_dc(const std::vector& signal) { + if (signal.size() < 5) return {0.0f, 0.0f}; + // 使用5%-95%分位点估计幅度,使用中位数估计直流 + float p5 = compute_percentile(signal, 0.05f); + float p95 = compute_percentile(signal, 0.95f); + float median = compute_percentile(signal, 0.50f); + float ac = (p95 - p5) * 0.5f; + float dc = median; + return {ac, dc}; +} + +// ECG心率计算 - 改进版本 +float MetricsCalculator::calculate_heart_rate_ecg(const SensorData& ecg_signal, float sample_rate) { + // 增强的输入验证 + if (sample_rate <= 0.0f || sample_rate > 10000.0f) { + std::cerr << "警告: 采样率超出合理范围: " << sample_rate << "Hz" << std::endl; + return 0.0f; + } + + const auto& signal = get_single_channel(ecg_signal); + if (signal.size() < static_cast(sample_rate * 0.5f)) { + std::cerr << "警告: 信号长度不足,至少需要 " << sample_rate * 0.5f << " 个样本" << std::endl; + return 0.0f; + } + + if (signal.size() < 3) { + std::cerr << "警告: 信号样本数过少: " << signal.size() << std::endl; + return 0.0f; + } + + // 检测R峰 + auto r_peaks = detect_r_peaks(signal, sample_rate); + if (r_peaks.size() < 2) { + std::cerr << "警告: 检测到的R峰数量不足: " << r_peaks.size() << ",至少需要2个" << std::endl; + return 0.0f; + } + + // 计算RR间期 + std::vector rr_intervals; + for (size_t i = 1; i < r_peaks.size(); i++) { + float rr = (r_peaks[i] - r_peaks[i-1]) / sample_rate; // 转换为秒 + if (rr > 0.2f && rr < 3.0f) { // 过滤异常值 (200ms-3s) + rr_intervals.push_back(rr); + } else { + std::cerr << "警告: 过滤异常RR间期: " << rr * 1000.0f << "ms" << std::endl; + } + } + + if (rr_intervals.empty()) { + std::cerr << "警告: 所有RR间期都被过滤,无法计算心率" << std::endl; + return 0.0f; + } + + // 计算平均RR间期 + float avg_rr = std::accumulate(rr_intervals.begin(), rr_intervals.end(), 0.0f) / rr_intervals.size(); + + // 转换为心率 (次/分钟) + float heart_rate = 60.0f / avg_rr; + + // 验证心率合理性 + if (heart_rate < 30.0f || heart_rate > 300.0f) { + std::cerr << "警告: 计算的心率超出正常范围: " << heart_rate << " bpm" << std::endl; + return 0.0f; + } + + return heart_rate; +} + +// T波振幅计算 - 改进版本 +float MetricsCalculator::calculate_t_wave_amplitude(const std::vector& ecg_signal) { + if (ecg_signal.empty()) return 0.0f; + + // 使用自适应窗口寻找T波 + const size_t n = ecg_signal.size(); + const size_t qrs_end = static_cast(n * 0.25f); // QRS结束位置估计 + const size_t t_end = static_cast(n * 0.6f); // T波结束位置估计 + + float max_amplitude = 0.0f; + size_t t_peak_pos = qrs_end; + + // 在T波窗口内寻找最大振幅 + for (size_t i = qrs_end; i < t_end && i < n; i++) { + if (std::abs(ecg_signal[i]) > max_amplitude) { + max_amplitude = std::abs(ecg_signal[i]); + t_peak_pos = i; + } + } + + // 计算T波相对于基线的振幅 + float baseline = 0.0f; + const size_t baseline_start = std::max(size_t(0), t_peak_pos - static_cast(0.1f * n)); + const size_t baseline_end = std::min(n - 1, t_peak_pos + static_cast(0.1f * n)); + + for (size_t i = baseline_start; i <= baseline_end; i++) { + baseline += ecg_signal[i]; + } + baseline /= (baseline_end - baseline_start + 1); + + return ecg_signal[t_peak_pos] - baseline; +} + +// 新增:QRS宽度计算 +float MetricsCalculator::calculate_qrs_width(const std::vector& ecg_signal, float sample_rate) { + if (ecg_signal.empty() || sample_rate <= 0) return 0.0f; + + // 检测R峰 + auto r_peaks = detect_r_peaks(ecg_signal, sample_rate); + if (r_peaks.empty()) return 0.0f; + + float total_width = 0.0f; + int valid_peaks = 0; + + for (float r_peak : r_peaks) { + size_t r_idx = static_cast(r_peak); + if (r_idx >= ecg_signal.size()) continue; + + // 向前搜索Q波起点 (信号下降超过阈值的点) + size_t q_start = r_idx; + float threshold = 0.3f * std::abs(ecg_signal[r_idx]); + + for (int i = 0; i < static_cast(0.1f * sample_rate) && q_start > 0; i++) { + if (std::abs(ecg_signal[q_start] - ecg_signal[q_start - 1]) > threshold) { + break; + } + q_start--; + } + + // 向后搜索S波终点 (信号下降超过阈值的点) + size_t s_end = r_idx; + for (int i = 0; i < static_cast(0.1f * sample_rate) && s_end < ecg_signal.size() - 1; i++) { + if (std::abs(ecg_signal[s_end] - ecg_signal[s_end + 1]) > threshold) { + break; + } + s_end++; + } + + float width = static_cast(s_end - q_start) / sample_rate * 1000.0f; // 转换为毫秒 + if (width > 40.0f && width < 200.0f) { // 正常QRS宽度范围 + total_width += width; + valid_peaks++; + } + } + + return valid_peaks > 0 ? total_width / valid_peaks : 0.0f; +} + +// 新增:ST段偏移计算 +float MetricsCalculator::calculate_st_offset(const std::vector& ecg_signal, float sample_rate) { + if (ecg_signal.empty() || sample_rate <= 0) return 0.0f; + + // 检测R峰 + auto r_peaks = detect_r_peaks(ecg_signal, sample_rate); + if (r_peaks.empty()) return 0.0f; + + float total_offset = 0.0f; + int valid_peaks = 0; + + for (float r_peak : r_peaks) { + size_t r_idx = static_cast(r_peak); + if (r_idx >= ecg_signal.size()) continue; + + // ST段位置:R峰后80ms + size_t st_pos = r_idx + static_cast(0.08f * sample_rate); + if (st_pos >= ecg_signal.size()) continue; + + // 计算基线 (使用R峰前50ms的平均值) + size_t baseline_start = std::max(size_t(0), r_idx - static_cast(0.05f * sample_rate)); + float baseline = 0.0f; + int baseline_count = 0; + + for (size_t i = baseline_start; i < r_idx; i++) { + baseline += ecg_signal[i]; + baseline_count++; + } + + if (baseline_count > 0) { + baseline /= baseline_count; + float st_offset = ecg_signal[st_pos] - baseline; + total_offset += st_offset; + valid_peaks++; + } + } + + return valid_peaks > 0 ? total_offset / valid_peaks : 0.0f; +} + +// PPG心率计算 - 改进版本 +float MetricsCalculator::calculate_heart_rate_ppg(const std::vector& ppg_signal, float sample_rate) { + // 增强的输入验证 + if (sample_rate <= 0.0f || sample_rate > 10000.0f) { + std::cerr << "警告: PPG采样率超出合理范围: " << sample_rate << "Hz" << std::endl; + return 0.0f; + } + + if (ppg_signal.size() < static_cast(sample_rate * 0.5f)) { + std::cerr << "警告: PPG信号长度不足,至少需要 " << sample_rate * 0.5f << " 个样本" << std::endl; + return 0.0f; + } + + if (ppg_signal.size() < 3) { + std::cerr << "警告: PPG信号样本数过少: " << ppg_signal.size() << std::endl; + return 0.0f; + } + + // 检测脉搏波峰 + auto pulse_peaks = detect_pulse_peaks(ppg_signal, sample_rate); + if (pulse_peaks.size() < 2) { + std::cerr << "警告: 检测到的脉搏波峰数量不足: " << pulse_peaks.size() << ",至少需要2个" << std::endl; + return 0.0f; + } + + // 计算脉搏间期 + std::vector pulse_intervals; + for (size_t i = 1; i < pulse_peaks.size(); i++) { + float interval = (pulse_peaks[i] - pulse_peaks[i-1]) / sample_rate; // 转换为秒 + if (interval > 0.3f && interval < 3.0f) { // 过滤异常值 (300ms-3s) + pulse_intervals.push_back(interval); + } else { + std::cerr << "警告: 过滤异常脉搏间期: " << interval * 1000.0f << "ms" << std::endl; + } + } + + if (pulse_intervals.empty()) { + std::cerr << "警告: 所有脉搏间期都被过滤,无法计算心率" << std::endl; + return 0.0f; + } + + // 计算平均脉搏间期 + float avg_interval = std::accumulate(pulse_intervals.begin(), pulse_intervals.end(), 0.0f) / pulse_intervals.size(); + + // 转换为心率 (次/分钟) + float heart_rate = 60.0f / avg_interval; + + // 验证心率合理性 + if (heart_rate < 30.0f || heart_rate > 300.0f) { + std::cerr << "警告: 计算的PPG心率超出正常范围: " << heart_rate << " bpm" << std::endl; + return 0.0f; + } + + return heart_rate; +} + +// 血氧饱和度计算 - 改进版本 +float MetricsCalculator::calculate_spo2(const SensorData& ppg_data) { + // 输入验证 + if (!std::holds_alternative>>(ppg_data.channel_data)) { + std::cerr << "警告: PPG数据格式不正确,需要多通道数据" << std::endl; + return 0.0f; + } + + const auto& channels = std::get>>(ppg_data.channel_data); + if (channels.size() < 2) { + std::cerr << "警告: PPG数据通道数不足,需要至少2个通道,当前: " << channels.size() << std::endl; + return 0.0f; + } + + const auto& red_channel = channels[0]; + const auto& ir_channel = channels[1]; + + if (red_channel.empty() || ir_channel.empty()) { + std::cerr << "警告: 红光或红外光通道数据为空" << std::endl; + return 0.0f; + } + + if (red_channel.size() != ir_channel.size()) { + std::cerr << "警告: 红光和红外光通道长度不一致: " << red_channel.size() << " vs " << ir_channel.size() << std::endl; + return 0.0f; + } + + // 轻量级异常值裁剪:将数据裁剪到1%-99%范围,提高稳健性 + auto clipped_copy = [](const std::vector& src) { + std::vector out = src; + if (out.size() >= 5) { + float p1 = compute_percentile(out, 0.01f); + float p99 = compute_percentile(out, 0.99f); + for (float& v : out) { + if (v < p1) v = p1; + else if (v > p99) v = p99; + } + } + return out; + }; + + std::vector red_clipped = clipped_copy(red_channel); + std::vector ir_clipped = clipped_copy(ir_channel); + + // 稳健估计红光和红外光的AC/DC分量 + auto [red_ac, red_dc] = robust_ac_dc(red_clipped); + auto [ir_ac, ir_dc] = robust_ac_dc(ir_clipped); + + // 防止除零和异常值 + if (std::abs(red_dc) < 0.0001f || std::abs(ir_dc) < 0.0001f) { + std::cerr << "警告: 直流分量过小,红光: " << red_dc << ", 红外光: " << ir_dc << std::endl; + return 0.0f; + } + + if (std::abs(red_ac) < 0.0001f || std::abs(ir_ac) < 0.0001f) { + std::cerr << "警告: 交流分量过小,红光: " << red_ac << ", 红外光: " << ir_ac << std::endl; + return 0.0f; + } + + // 计算R值 (红光AC/DC与红外光AC/DC的比值) + float r_value = (red_ac / red_dc) / (ir_ac / ir_dc); + + // 验证R值合理性 + if (std::abs(r_value) < 0.1f || std::abs(r_value) > 10.0f) { + std::cerr << "警告: R值超出合理范围: " << r_value << std::endl; + return 0.0f; + } + float spo2; + // 采用常用经验线性映射:SpO2 = 110 - 25 * R (需设备校准) + if(r_value>0) spo2 = 110.0f - 25.0f * r_value; + else spo2 = 110.0f + 25.0f * r_value; + + // 限制在合理范围内 (70% - 100%) + spo2 = clamp(spo2, 70.0f, 100.0f); + + return spo2; +} + +// 新增:灌注指数计算 (PI - Perfusion Index) +float MetricsCalculator::calculate_perfusion_index(const SensorData& ppg_data) { + if (!std::holds_alternative>>(ppg_data.channel_data) || + std::get>>(ppg_data.channel_data).size() < 1) { + return 0.0f; + } + + const auto& channels = std::get>>(ppg_data.channel_data); + const auto& signal = channels[0]; // 使用第一通道 + + if (signal.empty()) return 0.0f; + + // 计算AC和DC分量 + float max_val = *std::max_element(signal.begin(), signal.end()); + float min_val = *std::min_element(signal.begin(), signal.end()); + float dc = (max_val + min_val) / 2.0f; + float ac = (max_val - min_val) / 2.0f; + + // 灌注指数 = (AC / DC) * 100% + if (dc < 0.0001f) return 0.0f; + return (ac / dc) * 100.0f; +} + +// 脉搏波宽度计算 - 改进版本 +float MetricsCalculator::calculate_pulse_width(const std::vector& ppg_signal) { + if (ppg_signal.empty()) return 0.0f; + + // 找到最大峰值位置 + auto max_it = std::max_element(ppg_signal.begin(), ppg_signal.end()); + size_t peak_idx = std::distance(ppg_signal.begin(), max_it); + float peak_value = *max_it; + + // 自适应阈值 + float threshold = 0.1f * peak_value; + + // 找到上升沿起点 (信号值小于阈值的点) + size_t start_idx = peak_idx; + while (start_idx > 0 && ppg_signal[start_idx] > threshold) { + start_idx--; + } + + // 找到下降沿终点 (信号值小于阈值的点) + size_t end_idx = peak_idx; + while (end_idx < ppg_signal.size() - 1 && ppg_signal[end_idx] > threshold) { + end_idx++; + } + + return static_cast(end_idx - start_idx); +} + +// 红光/红外光振幅比 - 改进版本 +float MetricsCalculator::calculate_amplitude_ratio(const SensorData& ppg_data) { + if (!std::holds_alternative>>(ppg_data.channel_data) || + std::get>>(ppg_data.channel_data).size() < 2) { + return 0.0f; + } + + const auto& channels = std::get>>(ppg_data.channel_data); + const auto& red_channel = channels[0]; + const auto& ir_channel = channels[1]; + + if (red_channel.empty() || ir_channel.empty()) return 0.0f; + + // 计算红光和红外光的峰峰值 + float red_amp = *std::max_element(red_channel.begin(), red_channel.end()) - + *std::min_element(red_channel.begin(), red_channel.end()); + + float ir_amp = *std::max_element(ir_channel.begin(), ir_channel.end()) - + *std::min_element(ir_channel.begin(), ir_channel.end()); + + // 防止除零 + if (ir_amp < 0.0001f) return 0.0f; + + return red_amp / ir_amp; +} + +// HRV指标 - SDNN (RR间期的标准差) - 改进版本 +float MetricsCalculator::calculate_sdnn(const std::vector& rr_intervals) { + if (rr_intervals.size() < 2) return 0.0f; + + // 过滤异常RR间期 + std::vector filtered_rr; + for (float rr : rr_intervals) { + if (rr > 0.2f && rr < 3.0f) { // 200ms-3s范围 + filtered_rr.push_back(rr); + } + } + + if (filtered_rr.size() < 2) return 0.0f; + + float mean = std::accumulate(filtered_rr.begin(), filtered_rr.end(), 0.0f) / filtered_rr.size(); + + float variance = 0.0f; + for (float rr : filtered_rr) { + variance += (rr - mean) * (rr - mean); + } + variance /= filtered_rr.size(); + + return std::sqrt(variance); +} + +// HRV指标 - RMSSD (相邻RR间期差值的均方根) - 改进版本 +float MetricsCalculator::calculate_rmssd(const std::vector& rr_intervals) { + if (rr_intervals.size() < 2) return 0.0f; + + // 过滤异常RR间期 + std::vector filtered_rr; + for (float rr : rr_intervals) { + if (rr > 0.2f && rr < 3.0f) { // 200ms-3s范围 + filtered_rr.push_back(rr); + } + } + + if (filtered_rr.size() < 2) return 0.0f; + + float sum_sq_diff = 0.0f; + for (size_t i = 1; i < filtered_rr.size(); i++) { + float diff = filtered_rr[i] - filtered_rr[i-1]; + sum_sq_diff += diff * diff; + } + + return std::sqrt(sum_sq_diff / (filtered_rr.size() - 1)); +} + +// 新增:HRV指标 - pNN50 (相邻RR间期差值>50ms的比例) +float MetricsCalculator::calculate_pnn50(const std::vector& rr_intervals) { + if (rr_intervals.size() < 2) return 0.0f; + + // 过滤异常RR间期 + std::vector filtered_rr; + for (float rr : rr_intervals) { + if (rr > 0.2f && rr < 3.0f) { // 200ms-3s范围 + filtered_rr.push_back(rr); + } + } + + if (filtered_rr.size() < 2) return 0.0f; + + int count_50ms = 0; + for (size_t i = 1; i < filtered_rr.size(); i++) { + float diff = std::abs(filtered_rr[i] - filtered_rr[i-1]); + if (diff > 0.05f) { // 50ms + count_50ms++; + } + } + + return static_cast(count_50ms) / (filtered_rr.size() - 1) * 100.0f; +} + +// 新增:HRV指标 - 三角指数 +float MetricsCalculator::calculate_triangular_index(const std::vector& rr_intervals) { + if (rr_intervals.size() < 10) return 0.0f; + + // 过滤异常RR间期 + std::vector filtered_rr; + for (float rr : rr_intervals) { + if (rr > 0.2f && rr < 3.0f) { // 200ms-3s范围 + filtered_rr.push_back(rr); + } + } + + if (filtered_rr.size() < 10) return 0.0f; + + // 创建直方图 + const int bins = 256; + std::vector histogram(bins, 0); + + float min_rr = *std::min_element(filtered_rr.begin(), filtered_rr.end()); + float max_rr = *std::max_element(filtered_rr.begin(), filtered_rr.end()); + float bin_width = (max_rr - min_rr) / bins; + + if (bin_width < 0.001f) return 0.0f; // 防止除零 + + for (float rr : filtered_rr) { + int bin = static_cast((rr - min_rr) / bin_width); + if (bin >= 0 && bin < bins) { + histogram[bin]++; + } + } + + // 找到直方图最大值 + int max_count = *std::max_element(histogram.begin(), histogram.end()); + if (max_count == 0) return 0.0f; + + // 计算三角指数 + return static_cast(filtered_rr.size()) / max_count; +} + +// 改进的R峰检测算法 (基于Pan-Tompkins算法) +std::vector MetricsCalculator::detect_r_peaks(const std::vector& ecg_signal, float sample_rate) { + std::vector r_peaks; + if (ecg_signal.size() < 5 || sample_rate <= 0) return r_peaks; + + const size_t n = ecg_signal.size(); + + // 1. 计算积分窗口大小 (150ms窗口) + const int win_integ = static_cast(0.15f * sample_rate + 0.5f); // 四舍五入 + if (win_integ < 1) return r_peaks; + + // 有效数据起始位置 (滤波+积分导致的延迟) + const size_t start_index = 5 + win_integ - 1; + + // 2. 带通滤波 (通过移动平均近似) + std::vector filtered(n, 0.0f); + // 低通滤波 (5点移动平均) + for (size_t i = 4; i < n; i++) { + filtered[i] = (ecg_signal[i-4] + ecg_signal[i-3] + + ecg_signal[i-2] + ecg_signal[i-1] + + ecg_signal[i]) / 5.0f; + } + + // 3. 微分 (增强QRS斜率) + std::vector diff(n, 0.0f); + for (size_t i = 5; i < n; i++) { + diff[i] = filtered[i] - filtered[i-1]; + } + + // 4. 平方 (放大高频分量) + std::vector sqrd(n, 0.0f); + for (size_t i = 0; i < n; i++) { + sqrd[i] = diff[i] * diff[i]; + } + + // 5. 移动平均积分 (突出QRS复合波) + std::vector integrated(n, 0.0f); + for (size_t i = start_index; i < n; i++) { + float sum = 0.0f; + for (int j = 0; j < win_integ; j++) { + sum += sqrd[i - j]; + } + integrated[i] = sum / win_integ; + } + + // 6. 自适应阈值检测 + float threshold = 0.0f; + float peak_value = 0.0f; + const size_t min_interval = static_cast(0.2f * sample_rate); // 200ms最小间隔 + + // 初始阈值设置 (使用前2秒数据) + const size_t init_win = std::min(static_cast(2 * sample_rate), n - start_index); + if (init_win > 10) { + auto max_it = std::max_element(integrated.begin() + start_index, + integrated.begin() + start_index + init_win); + threshold = 0.5f * (*max_it); + } else { + threshold = 0.5f * (*std::max_element(integrated.begin(), integrated.end())); + } + + // 噪声和信号峰值跟踪 + float noise_peak = 0.25f * threshold; + float signal_peak = threshold; + const float decay_rate = 0.125f; // 阈值衰减率 + + size_t last_peak = 0; + bool found_peak = false; + + for (size_t i = start_index + 1; i < n - 1; i++) { + // 检测峰值 (大于前后两点) + if (integrated[i] > integrated[i-1] && + integrated[i] > integrated[i+1] && + integrated[i] > threshold) { + + // 检查最小间隔 + if (last_peak == 0 || i - last_peak > min_interval) { + r_peaks.push_back(static_cast(i)); + last_peak = i; + found_peak = true; + peak_value = integrated[i]; + } + } + + // 自适应阈值更新 + if (found_peak) { + // 检测到峰值后更新信号阈值 + signal_peak = decay_rate * peak_value + (1 - decay_rate) * signal_peak; + found_peak = false; + } else { + // 未检测到峰值时更新噪声阈值 + if (integrated[i] > noise_peak) { + noise_peak = decay_rate * integrated[i] + (1 - decay_rate) * noise_peak; + } + } + + // 组合阈值 (信号和噪声阈值的加权平均) + threshold = 0.25f * signal_peak + 0.75f * noise_peak; + } + + return r_peaks; +} + +// 脉搏波峰检测 - 改进版本 +std::vector MetricsCalculator::detect_pulse_peaks(const std::vector& ppg_signal, float sample_rate) { + std::vector pulse_peaks; + if (ppg_signal.empty() || sample_rate <= 0) return pulse_peaks; + + const size_t n = ppg_signal.size(); + + // 1) 轻量平滑(5点移动平均),提升SNR以适应低采样率(50Hz) + std::vector smooth(n, 0.0f); + const size_t ma_win = 5; // ≈100ms@50Hz + float acc = 0.0f; + for (size_t i = 0; i < n; ++i) { + acc += ppg_signal[i]; + if (i >= ma_win) acc -= ppg_signal[i - ma_win]; + size_t denom = (i + 1 < ma_win) ? (i + 1) : ma_win; + smooth[i] = acc / static_cast(denom); + } + + // 2) 参数:窗口与最小间隔 + // 最小脉搏间隔取400ms(≈150bpm上限),避免误检重复峰 + const size_t min_interval = static_cast(0.4f * sample_rate); + // 局部比较窗口:200ms用于极值判断 + const size_t window_size = static_cast(0.2f * sample_rate); // 200ms窗口 + // 局部阈值窗口:1.5s,用于计算局部分位数阈值和显著性 + const size_t local_win = static_cast(2.0f * sample_rate); + + // 3) 使用局部分位阈值与显著性(prominence)判据 + size_t last_peak = 0; + for (size_t i = window_size; i + window_size < n; ++i) { + // 局部极大判定 + bool is_peak = true; + for (size_t j = i - window_size; j <= i + window_size; ++j) { + if (j != i && smooth[j] >= smooth[i]) { is_peak = false; break; } + } + if (!is_peak) continue; + + // 局部阈值(基线+分位幅值的一部分) + size_t l0 = (i > local_win ? i - local_win : 0); + size_t r0 = std::min(n - 1, i + local_win); + if (r0 <= l0 + 2) continue; + std::vector window(smooth.begin() + l0, smooth.begin() + r0 + 1); + float p5 = compute_percentile(window, 0.05f); + float p50 = compute_percentile(window, 0.50f); + float p95 = compute_percentile(window, 0.95f); + float loc_range = std::max(0.0f, p95 - p5); + float loc_baseline = p50; + float loc_threshold = loc_baseline + 0.15f * loc_range; // 低门槛,避免只出1峰 + + if (smooth[i] <= loc_threshold) continue; + + // 显著性:峰值高于局部中位线的幅度需达到一定比例 + float prominence = smooth[i] - loc_baseline; + if (prominence < 0.1f * loc_range) continue; + + // 间隔判定 + if (last_peak != 0 && i - last_peak <= min_interval) continue; + + pulse_peaks.push_back(static_cast(i)); + last_peak = i; + } + + return pulse_peaks; +} + +// 新增:信号质量评估 +float MetricsCalculator::calculate_signal_quality(const std::vector& signal) { + if (signal.empty()) return 0.0f; + + const size_t n = signal.size(); + + // 1. 计算信噪比 (SNR) + float signal_power = 0.0f; + float noise_power = 0.0f; + + // 使用简化的功率谱密度计算(避免复杂的FFT) + // 假设低频部分是信号,高频部分是噪声 + size_t mid_point = n / 2; + for (size_t i = 0; i < mid_point; i++) { + signal_power += signal[i] * signal[i]; + } + for (size_t i = mid_point; i < n; i++) { + noise_power += signal[i] * signal[i]; + } + + float snr = 0.0f; + if (noise_power > 0.0001f) { + snr = 10.0f * std::log10(signal_power / noise_power); + } + + // 2. 计算信号连续性 + float continuity_score = 0.0f; + int discontinuity_count = 0; + + for (size_t i = 1; i < n; i++) { + float diff = std::abs(signal[i] - signal[i-1]); + if (diff > 10.0f * std::sqrt(noise_power / n)) { // 自适应阈值 + discontinuity_count++; + } + } + + continuity_score = 100.0f * (1.0f - static_cast(discontinuity_count) / (n - 1)); + + // 3. 幅度评估 + float max_val = *std::max_element(signal.begin(), signal.end()); + float min_val = *std::min_element(signal.begin(), signal.end()); + float amplitude = max_val - min_val; + float amplitude_score = clamp(amplitude / 2.0f, 0.0f, 1.0f) * 100.0f; + + // 4. 综合质量评分 (0-100) + float quality_score = 0.0f; + + // SNR贡献 (40%) + if (snr > 20.0f) quality_score += 40.0f; + else if (snr > 10.0f) quality_score += 30.0f; + else if (snr > 5.0f) quality_score += 20.0f; + else if (snr > 0.0f) quality_score += 10.0f; + + // 连续性贡献 (30%) + quality_score += 0.3f * continuity_score; + + // 幅度贡献 (30%) + quality_score += 0.3f * amplitude_score; + + return clamp(quality_score, 0.0f, 100.0f); +} + +// 综合ECG指标计算 +std::map MetricsCalculator::calculate_all_ecg_metrics(const SensorData& ecg_data, float sample_rate) { + std::map metrics; + + const auto& signal = get_single_channel(ecg_data); + if (signal.empty()) return metrics; + + // 基础指标 + metrics["heart_rate"] = calculate_heart_rate_ecg(ecg_data, sample_rate); + metrics["t_wave_amplitude"] = calculate_t_wave_amplitude(signal); + metrics["qrs_width"] = calculate_qrs_width(signal, sample_rate); + metrics["st_offset"] = calculate_st_offset(signal, sample_rate); + metrics["signal_quality"] = calculate_signal_quality(signal); + + // 统计指标 + float sum = std::accumulate(signal.begin(), signal.end(), 0.0f); + float mean = sum / signal.size(); + + float variance = 0.0f; + for (float val : signal) { + variance += (val - mean) * (val - mean); + } + variance /= signal.size(); + + metrics["mean"] = mean; + metrics["std_dev"] = std::sqrt(std::max(0.0f, variance)); + metrics["min_value"] = *std::min_element(signal.begin(), signal.end()); + metrics["max_value"] = *std::max_element(signal.begin(), signal.end()); + metrics["peak_to_peak"] = metrics["max_value"] - metrics["min_value"]; + + // HRV指标 (如果能够检测到R峰) + auto r_peaks = detect_r_peaks(signal, sample_rate); + if (r_peaks.size() >= 2) { + std::vector rr_intervals; + for (size_t i = 1; i < r_peaks.size(); i++) { + float rr = (r_peaks[i] - r_peaks[i-1]) / sample_rate; + if (rr > 0.2f && rr < 3.0f) { + rr_intervals.push_back(rr); + } + } + + if (rr_intervals.size() >= 2) { + metrics["sdnn"] = calculate_sdnn(rr_intervals); + metrics["rmssd"] = calculate_rmssd(rr_intervals); + metrics["pnn50"] = calculate_pnn50(rr_intervals); + metrics["triangular_index"] = calculate_triangular_index(rr_intervals); + } + } + + return metrics; +} + +// 综合PPG指标计算 +std::map MetricsCalculator::calculate_all_ppg_metrics(const SensorData& ppg_data, float sample_rate) { + std::map metrics; + + if (!std::holds_alternative>>(ppg_data.channel_data) || + std::get>>(ppg_data.channel_data).size() < 1) { + return metrics; + } + + const auto& channels = std::get>>(ppg_data.channel_data); + const auto& signal = channels[0]; // 使用第一通道 + + if (signal.empty()) return metrics; + + // 基础指标 + metrics["heart_rate"] = calculate_heart_rate_ppg(signal, sample_rate); + metrics["spo2"] = calculate_spo2(ppg_data); + metrics["perfusion_index"] = calculate_perfusion_index(ppg_data); + metrics["pulse_width"] = calculate_pulse_width(signal); + metrics["amplitude_ratio"] = calculate_amplitude_ratio(ppg_data); + metrics["signal_quality"] = calculate_signal_quality(signal); + + // 统计指标 + float sum = std::accumulate(signal.begin(), signal.end(), 0.0f); + float mean = sum / signal.size(); + + float variance = 0.0f; + for (float val : signal) { + variance += (val - mean) * (val - mean); + } + variance /= signal.size(); + + metrics["mean"] = mean; + metrics["std_dev"] = std::sqrt(std::max(0.0f, variance)); + metrics["min_value"] = *std::min_element(signal.begin(), signal.end()); + metrics["max_value"] = *std::max_element(signal.begin(), signal.end()); + metrics["peak_to_peak"] = metrics["max_value"] - metrics["min_value"]; + + // 多通道指标 (如果有多个通道) + if (channels.size() >= 2) { + const auto& red_channel = channels[0]; + const auto& ir_channel = channels[1]; + + if (!red_channel.empty() && !ir_channel.empty()) { + // 红光和红外光的统计信息 + float red_mean = std::accumulate(red_channel.begin(), red_channel.end(), 0.0f) / red_channel.size(); + float ir_mean = std::accumulate(ir_channel.begin(), ir_channel.end(), 0.0f) / ir_channel.size(); + + metrics["red_channel_mean"] = red_mean; + metrics["ir_channel_mean"] = ir_mean; + metrics["red_ir_ratio"] = (ir_mean > 0.0001f) ? red_mean / ir_mean : 0.0f; + } + } + + return metrics; +} + +// 综合HRV指标计算 +std::map MetricsCalculator::calculate_all_hrv_metrics(const std::vector& rr_intervals) { + std::map metrics; + + if (rr_intervals.size() < 2) return metrics; + + // 过滤异常RR间期 + std::vector filtered_rr; + for (float rr : rr_intervals) { + if (rr > 0.2f && rr < 3.0f) { // 200ms-3s范围 + filtered_rr.push_back(rr); + } + } + + if (filtered_rr.size() < 2) return metrics; + + // 基础HRV指标 + metrics["sdnn"] = calculate_sdnn(filtered_rr); + metrics["rmssd"] = calculate_rmssd(filtered_rr); + metrics["pnn50"] = calculate_pnn50(filtered_rr); + metrics["triangular_index"] = calculate_triangular_index(filtered_rr); + + // 统计指标 + float mean_rr = std::accumulate(filtered_rr.begin(), filtered_rr.end(), 0.0f) / filtered_rr.size(); + metrics["mean_rr"] = mean_rr; + metrics["mean_hr"] = 60.0f / mean_rr; // 平均心率 + + // 几何指标 + float geometric_mean = 1.0f; + for (float rr : filtered_rr) { + geometric_mean *= rr; + } + geometric_mean = std::pow(geometric_mean, 1.0f / filtered_rr.size()); + metrics["geometric_mean"] = geometric_mean; + + // 变异系数 (CV = SDNN / mean_RR * 100%) + if (mean_rr > 0.0001f) { + metrics["cv"] = (metrics["sdnn"] / mean_rr) * 100.0f; + } + + // 总功率 (所有RR间期的方差) + float total_power = 0.0f; + for (float rr : filtered_rr) { + total_power += (rr - mean_rr) * (rr - mean_rr); + } + total_power /= filtered_rr.size(); + metrics["total_power"] = total_power; + + return metrics; +} \ No newline at end of file diff --git a/app/src/main/cpp/src/signal_processor.cpp b/app/src/main/cpp/src/signal_processor.cpp new file mode 100644 index 0000000..70cb937 --- /dev/null +++ b/app/src/main/cpp/src/signal_processor.cpp @@ -0,0 +1,1573 @@ +#include "cpp/signal_processor.h" +#include +#include +#include +#include + +// 自定义clamp函数,替代std::clamp(C++17特性) +template +T clamp(T value, T min_val, T max_val) { + if (value < min_val) return min_val; + if (value > max_val) return max_val; + return value; +} + +// 新增:简化的通道级滤波处理(测试版本) +std::vector SignalProcessor::process_channel_based_filtering_simple( + const std::vector& data_packets) { + + std::cout << "=== 开始简化版通道级滤波处理 ===" << std::endl; + + if (data_packets.empty()) { + std::cout << "输入数据包为空,返回空结果" << std::endl; + return {}; + } + + std::cout << "输入数据包数量: " << data_packets.size() << std::endl; + + // 按数据类型分组 + std::map> grouped_data; + for (const auto& packet : data_packets) { + grouped_data[packet.data_type].push_back(packet); + } + + std::cout << "按数据类型分组完成,共 " << grouped_data.size() << " 种类型" << std::endl; + + std::vector processed_packets; + + // 对每种数据类型分别处理 + for (auto& [data_type, packets] : grouped_data) { + if (packets.empty()) continue; + + std::cout << "处理数据类型: " << static_cast(data_type) << ",数据包数量: " << packets.size() << std::endl; + + // 获取第一个数据包作为模板 + SensorData template_packet = packets[0]; + + // 检查通道数据类型 + if (!std::holds_alternative>>(packets[0].channel_data)) { + std::cout << "警告: 数据类型 " << static_cast(data_type) << " 不是多通道格式,跳过" << std::endl; + continue; + } + + // 获取通道信息 + auto& first_channels = std::get>>(packets[0].channel_data); + size_t num_channels = first_channels.size(); + size_t samples_per_packet = first_channels.empty() ? 0 : first_channels[0].size(); + + std::cout << "通道数量: " << num_channels << ", 每包采样点数: " << samples_per_packet << std::endl; + + if (num_channels == 0 || samples_per_packet == 0) { + std::cout << "警告: 通道数据无效,跳过此类型" << std::endl; + continue; + } + + // 计算总采样点数 + size_t total_samples = samples_per_packet * packets.size(); + std::cout << "总采样点数: " << total_samples << std::endl; + + // 创建合并后的数据包 + SensorData merged_packet = template_packet; + merged_packet.packet_sn = 0; // 合并后的包序号为0 + + // 创建合并后的通道数据 + auto& merged_channels = merged_packet.channel_data.emplace>>(); + merged_channels.resize(num_channels); + + // 合并所有数据包的通道数据 + for (size_t ch = 0; ch < num_channels; ch++) { + merged_channels[ch].reserve(total_samples); + + for (const auto& packet : packets) { + if (auto* channels = std::get_if>>(&packet.channel_data)) { + if (ch < channels->size() && !(*channels)[ch].empty()) { + merged_channels[ch].insert(merged_channels[ch].end(), + (*channels)[ch].begin(), + (*channels)[ch].end()); + } + } + } + + std::cout << "通道 " << ch << " 合并完成,采样点数: " << merged_channels[ch].size() << std::endl; + } + + // 对合并后的数据进行基本信号处理 + std::cout << "开始基本信号处理..." << std::endl; + for (size_t ch = 0; ch < num_channels; ch++) { + if (merged_channels[ch].empty()) continue; + + try { + // 根据数据类型应用不同的基本处理 + switch (data_type) { + case DataType::ECG_2LEAD: + // ECG 2导联基本处理:去除直流分量 + merged_channels[ch] = remove_dc_offset(merged_channels[ch]); + break; + case DataType::ECG_12LEAD: + // ECG 12导联专业滤波处理 + std::cout << "通道 " << ch << " 开始12导联心电专业滤波..." << std::endl; + + // 1. 去除直流分量 + merged_channels[ch] = remove_dc_offset(merged_channels[ch]); + std::cout << " - 直流分量去除完成" << std::endl; + + // 2. 0.1Hz高通滤波(去除基线漂移,比0.5Hz更温和) + merged_channels[ch] = filter(merged_channels[ch], 250.0, 0, 0.1, filtertype::highpass); + std::cout << " - 0.1Hz高通滤波完成" << std::endl; + + // 3. 50Hz陷波滤波(去除工频干扰,带宽1.0Hz更精确) + merged_channels[ch] = filter(merged_channels[ch], 250.0, 49.5, 50.5, filtertype::notchpass); + std::cout << " - 50Hz陷波滤波完成" << std::endl; + + std::cout << "通道 " << ch << " 12导联心电滤波处理完成" << std::endl; + break; + case DataType::EEG: + // EEG基本处理:去除直流分量 + merged_channels[ch] = remove_dc_offset(merged_channels[ch]); + break; + case DataType::PPG: + // PPG基本处理:去除直流分量 + merged_channels[ch] = remove_dc_offset(merged_channels[ch]); + // 2. 0.5-8Hz带通滤波(更精确的PPG频带) + merged_channels[ch] = bandpass_filter(merged_channels[ch], 50, 0.5, 8.0); + // // 3. 50Hz陷波滤波(去除工频干扰) + //merged_channels[ch] = filter(merged_channels[ch], 50, 49.5, 50.5, filtertype::notchpass); + // // 4. 运动伪迹检测和去除(优化版本) + merged_channels[ch] = remove_motion_artifacts(merged_channels[ch], 50); + break; + case DataType::RESPIRATION: + // 呼吸信号基本处理:去除直流分量 + merged_channels[ch] = remove_dc_offset(merged_channels[ch]); + break; + default: + // 通用处理:去除直流分量 + merged_channels[ch] = remove_dc_offset(merged_channels[ch]); + break; + } + + std::cout << "通道 " << ch << " 基本处理完成" << std::endl; + } catch (const std::exception& e) { + std::cerr << "通道 " << ch << " 处理失败: " << e.what() << std::endl; + // 继续处理其他通道 + } + } + + // 添加到处理结果中 + processed_packets.push_back(merged_packet); + + std::cout << "数据类型 " << static_cast(data_type) << " 处理完成" << std::endl; + } + + std::cout << "=== 简化版通道级滤波处理完成 ===" << std::endl; + std::cout << "总共创建 " << processed_packets.size() << " 个合并数据包" << std::endl; + + return processed_packets; +} + +// 新增:对完整通道数据应用滤波器 +std::vector> SignalProcessor::apply_channel_filters( + const std::vector>& channels, DataType data_type) { + + std::vector> filtered_channels = channels; + + switch (data_type) { + case DataType::EEG: + filtered_channels = apply_eeg_filters(channels); + break; + case DataType::ECG_2LEAD: + case DataType::ECG_12LEAD: + filtered_channels = apply_ecg_filters(channels); + break; + case DataType::PPG: + filtered_channels = apply_ppg_filters(channels); + break; + case DataType::RESPIRATION: + filtered_channels = apply_respiration_filters(channels); + break; + case DataType::SNORE: + filtered_channels = apply_snore_filters(channels); + break; + case DataType::STETHOSCOPE: + filtered_channels = apply_stethoscope_filters(channels); + break; + default: + // 通用滤波 + for (auto& channel : filtered_channels) { + channel = bandpass_filter(channel, 250.0, 0.5, 45.0); + } + break; + } + + return filtered_channels; +} + +// 新增:EEG通道滤波 +std::vector> SignalProcessor::apply_eeg_filters( + const std::vector>& channels) { + + const double SAMPLE_RATE = 250.0; + std::vector> filtered_channels = channels; + + if (channels.size() < 8) return filtered_channels; + + // 分离EEG和EOG通道 + std::vector> eeg_channels(channels.begin(), channels.begin() + 6); + std::vector> eog_channels(channels.begin() + 6, channels.end()); + + // 处理EEG通道 + for (auto& channel : eeg_channels) { + // 眼电伪迹补偿 + if (eog_channels.size() >= 2) { + channel = compensate_eog_artifact(channel, eog_channels[0], eog_channels[1]); + } + + // 50Hz自适应陷波滤波 + channel = adaptive_notch_filter(channel, SAMPLE_RATE, 50.0, 5.0); + + // 0.5-45Hz带通滤波 + channel = bandpass_filter(channel, SAMPLE_RATE, 0.5, 45.0); + } + + // 处理EOG通道 + for (auto& channel : eog_channels) { + channel = bandpass_filter(channel, SAMPLE_RATE, 0.5, 30.0); + } + + // 合并处理后的通道 + filtered_channels.clear(); + filtered_channels.insert(filtered_channels.end(), eeg_channels.begin(), eeg_channels.end()); + filtered_channels.insert(filtered_channels.end(), eog_channels.begin(), eog_channels.end()); + + return filtered_channels; +} + +// 新增:ECG通道滤波 +std::vector> SignalProcessor::apply_ecg_filters( + const std::vector>& channels) { + + const double SAMPLE_RATE = 250.0; + std::vector> filtered_channels = channels; + + std::cout << "开始ECG专业滤波处理..." << std::endl; + + for (size_t ch = 0; ch < filtered_channels.size(); ch++) { + if (filtered_channels[ch].empty()) continue; + + std::cout << "处理ECG通道 " << ch << "/" << filtered_channels.size() << std::endl; + + try { + // 1. 去除直流分量 + filtered_channels[ch] = remove_dc_offset(filtered_channels[ch]); + std::cout << " 通道 " << ch << " - 直流分量去除完成" << std::endl; + + // 2. 0.1Hz高通滤波(去除基线漂移,比0.5Hz更温和) + filtered_channels[ch] = filter(filtered_channels[ch], SAMPLE_RATE, 0, 0.1, filtertype::highpass); + std::cout << " 通道 " << ch << " - 0.1Hz高通滤波完成" << std::endl; + + // 3. 50Hz陷波滤波(去除工频干扰,带宽1.0Hz更精确) + filtered_channels[ch] = filter(filtered_channels[ch], SAMPLE_RATE, 49.5, 50.5, filtertype::notchpass); + std::cout << " 通道 " << ch << " - 50Hz陷波滤波完成" << std::endl; + + // 4. 25-40Hz带阻滤波(去除肌电干扰) + filtered_channels[ch] = filter(filtered_channels[ch], SAMPLE_RATE, 25.0, 40.0, filtertype::bandstop); + std::cout << " 通道 " << ch << " - 25-40Hz带阻滤波完成" << std::endl; + + std::cout << "ECG通道 " << ch << " 滤波处理完成" << std::endl; + + } catch (const std::exception& e) { + std::cerr << "ECG通道 " << ch << " 滤波失败: " << e.what() << std::endl; + // 继续处理其他通道 + } + } + + std::cout << "ECG专业滤波处理完成" << std::endl; + return filtered_channels; +} + +// 新增:PPG通道滤波 +std::vector> SignalProcessor::apply_ppg_filters( + const std::vector>& channels) { + + const double SAMPLE_RATE = 50.0; + std::vector> filtered_channels = channels; + + for (auto& channel : filtered_channels) { + // 1. 移除直流分量 + channel = remove_dc_offset(channel); + + // 2. 0.5-8Hz带通滤波(更精确的PPG频带) + channel = bandpass_filter(channel, SAMPLE_RATE, 0.5, 8.0); + + // 3. 50Hz陷波滤波(去除工频干扰) + channel = filter(channel, SAMPLE_RATE, 49.5, 50.5, filtertype::notchpass); + + // 4. 运动伪迹检测和去除(简单版本) + channel = remove_motion_artifacts(channel, SAMPLE_RATE); + } + + return filtered_channels; +} + +// 新增:呼吸通道滤波 +std::vector> SignalProcessor::apply_respiration_filters( + const std::vector>& channels) { + + const double SAMPLE_RATE = 100.0; + std::vector> filtered_channels = channels; + + for (auto& channel : filtered_channels) { + // 0.1Hz高通滤波 + channel = filter(channel, SAMPLE_RATE, 0, 0.1, filtertype::highpass); + + // 50Hz陷波滤波 + channel = adaptive_notch_filter(channel, SAMPLE_RATE, 50.0, 5.0); + + // 振幅归一化 + normalize_amplitude(channel); + } + + return filtered_channels; +} + +// 新增:打鼾通道滤波 +std::vector> SignalProcessor::apply_snore_filters( + const std::vector>& channels) { + + const double SAMPLE_RATE = 2000.0; + std::vector> filtered_channels = channels; + + for (auto& channel : filtered_channels) { + // 50-2000Hz带通滤波 + channel = bandpass_filter(channel, SAMPLE_RATE, 50.0, 2000.0); + + // 振幅归一化 + normalize_amplitude(channel); + } + + return filtered_channels; +} + +// 新增:听诊器通道滤波 +std::vector> SignalProcessor::apply_stethoscope_filters( + const std::vector>& channels) { + + const double SAMPLE_RATE = 4000.0; + std::vector> filtered_channels = channels; + + for (auto& channel : filtered_channels) { + // 20-2000Hz带通滤波 + channel = bandpass_filter(channel, SAMPLE_RATE, 20.0, 2000.0); + + // 振幅归一化 + normalize_amplitude(channel); + } + + return filtered_channels; +} + +SensorData SignalProcessor::preprocess_generic(const SensorData& data) { + SensorData processed = data; + + // 通用预处理:带通滤波 + if (auto* channels = std::get_if>>(&processed.channel_data)) { + for (auto& channel : *channels) { + channel = bandpass_filter(channel, 100.0, 0.5, 45.0); + } + } + return processed; +} +SensorData SignalProcessor::preprocess_signals(const SensorData& raw_data ) { + // 1. 创建处理后的数据结构 + SensorData processed = raw_data; + + // 2. 设备特定预处理 + switch (processed.data_type) { + case DataType::EEG: + processed = preprocess_eeg(raw_data); + break; + case DataType::ECG_2LEAD: + processed = preprocess_ecg_2lead(raw_data); + break; + case DataType::ECG_12LEAD: + processed = preprocess_ecg_12lead(raw_data); + break; + case DataType::PPG: + processed = preprocess_ppg(raw_data); + break; + case DataType::RESPIRATION: + processed = preprocess_respiration(raw_data); + break; + case DataType::SNORE: + processed = preprocess_snore(raw_data); + break; + case DataType::STETHOSCOPE: + processed = preprocess_stethoscope(raw_data); + break; + default: + processed = preprocess_generic(raw_data); + break; + } + + // 3. 通用后处 + return processed; +} + +SensorData SignalProcessor::preprocess_eeg(const SensorData& data) { +const double SAMPLE_RATE = 250.0; // 脑电标准采样率250Hz + + SensorData processed = data; + + // 获取通道数据 + auto& channels = std::get>>(processed.channel_data); + if (channels.size() < 8) { + throw std::runtime_error("Invalid channel count for EEG"); + } + + // 分离EEG和EOG通道 + std::vector> eeg_channels(channels.begin(), channels.begin() + 6); + std::vector> 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; +} +SensorData SignalProcessor::preprocess_ecg_2lead(const SensorData& data) +{ + const double SAMPLE_RATE = 250.0; // 2导联心电标准采样率500Hz + + SensorData processed = data; + + // 获取通道数据 + auto& channels = std::get>>(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; + +} +// 12导联心电预处理函数 +SensorData SignalProcessor::preprocess_ecg_12lead(const SensorData& data) { + const double SAMPLE_RATE = 250.0; // 12导联心电标准采样率250.0Hz + + // 创建处理后的数据结构 + SensorData processed = data; + + // 获取通道数据 + auto& channels = std::get>>(processed.channel_data); + if (channels.size() != 12) { + throw std::runtime_error("Invalid channel count for 12-lead ECG"); + } + + + // 对每个导联独立进行信号处理 + for (auto& channel : channels) { + // 1. 0.5Hz高通滤波 (去除基线漂移) + //channel = remove_dc_offset(channel); + channel = filter(channel, SAMPLE_RATE,0,0.5, filtertype::highpass); + + // 2. 50Hz自适应陷波滤波 (去除工频干扰) + channel = filter(channel, SAMPLE_RATE, 49.5, 51.5, filtertype::notchpass); + + // 3. 25-40Hz带阻滤波 (去除肌电干扰) + channel = filter(channel, SAMPLE_RATE, 0.5, 0.6, filtertype::bandstop); + } + + // 计算并存储信号质量指数 + 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; +} +SensorData SignalProcessor::preprocess_ppg(const SensorData& data) { + // 1. 创建处理后的数据结构 + double SAMPLE_RATE = 50; + SensorData processed = data; + + // 2. 获取通道数据(红光和红外光) + auto& channels = std::get>>(processed.channel_data); + if (channels.size() < 2) { + throw std::runtime_error("PPG数据需要至少两个通道(红光和红外光)"); + } + + std::cout << "开始PPG信号预处理,采样率: " << SAMPLE_RATE << "Hz" << std::endl; + + // 3. 预处理红光通道(通道0) + std::cout << "处理红光通道..." << std::endl; + // a. 移除直流分量 + channels[0] = remove_dc_offset(channels[0]); + + // b. 带通滤波 (0.5-8Hz,更精确的PPG频带) + channels[0] = bandpass_filter(channels[0], SAMPLE_RATE, 0.5, 8.0); + + // c. 50Hz陷波滤波(去除工频干扰) + channels[0] = filter(channels[0], SAMPLE_RATE, 49.5, 50.5, filtertype::notchpass); + + // d. 运动伪迹检测和去除 + channels[0] = remove_motion_artifacts(channels[0], SAMPLE_RATE); + + // 4. 预处理红外光通道(通道1) + std::cout << "处理红外光通道..." << std::endl; + // a. 移除直流分量 + channels[1] = remove_dc_offset(channels[1]); + + // b. 带通滤波 (0.5-8Hz) + channels[1] = bandpass_filter(channels[1], SAMPLE_RATE, 0.5, 8.0); + + // c. 50Hz陷波滤波 + channels[1] = filter(channels[1], SAMPLE_RATE, 49.5, 50.5, filtertype::notchpass); + + // d. 运动伪迹检测和去除 + channels[1] = remove_motion_artifacts(channels[1], SAMPLE_RATE); + + // 5. 计算信号质量指数(SQI) + std::cout << "计算PPG信号质量..." << std::endl; + processed.sqi = calculate_PPG_sqi(channels[0], channels[1]); + + // 6. 更新附加数据(心率和血氧) + if (processed.sqi > 0.7) { // 降低阈值,提高容错性 + std::cout << "信号质量良好 (SQI: " << processed.sqi << ")" << std::endl; + // 高质量信号,保持设备提供的值 + } else { + std::cout << "信号质量较差 (SQI: " << processed.sqi << "),标记为不可靠" << std::endl; + // 低质量信号,标记为不可靠 + processed.additional.hr = 0; + processed.additional.spo2 = 0; + } + + std::cout << "PPG预处理完成,最终SQI: " << processed.sqi << std::endl; + return processed; +} +SensorData SignalProcessor::preprocess_respiration(const SensorData& data) +{ + const double SAMPLE_RATE = 100.0; // 呼吸信号标准采样率100Hz + + SensorData processed = data; + + // 获取通道数据 + auto& channels = std::get>>(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; +} +SensorData SignalProcessor::preprocess_snore(const SensorData& data) +{ + const double SAMPLE_RATE = 2000.0; // 鼾声信号标准采样率2000Hz + + SensorData processed = data; + + // 获取通道数据 + auto& channel = std::get>(processed.channel_data); + + // 1. 50-2000Hz带通滤波 (保留有效频段) + std::vector 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; +} +SensorData SignalProcessor::preprocess_stethoscope(const SensorData& data) +{ +const double SAMPLE_RATE = 4000.0; // 听诊信号标准采样率4000Hz + + SensorData processed = data; + + // 获取通道数据 + auto& channels = std::get>>(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; +} +// 添加预处理辅助函数 +std::vector SignalProcessor::remove_dc_offset(const std::vector& signal) { + if (signal.empty()) return {}; // 处理空输入 + std::vector result = signal; + float dc_remove = 0; + for(auto& val:signal) dc_remove += val; + dc_remove /= result.size(); + for(auto& value:result) value -= dc_remove; return result; +} +std::vector SignalProcessor::apply_gain(const std::vector& signal, float gain) { + std::vector result = signal; + return result; +} +// 实现眼电伪迹补偿 +std::vector SignalProcessor::compensate_eog_artifact(const std::vector& eeg, + const std::vector& eog1, + const std::vector& eog2) { + std::vector result = eeg; + return result; +} +// 实现自适应陷波滤波器(成员函数) +std::vector SignalProcessor::adaptive_notch_filter(const std::vector& input, + double sample_rate, + double target_freq, + double bandwidth) { + if (input.empty()) return {}; + + // 使用更稳定的实现 + const double omega0 = 2 * M_PI * target_freq / sample_rate; + const double alpha = sin(omega0) * sinh(log(2) / 2 * bandwidth * omega0 / sin(omega0)); + + // 系数计算 + const double b0 = 1.0; + const double b1 = -2 * cos(omega0); + const double b2 = 1.0; + const double a0 = 1 + alpha; + const double a1 = -2 * cos(omega0); + const double a2 = 1 - alpha; + + // 归一化系数 + const double inv_a0 = 1.0 / a0; + const double nb0 = b0 * inv_a0; + const double nb1 = b1 * inv_a0; + const double nb2 = b2 * inv_a0; + const double na1 = a1 * inv_a0; + const double na2 = a2 * inv_a0; + + // 应用滤波器 + std::vector output(input.size()); + double x1 = 0.0, x2 = 0.0; + double y1 = 0.0, y2 = 0.0; + + for (size_t n = 0; n < input.size(); ++n) { + double x0 = input[n]; + double y = nb0 * x0 + nb1 * x1 + nb2 * x2 - na1 * y1 - na2 * y2; + + // 防止不稳定 + if (!std::isfinite(y)) y = 0.0; + + output[n] = static_cast(y); + + // 更新状态 + x2 = x1; + x1 = x0; + y2 = y1; + y1 = y; + } + + return output; +} + + +std::vector SignalProcessor::filter(const std::vector& input, + double sample_rate, + double low_cutoff, + double high_cutoff, + filtertype type){ + + switch(type) + { + case filtertype::lowpass: + return Lowpass_filter(input,sample_rate,low_cutoff); + case filtertype::highpass: + return Highpass_filter(input,sample_rate,high_cutoff); + case filtertype::notchpass: + return adaptive_notch_filter(input,sample_rate,0.5*(high_cutoff+low_cutoff),high_cutoff-low_cutoff); + case filtertype::bandpass: + return bandpass_filter(input,sample_rate,low_cutoff,high_cutoff); + case filtertype::bandstop: + return bandstop_filter(input,sample_rate,low_cutoff,high_cutoff); + default: + return input; + } +} +//低通滤波器 +std::vector SignalProcessor::Lowpass_filter(const std::vector& input, + double sample_rate, + double low_cutoff){ + if (input.empty() || low_cutoff <= 0 || low_cutoff >= sample_rate/2) { + return input; + } + + const double nyquist = sample_rate / 2.0; + const double omega = 2.0 * 3.14159265358979323846 * low_cutoff / sample_rate; + const double k = 1.0 / tan(omega / 2.0); // 双线性变换预矫正 + const double k2 = k * k; + const double sqrt2 = std::sqrt(2.0); + + // 计算归一化系数 + const double a0 = k2 + sqrt2 * k + 1; + const double b0 = 1.0 / a0; + const double b1 = 2 * b0; + const double b2 = b0; + const double a1 = 2 * (1 - k2) * b0; + const double a2 = (k2 - sqrt2 * k + 1) * b0; + + // 应用滤波器 + std::vector output(input.size()); + double x1 = 0.0, x2 = 0.0; // 输入延迟 + double y1 = 0.0, y2 = 0.0; // 输出延迟 + + for (size_t i = 0; i < input.size(); ++i) { + const double x0 = input[i]; + const double y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; + + output[i] = static_cast(y0); + + // 更新延迟 + x2 = x1; + x1 = x0; + y2 = y1; + y1 = y0; + } + + return output; +} +//改进后的高通滤波实现 +std::vector SignalProcessor::Highpass_filter(const std::vector& input, + double sample_rate, + double cutoff) { + if (input.empty()) return input; + int a = input[0]; + // 1. 计算滤波器系数(二阶巴特沃斯) + const double omega = 2.0 * 3.14159265358979323846 * cutoff / sample_rate; + const double sn = sin(omega); + const double cs = cos(omega); + const double alpha = sn / (2.0 * 0.707); // Q=0.707 (Butterworth) + + const double b0 = (1 + cs) / 2.0; + const double b1 = -(1 + cs); + const double b2 = b0; + const double a0 = 1 + alpha; + const double a1 = -2 * cs; + const double a2 = 1 - alpha; + + // 2. 归一化系数 + const double inv_a0 = 1.0 / a0; + const double nb0 = b0 * inv_a0; + const double nb1 = b1 * inv_a0; + const double nb2 = b2 * inv_a0; + const double na1 = a1 * inv_a0; + const double na2 = a2 * inv_a0; + + // 3. 初始化状态(使用前两个样本值) + double x1 = input.size() > 0 ? input[0] : 0; + double x2 = x1; + double y1 = 0; + double y2 = 0; + + // 4. 应用滤波器(处理边界条件) + std::vector output(input.size()); + for (size_t n = 0; n < input.size(); ++n) { + const double x0 = input[n]; + + // 计算当前输出 + double y = nb0 * x0 + nb1 * x1 + nb2 * x2 - na1 * y1 - na2 * y2; + + // 5. 稳定化处理(防止NaN/Inf) + if (!std::isfinite(y)) y = 0.0; + + output[n] = static_cast(y); + + // 6. 更新状态变量 + x2 = x1; + x1 = x0; + y2 = y1; + y1 = y; + } + + // 7. 可选:去除初始瞬态(前100ms数据) + /*const size_t transient_samples = static_cast(0.1 * sample_rate); + if (output.size() > transient_samples) { + const float initial_value = output[transient_samples]; + for (size_t i = 0; i < transient_samples; ++i) { + output[i] = initial_value; + } + }*/ + + return output; +} +// 带通滤波器 +std::vector SignalProcessor::bandpass_filter(const std::vector& input, + double sample_rate, + double low_cutoff, + double high_cutoff) { + std::vector output(input.size(), 0.0f); + + // 计算滤波器系数 + double omega0 = 2 * 3.14159265358979323846 * (low_cutoff + high_cutoff) / (2 * sample_rate); + double BW = (high_cutoff - low_cutoff) / sample_rate; + double Q = (low_cutoff + high_cutoff) / (2 * (high_cutoff - low_cutoff)); + + double alpha = sin(omega0) / (2 * Q); + double b0 = alpha; + double b1 = 0; + double b2 = -alpha; + double a0 = 1 + alpha; + double a1 = -2 * cos(omega0); + double a2 = 1 - alpha; + + // 归一化系数 + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + // 初始化滤波器状态 + double x1 = 0, x2 = 0; + double y1 = 0, y2 = 0; + + // 应用滤波器 + for (size_t n = 0; n < input.size(); ++n) { + double x0 = input[n]; + output[n] = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; + + // 更新状态 + x2 = x1; + x1 = x0; + y2 = y1; + y1 = output[n]; + } + + return output; +} +//带阻滤波器 +std::vector SignalProcessor::bandstop_filter(const std::vector& input, + double sample_rate, + double low_cutoff, + double high_cutoff) +{ + if (input.empty()) return {}; + if (low_cutoff >= high_cutoff) { + throw std::invalid_argument("Low cutoff must be less than high cutoff"); + } + if (input.size() < 4) return input; // 太短的信号无法有效滤波 + + const double f0 = (low_cutoff + high_cutoff) / 2.0; + const double bw = high_cutoff - low_cutoff; + + // 1. 使用双线性变换进行频率预矫正 + const double T = 1.0 / sample_rate; + const double w0 = 2.0 * 3.14159265358979323846 * f0; + const double wd = 2.0 * 3.14159265358979323846 * bw; + + // 预矫正模拟频率 + const double wa = 2.0 / T * tan(w0 * T / 2.0); + const double Ba = 2.0 / T * tan(wd * T / 2.0); + + // 2. 计算Butterworth滤波器系数 + const double Q = wa / Ba; // 更精确的Q值计算 + const double alpha = sin(w0 * T) / (2 * Q); + + const double b0 = 1.0; + const double b1 = -2.0 * cos(w0 * T); + const double b2 = 1.0; + const double a0 = 1.0 + alpha; + const double a1 = -2.0 * cos(w0 * T); + const double a2 = 1.0 - alpha; + + // 3. 更精确的系数归一化 + const double gain = 1.0 / a0; // 保证通带增益为1 + const double nb0 = b0 * gain; + const double nb1 = b1 * gain; + const double nb2 = b2 * gain; + const double na1 = a1 * gain; + const double na2 = a2 * gain; + + // 4. 应用滤波器(带合理状态初始化) + std::vector output(input.size()); + double x1 = input[0], x2 = input[0]; // 输入状态初始化 + double y1 = 0.0, y2 = 0.0; // 输出状态初始化 + + // 使用前两个样本计算初始输出状态 + if (input.size() >= 2) { + const double x0 = input[0]; + y1 = nb0 * x0 + nb1 * x1 + nb2 * x2; + x2 = x1; + x1 = x0; + } + + for (size_t n = 0; n < input.size(); ++n) { + const double x0 = input[n]; + double y = nb0 * x0 + nb1 * x1 + nb2 * x2 - na1 * y1 - na2 * y2; + + // 5. 添加输出限幅保护 + const double input_max = *std::max_element(input.begin(), input.end()); + const double safety_margin = 2.0 * input_max; + if (std::abs(y) > safety_margin) { + y = std::copysign(safety_margin, y); + } + + output[n] = static_cast(y); + + // 更新状态 + x2 = x1; + x1 = x0; + y2 = y1; + y1 = y; + } + return output; +} +// 运动补偿 +std::vector SignalProcessor::compensate_motion_artifact(const std::vector& ppg, + const std::vector& motion) { + std::vector output = ppg; + return ppg; +} +// 辅助函数:计算PPG信号质量指数 +float SignalProcessor::calculate_PPG_sqi(const std::vector& red_channel, + const std::vector& ir_channel) { + if (red_channel.empty() || ir_channel.empty()) return 0.0f; + if (red_channel.size() != ir_channel.size()) return 0.0f; + + const size_t min_samples = 100; // 至少需要100个样本 + if (red_channel.size() < min_samples) return 0.0f; + + // 1. 信号幅度检测 + float red_max = *std::max_element(red_channel.begin(), red_channel.end()); + float red_min = *std::min_element(red_channel.begin(), red_channel.end()); + float red_pp = red_max - red_min; + + float ir_max = *std::max_element(ir_channel.begin(), ir_channel.end()); + float ir_min = *std::min_element(ir_channel.begin(), ir_channel.end()); + float ir_pp = ir_max - ir_min; + + // 检查信号幅度是否合理 + if (red_pp < 0.01f || ir_pp < 0.01f) return 0.0f; + + // 2. 信噪比计算 + float red_snr = calculate_snr(red_channel); + float ir_snr = calculate_snr(ir_channel); + + // 3. 信号连续性检测(检测信号丢失) + int red_gaps = 0, ir_gaps = 0; + const float gap_threshold = 0.001f; // 间隙阈值 + + for (size_t i = 1; i < red_channel.size(); ++i) { + if (std::abs(red_channel[i] - red_channel[i-1]) > gap_threshold) { + red_gaps++; + } + if (std::abs(ir_channel[i] - ir_channel[i-1]) > gap_threshold) { + ir_gaps++; + } + } + + float red_continuity = 1.0f - (float)red_gaps / red_channel.size(); + float ir_continuity = 1.0f - (float)ir_gaps / ir_channel.size(); + + // 4. 红光和红外光信号相关性 + float correlation = calculate_correlation(red_channel, ir_channel); + + // 5. 综合质量评分 + float sqi = 0.0f; + + // 幅度因子(权重0.2) + float amp_factor = std::min(red_pp, ir_pp) / std::max(red_pp, ir_pp); + + // SNR因子(权重0.3) + float snr_factor = (red_snr + ir_snr) / 2.0f; + + // 连续性因子(权重0.2) + float continuity_factor = (red_continuity + ir_continuity) / 2.0f; + + // 相关性因子(权重0.3) + float corr_factor = std::max(0.0f, correlation); + + // 加权平均 + sqi = 0.2f * amp_factor + 0.3f * snr_factor + 0.2f * continuity_factor + 0.3f * corr_factor; + + // 确保在[0,1]范围内 + return clamp(sqi, 0.0f, 1.0f); +} + +// 增强版运动伪迹检测和去除函数 +std::vector SignalProcessor::remove_motion_artifacts(const std::vector& signal, double sample_rate) { + if (signal.empty()) return signal; + + std::vector result = signal; + const size_t window_size = static_cast(0.5 * sample_rate); // 0.5秒窗口 + + if (signal.size() < window_size) return result; + + // 使用滑动窗口检测运动伪迹 + for (size_t i = window_size; i < signal.size(); ++i) { + // 计算窗口内的统计特性 + float sum = 0.0f, sum_sq = 0.0f; + for (size_t j = i - window_size; j < i; ++j) { + sum += signal[j]; + sum_sq += signal[j] * signal[j]; + } + + float mean = sum / window_size; + float variance = (sum_sq / window_size) - (mean * mean); + float std_dev = std::sqrt(std::max(0.0f, variance)); + + // 检测异常值(可能的运动伪迹) + float current_val = signal[i]; + float z_score = std::abs(current_val - mean) / (std_dev + 1e-6f); + + // 如果Z-score > 3,认为是运动伪迹 + if (z_score > 3.0f) { + // 使用中值滤波替换异常值 + std::vector window_values; + for (size_t j = std::max(static_cast(0), i - window_size/2); + j < std::min(signal.size(), i + window_size/2); ++j) { + window_values.push_back(signal[j]); + } + + if (!window_values.empty()) { + std::sort(window_values.begin(), window_values.end()); + result[i] = window_values[window_values.size() / 2]; // 中值 + } + } + } + + return result; +} + +// 辅助函数:计算信号的信噪比(SNR) +float SignalProcessor::calculate_snr(const std::vector& signal) { + if (signal.size() < 2) return 0.1f; // 至少给一个基础质量 + + // 计算信号的基本统计特征 + float min_val = signal[0], max_val = signal[0]; + float sum = 0.0f, sum_sq = 0.0f; + + for (float s : signal) { + if (s < min_val) min_val = s; + if (s > max_val) max_val = s; + sum += s; + sum_sq += s * s; + } + + float mean = sum / signal.size(); + float variance = (sum_sq / signal.size()) - (mean * mean); + float peak_to_peak = max_val - min_val; + + // 基于多个指标计算质量 + float quality = 0.0f; + + // 1. 峰峰值质量 (0-0.3分) + if (peak_to_peak > 0.01f) { + quality += 0.3f * clamp(peak_to_peak / 1.0f, 0.0f, 1.0f); + } + + // 2. 方差质量 (0-0.3分) + if (variance > 1e-6f) { + quality += 0.3f * clamp(variance / 0.1f, 0.0f, 1.0f); + } + + // 3. 信号变化质量 (0-0.4分) + float total_variation = 0.0f; + for (size_t i = 1; i < signal.size(); ++i) { + total_variation += std::abs(signal[i] - signal[i-1]); + } + total_variation /= (signal.size() - 1); + + if (total_variation > 1e-6f) { + quality += 0.4f * clamp(total_variation / 0.01f, 0.0f, 1.0f); + } + + // 确保质量在合理范围内 + return clamp(quality, 0.1f, 1.0f); +} +// 辅助函数:计算两个信号的相关系数 +float SignalProcessor::calculate_correlation(const std::vector& x, + const std::vector& y) { + if (x.empty() || y.empty() || x.size() != y.size()) return 0.0f; + + const size_t n = x.size(); + if (n < 2) return 0.0f; + + // 计算均值 + float x_mean = 0.0f, y_mean = 0.0f; + for (size_t i = 0; i < n; ++i) { + x_mean += x[i]; + y_mean += y[i]; + } + x_mean /= n; + y_mean /= n; + + // 计算协方差和方差 + float covariance = 0.0f; + float x_variance = 0.0f; + float y_variance = 0.0f; + + for (size_t i = 0; i < n; ++i) { + float x_diff = x[i] - x_mean; + float y_diff = y[i] - y_mean; + + covariance += x_diff * y_diff; + x_variance += x_diff * x_diff; + y_variance += y_diff * y_diff; + } + + // 计算相关系数 + if (x_variance < 1e-6f || y_variance < 1e-6f) return 0.0f; + + float correlation = covariance / std::sqrt(x_variance * y_variance); + + // 确保在[-1, 1]范围内 + return clamp(correlation, -1.0f, 1.0f); +} +//ecg sqi +float SignalProcessor::calculate_ecg_sqi(const std::vector& signal, double sample_rate) { + // 1. 检查输入有效性 + if (signal.empty()) return 0.0f; + if (sample_rate <= 0) return 0.0f; + const size_t min_samples = static_cast(0.5 * sample_rate); // 至少0.5秒数据 + if (signal.size() < min_samples) return 0.0f; + + // 3. 幅度检测(检测导联脱落或信号丢失) + float max_val = *std::max_element(signal.begin(), signal.end()); + float min_val = *std::min_element(signal.begin(), signal.end()); + float pp_amp = max_val - min_val; // 峰峰值幅度 + if (pp_amp < 0.1f) return 0.0f; // 幅度过低(假设单位是mV) + + // 4. 噪声水平评估 + float noise_level = 0.0f; + for (size_t i = 1; i < signal.size(); ++i) { + float diff = signal[i] - signal[i-1]; + noise_level += diff * diff; + } + noise_level = std::sqrt(noise_level / signal.size()); + + // 5. 功率谱分析(QRS频带能量比) + float total_power = 0.0f; + float qrs_power = 0.0f; + for (float s : signal) { + total_power += s * s; + } + + // 5-20Hz带通滤波(QRS主要能量带) + std::vector qrs_band = bandpass_filter(signal, sample_rate, 5.0, 20.0); + for (float s : qrs_band) { + qrs_power += s * s; + } + + float qrs_ratio = (total_power > 0) ? qrs_power / total_power : 0.0f; + + // 6. 基于特征的SQI计算 + float sqi = 0.0f; + + // 幅度因子(0.5-5mV为理想范围) + float amp_factor = clamp((pp_amp - 0.5f) / 4.5f, 0.0f, 1.0f); + + // 噪声因子(经验阈值) + float noise_factor = std::exp(-noise_level * 50.0f); + + // QRS能量因子 + float qrs_factor = clamp((qrs_ratio - 0.3f) * 2.5f, 0.0f, 1.0f); + + // 综合评分(加权平均) + sqi = 0.4f * amp_factor + 0.4f * qrs_factor + 0.2f * noise_factor; + + // 确保在[0,1]范围内 + return clamp(sqi, 0.0f, 1.0f); +} +void SignalProcessor::normalize_amplitude(std::vector& 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; + } + } +} + +// 实现缺失的extract_signal_features函数 +FeatureSet SignalProcessor::extract_signal_features(const SensorData& processed_data, + const std::vector& features) { + FeatureSet result; + + // 检查通道数据类型 + if (!std::holds_alternative>>(processed_data.channel_data)) { + return result; // 返回空的特征集 + } + + auto& channels = std::get>>(processed_data.channel_data); + + // 根据数据类型提取不同的特征 + switch (processed_data.data_type) { + case DataType::ECG_2LEAD: + case DataType::ECG_12LEAD: + result = extract_ecg_features(processed_data); + break; + case DataType::EEG: + result = extract_eeg_features(processed_data); + break; + case DataType::PPG: + result = extract_ppg_features(processed_data); + break; + case DataType::RESPIRATION: + result = extract_respiration_features(processed_data); + break; + case DataType::SNORE: + result = extract_snore_features(processed_data); + break; + default: + // 对于其他类型,提取基本特征 + break; + } + + return result; +} + +// 实现缺失的compute_physiological_indicators函数 +IndicatorSet SignalProcessor::compute_physiological_indicators(const FeatureSet& features, + const std::vector& indicators) { + IndicatorSet result; + + // 计算信号质量指数 + result.sqi = 0.8f; // 默认值,实际应该基于特征计算 + + // 根据特征计算生理指标 + if (!features.ecg_features.empty()) { + ECGIndicators ecg_ind; + const auto& ecg_feat = features.ecg_features[0]; + ecg_ind.sdnn = ecg_feat.sdnn; + ecg_ind.rmssd = ecg_feat.rmssd; + ecg_ind.heart_rate = 60.0f; // 默认心率,实际应该基于R波计算 + result.ecg_indicators.push_back(ecg_ind); + } + + return result; +} + +// 实现缺失的getProcessingPipeline函数 +std::string SignalProcessor::getProcessingPipeline(DataType device_type) { + switch (device_type) { + case DataType::EEG: + return "EEG: DC_removal -> Bandpass(1-40Hz) -> Normalize"; + case DataType::ECG_2LEAD: + case DataType::ECG_12LEAD: + return "ECG: Highpass(0.5Hz) -> Lowpass(40Hz) -> Notch(50Hz)"; + case DataType::PPG: + return "PPG: Lowpass(8Hz) -> Motion_artifact_removal"; + case DataType::RESPIRATION: + return "RESP: Lowpass(2Hz) -> DC_removal"; + case DataType::SNORE: + return "SNORE: Bandpass(50-2000Hz) -> Normalize"; + default: + return "Generic: DC_removal -> Normalize"; + } +} + +// 实现缺失的setFilterParams函数 +void SignalProcessor::setFilterParams(const std::string& filter_type, const FilterConfig& params) { + // 存储滤波器参数 + filter_configs_[filter_type] = params; +} + +// 实现缺失的extract_eeg_features函数 +FeatureSet SignalProcessor::extract_eeg_features(const SensorData& data) { + FeatureSet result; + + if (!std::holds_alternative>>(data.channel_data)) { + return result; + } + + auto& channels = std::get>>(data.channel_data); + + // 提取EEG特征(简化版本) + for (const auto& channel : channels) { + if (!channel.empty()) { + // 计算基本统计特征 + float mean = 0.0f, variance = 0.0f; + for (float sample : channel) { + mean += sample; + } + mean /= channel.size(); + + for (float sample : channel) { + float diff = sample - mean; + variance += diff * diff; + } + variance /= channel.size(); + + // 这里可以添加更多EEG特定特征 + } + } + + return result; +} + +// 实现缺失的extract_ecg_features函数 +FeatureSet SignalProcessor::extract_ecg_features(const SensorData& data) { + FeatureSet result; + + if (!std::holds_alternative>>(data.channel_data)) { + return result; + } + + auto& channels = std::get>>(data.channel_data); + + // 提取ECG特征(简化版本) + for (const auto& channel : channels) { + if (!channel.empty()) { + ECGChannelFeatures ecg_feat; + + // 简化的R波检测(这里只是示例) + std::vector r_peaks; + for (size_t i = 1; i < channel.size() - 1; ++i) { + if (channel[i] > channel[i-1] && channel[i] > channel[i+1] && + channel[i] > 0.5f) { // 简单的峰值检测 + r_peaks.push_back(i); + } + } + ecg_feat.r_peaks = r_peaks; + + // 计算RR间期统计 + if (r_peaks.size() > 1) { + std::vector rr_intervals; + for (size_t i = 1; i < r_peaks.size(); ++i) { + rr_intervals.push_back(static_cast(r_peaks[i] - r_peaks[i-1])); + } + + // 计算SDNN和RMSSD + float mean_rr = 0.0f; + for (float rr : rr_intervals) { + mean_rr += rr; + } + mean_rr /= rr_intervals.size(); + + float sdnn_sum = 0.0f, rmssd_sum = 0.0f; + for (size_t i = 0; i < rr_intervals.size(); ++i) { + float diff = rr_intervals[i] - mean_rr; + sdnn_sum += diff * diff; + + if (i > 0) { + float rr_diff = rr_intervals[i] - rr_intervals[i-1]; + rmssd_sum += rr_diff * rr_diff; + } + } + + ecg_feat.sdnn = std::sqrt(sdnn_sum / rr_intervals.size()); + ecg_feat.rmssd = std::sqrt(rmssd_sum / (rr_intervals.size() - 1)); + } + + result.ecg_features.push_back(ecg_feat); + } + } + + return result; +} + +// 实现缺失的extract_ppg_features函数 +FeatureSet SignalProcessor::extract_ppg_features(const SensorData& data) { + FeatureSet result; + + if (!std::holds_alternative>>(data.channel_data)) { + return result; + } + + auto& channels = std::get>>(data.channel_data); + + // 提取PPG特征(简化版本) + for (const auto& channel : channels) { + if (!channel.empty()) { + // 计算基本统计特征 + float mean = 0.0f, max_val = channel[0], min_val = channel[0]; + for (float sample : channel) { + mean += sample; + if (sample > max_val) max_val = sample; + if (sample < min_val) min_val = sample; + } + mean /= channel.size(); + + // 计算AC/DC比值(简化版本) + float ac_component = max_val - min_val; + float dc_component = mean; + float ac_dc_ratio = (dc_component > 0) ? ac_component / dc_component : 0.0f; + + // 这里可以添加更多PPG特定特征 + } + } + + return result; +} + +// 实现缺失的extract_respiration_features函数 +FeatureSet SignalProcessor::extract_respiration_features(const SensorData& data) { + FeatureSet result; + + if (!std::holds_alternative>>(data.channel_data)) { + return result; + } + + auto& channels = std::get>>(data.channel_data); + + // 提取呼吸特征(简化版本) + for (const auto& channel : channels) { + if (!channel.empty()) { + // 计算呼吸率(通过过零点计数) + int zero_crossings = 0; + for (size_t i = 1; i < channel.size(); ++i) { + if ((channel[i] >= 0 && channel[i-1] < 0) || + (channel[i] < 0 && channel[i-1] >= 0)) { + zero_crossings++; + } + } + + // 这里可以添加更多呼吸特定特征 + } + } + + return result; +} + +// 实现缺失的extract_snore_features函数 +FeatureSet SignalProcessor::extract_snore_features(const SensorData& data) { + FeatureSet result; + + if (!std::holds_alternative>>(data.channel_data)) { + return result; + } + + auto& channels = std::get>>(data.channel_data); + + // 提取鼾声特征(简化版本) + for (const auto& channel : channels) { + if (!channel.empty()) { + // 计算能量和频率特征 + float energy = 0.0f; + for (float sample : channel) { + energy += sample * sample; + } + energy /= channel.size(); + + // 这里可以添加更多鼾声特定特征 + } + } + + return result; +} + +// 实现缺失的extract_stethoscope_features函数 +FeatureSet SignalProcessor::extract_stethoscope_features(const SensorData& data) { + FeatureSet result; + + if (!std::holds_alternative>>(data.channel_data)) { + return result; + } + + auto& channels = std::get>>(data.channel_data); + + // 提取听诊器特征(简化版本) + for (const auto& channel : channels) { + if (!channel.empty()) { + // 计算音频特征 + float energy = 0.0f; + float max_val = channel[0], min_val = channel[0]; + + for (float sample : channel) { + energy += sample * sample; + if (sample > max_val) max_val = sample; + if (sample < min_val) min_val = sample; + } + energy /= channel.size(); + + // 计算动态范围 + float dynamic_range = max_val - min_val; + + // 这里可以添加更多听诊器特定特征,如心音分析等 + } + } + + return result; +} + diff --git a/app/src/main/java/com/example/cmake_project_test/CompletePipelineExample.kt b/app/src/main/java/com/example/cmake_project_test/CompletePipelineExample.kt new file mode 100644 index 0000000..992985c --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/CompletePipelineExample.kt @@ -0,0 +1,183 @@ +package com.example.cmake_project_test + +import android.util.Log +import type.SensorData + +/** + * 完整数据处理管道示例 + * 演示从原始数据到最终指标的完整流程 + */ +class CompletePipelineExample { + + private val dataMapper = DataMapper() + private val indicatorCalculator = IndicatorCalculator() + private val signalProcessor = SignalProcessorJNI() + + private var dataMapperInitialized = false + private var indicatorCalculatorInitialized = false + private var signalProcessorInitialized = false + + /** + * 初始化完整管道 + */ + fun initialize(): Boolean { + Log.d("CompletePipeline", "初始化完整数据处理管道...") + + // 初始化数据映射器 + dataMapperInitialized = dataMapper.initialize() + if (!dataMapperInitialized) { + Log.e("CompletePipeline", "数据映射器初始化失败") + return false + } + + // 初始化指标计算器 + indicatorCalculatorInitialized = indicatorCalculator.initialize() + if (!indicatorCalculatorInitialized) { + Log.e("CompletePipeline", "指标计算器初始化失败") + cleanup() + return false + } + + // 初始化信号处理器 + try { + signalProcessorInitialized = signalProcessor.createProcessor() + if (!signalProcessorInitialized) { + Log.e("CompletePipeline", "信号处理器初始化失败") + cleanup() + return false + } + } catch (e: Exception) { + Log.e("CompletePipeline", "信号处理器初始化异常: ${e.message}") + cleanup() + return false + } + + Log.d("CompletePipeline", "完整数据处理管道初始化成功") + return true + } + + /** + * 处理单个数据包的完整流程 + * 原始数据 → 解析数据 → 通道映射 → 信号处理 → 指标计算 + */ + fun processCompletePipeline(rawData: SensorData, sampleRate: Float = 250.0f): Map? { + if (!isInitialized()) { + Log.w("CompletePipeline", "管道未初始化") + return null + } + + Log.d("CompletePipeline", "开始处理完整数据管道...") + Log.d("CompletePipeline", "输入数据类型: ${rawData.dataType}, 采样率: $sampleRate Hz") + + try { + // 步骤1: 通道映射 + Log.d("CompletePipeline", "步骤1: 执行通道映射...") + val mappedData = dataMapper.mapSensorData(rawData) + if (mappedData == null) { + Log.e("CompletePipeline", "通道映射失败") + return null + } + Log.d("CompletePipeline", "通道映射完成") + + // 步骤2: 信号处理 (这里可以添加信号处理逻辑) + Log.d("CompletePipeline", "步骤2: 执行信号处理...") + // 暂时跳过信号处理,直接使用映射后的数据 + val processedData = mappedData + Log.d("CompletePipeline", "信号处理完成") + + // 步骤3: 指标计算 + Log.d("CompletePipeline", "步骤3: 执行指标计算...") + val metrics = indicatorCalculator.processCompletePipeline(processedData, sampleRate) + if (metrics == null) { + Log.e("CompletePipeline", "指标计算失败") + return null + } + + Log.d("CompletePipeline", "指标计算完成,计算了 ${metrics.size} 个指标") + Log.d("CompletePipeline", "完整数据处理管道执行成功") + + // 打印主要指标 + metrics["heart_rate"]?.let { + Log.d("CompletePipeline", "心率: ${String.format("%.1f", it)} bpm") + } + metrics["signal_quality"]?.let { + Log.d("CompletePipeline", "信号质量: ${String.format("%.2f", it)}") + } + metrics["spo2"]?.let { + Log.d("CompletePipeline", "血氧饱和度: ${String.format("%.1f", it)}%") + } + + return metrics + + } catch (e: Exception) { + Log.e("CompletePipeline", "完整数据处理管道异常: ${e.message}") + return null + } + } + + /** + * 批量处理数据包 + */ + fun processBatchPipeline(rawDataList: List, sampleRate: Float = 250.0f): List> { + if (!isInitialized()) { + Log.w("CompletePipeline", "管道未初始化") + return emptyList() + } + + Log.d("CompletePipeline", "开始批量处理 ${rawDataList.size} 个数据包...") + + val results = mutableListOf>() + var successCount = 0 + + for ((index, rawData) in rawDataList.withIndex()) { + val metrics = processCompletePipeline(rawData, sampleRate) + if (metrics != null) { + results.add(metrics) + successCount++ + } else { + results.add(emptyMap()) + } + + // 每处理100个数据包打印一次进度 + if ((index + 1) % 100 == 0) { + Log.d("CompletePipeline", "已处理 ${index + 1}/${rawDataList.size} 个数据包") + } + } + + Log.d("CompletePipeline", "批量处理完成: 成功 $successCount/${rawDataList.size} 个数据包") + return results + } + + /** + * 检查管道是否已初始化 + */ + fun isInitialized(): Boolean { + return dataMapperInitialized && indicatorCalculatorInitialized && signalProcessorInitialized + } + + /** + * 清理资源 + */ + fun cleanup() { + try { + if (dataMapperInitialized) { + // 使用公共方法或直接设置为false + dataMapperInitialized = false + } + + if (indicatorCalculatorInitialized) { + // 使用公共方法或直接设置为false + indicatorCalculatorInitialized = false + } + + if (signalProcessorInitialized) { + signalProcessor.destroyProcessor() + signalProcessorInitialized = false + } + + Log.d("CompletePipelineExample", "资源清理完成") + } catch (e: Exception) { + Log.e("CompletePipelineExample", "资源清理异常: ${e.message}") + } + } +} diff --git a/app/src/main/java/com/example/cmake_project_test/Constants.kt b/app/src/main/java/com/example/cmake_project_test/Constants.kt new file mode 100644 index 0000000..1805b5c --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/Constants.kt @@ -0,0 +1,26 @@ +package com.example.cmake_project_test + +object Constants { + // UI更新相关 + const val UPDATE_INTERVAL = 500L // 每500毫秒更新一次UI + + // 数据分块相关 + const val CHUNK_SIZE = 64 // 数据分块大小 + + // 缓冲区管理相关 + const val BUFFER_CLEANUP_THRESHOLD = 50 // 缓冲区清理阈值 + const val BUFFER_KEEP_COUNT = 30 // 缓冲区保留数量 + + // 显示限制相关 + const val MAX_DISPLAY_PACKETS = 5 // 最大显示数据包数量 + const val MAX_DETAIL_PACKETS = 3 // 最大详情数据包数量 + const val MAX_DISPLAY_CHANNELS = 4 // 最大显示通道数量 + const val MAX_12LEAD_CHANNELS = 6 // 12导联心电最大通道数量 + const val MAX_DISPLAY_SAMPLES = 10 // 最大显示采样点数量 + + // 文件相关 + const val DEFAULT_DATA_FILE = "ecg_data_raw.dat" // 默认数据文件名 + + // 延迟相关 + const val SIMULATION_DELAY = 1L // 模拟传输延迟(毫秒)- 减少延迟 +} diff --git a/app/src/main/java/com/example/cmake_project_test/DataManager.kt b/app/src/main/java/com/example/cmake_project_test/DataManager.kt new file mode 100644 index 0000000..3c9860f --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/DataManager.kt @@ -0,0 +1,687 @@ +package com.example.cmake_project_test + +import android.util.Log +import type.SensorData +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.nio.ByteBuffer +import java.nio.ByteOrder + +/** + * 数据管理类 + * 负责数据解析、缓冲管理和原生方法调用 + */ +class DataManager(private val nativeCallback: NativeMethodCallback) { + + private var parserHandle: Long = 0L + private val rawStream = ByteArrayOutputStream(4096) + private val packetBuffer = mutableListOf() + private var totalPacketsParsed = 0L + + // 信号处理相关 + private var streamingSignalProcessor: StreamingSignalProcessor? = null + private var streamingSignalProcessorInitialized = false + private val processedPackets = mutableListOf() + private val filterSettings = FilterSettings() + + // 通道映射相关 + private var dataMapper: DataMapper? = null + private var dataMapperInitialized = false + + // 指标计算相关 + private var indicatorCalculator: IndicatorCalculator? = null + private var indicatorCalculatorInitialized = false + private val calculatedMetrics = mutableListOf>() + private var latestMetrics: Map = emptyMap() + + // 流式数据处理相关 + private val channelBuffers = mutableMapOf>() // 通道号 -> 数据缓冲区 + private val processedChannelBuffers = mutableMapOf>() // 处理后的通道数据 + private val processingWindowSize = 2000 // 处理窗口大小(样本数) + private val minSamplesForMetrics = 1000 // 恢复最小样本数到合理值 + private var currentDataType: type.SensorData.DataType? = null // 当前数据类型 + private var lastProcessTime = 0L // 上次处理时间 + private val processingInterval = 2000L // 恢复处理间隔到合理值 + private var totalProcessedSamples = 0L // 总处理样本数 + + /** + * 确保解析器已创建 + */ + fun ensureParser() { + if (parserHandle == 0L) { + parserHandle = nativeCallback.createStreamParser() + } + } + + /** + * 处理蓝牙通知数据块 + */ + fun onBleNotify(chunk: ByteArray) { + if (chunk.isEmpty()) return + + ensureParser() + rawStream.write(chunk) + nativeCallback.streamParserAppend(parserHandle, chunk) + + // 拉取解析出的数据包 + val packets = nativeCallback.streamParserDrainPackets(parserHandle) + if (!packets.isNullOrEmpty()) { + totalPacketsParsed += packets.size + packetBuffer.addAll(packets) + + // 应用流式数据处理 + processStreamingData(packets) + } + } + + /** + * 确保数据映射器已初始化 + */ + fun ensureDataMapper() { + if (!dataMapperInitialized) { + try { + Log.d("DataManager", "开始创建数据映射器实例...") + dataMapper = DataMapper() + Log.d("DataManager", "数据映射器实例创建成功") + + Log.d("DataManager", "开始初始化数据映射器...") + dataMapperInitialized = dataMapper?.initialize() ?: false + if (dataMapperInitialized) { + Log.d("DataManager", "数据映射器初始化成功") + } else { + Log.e("DataManager", "数据映射器初始化失败") + } + } catch (e: Exception) { + Log.e("DataManager", "数据映射器初始化异常: ${e.message}", e) + dataMapperInitialized = false + } + } + } + + /** + * 确保指标计算器已初始化 + */ + fun ensureIndicatorCalculator() { + if (!indicatorCalculatorInitialized) { + try { + Log.d("DataManager", "开始创建指标计算器实例...") + indicatorCalculator = IndicatorCalculator() + Log.d("DataManager", "指标计算器实例创建成功") + + Log.d("DataManager", "开始初始化指标计算器...") + indicatorCalculatorInitialized = indicatorCalculator?.initialize() ?: false + if (indicatorCalculatorInitialized) { + Log.d("DataManager", "指标计算器初始化成功") + } else { + Log.e("DataManager", "指标计算器初始化失败") + } + } catch (e: Exception) { + Log.e("DataManager", "指标计算器初始化异常: ${e.message}", e) + indicatorCalculatorInitialized = false + } + } + } + + /** + * 确保流式信号处理器已初始化 + */ + fun ensureStreamingSignalProcessor() { + if (!streamingSignalProcessorInitialized) { + try { + Log.d("DataManager", "开始创建流式信号处理器实例...") + streamingSignalProcessor = StreamingSignalProcessor() + Log.d("DataManager", "流式信号处理器实例创建成功") + + Log.d("DataManager", "开始初始化流式信号处理器...") + streamingSignalProcessorInitialized = streamingSignalProcessor?.initialize() ?: false + if (streamingSignalProcessorInitialized) { + Log.d("DataManager", "流式信号处理器初始化成功") + } else { + Log.e("DataManager", "流式信号处理器初始化失败") + } + } catch (e: Exception) { + Log.e("DataManager", "流式信号处理器初始化异常: ${e.message}", e) + streamingSignalProcessorInitialized = false + } + } + } + + /** + * 流式数据处理 - 将数据包按通道合并并处理 + */ + private fun processStreamingData(packets: List) { + if (packets.isEmpty()) return + + Log.d("DataManager", "开始流式数据处理,处理 ${packets.size} 个数据包") + + // 1. 通道映射 + ensureDataMapper() + val mappedPackets = if (dataMapperInitialized && dataMapper != null) { + try { + val mapped = dataMapper!!.mapSensorDataList(packets) + Log.d("DataManager", "通道映射完成,映射了 ${mapped.size} 个数据包") + mapped + } catch (e: Exception) { + Log.e("DataManager", "通道映射失败: ${e.message}") + packets + } + } else { + Log.w("DataManager", "数据映射器未初始化,跳过通道映射") + packets + } + + // 2. 按通道合并数据 + for (packet in mappedPackets) { + val dataType = packet.getDataType() + if (currentDataType == null) { + currentDataType = dataType + Log.d("DataManager", "设置当前数据类型: $dataType") + } + + val channelData = packet.getChannelData() + if (channelData != null) { + for ((channelIndex, channel) in channelData.withIndex()) { + if (!channelBuffers.containsKey(channelIndex)) { + channelBuffers[channelIndex] = mutableListOf() + processedChannelBuffers[channelIndex] = mutableListOf() + } + channelBuffers[channelIndex]!!.addAll(channel) + } + } + } + + // 检查是否达到处理条件 + val totalSamples = channelBuffers.values.firstOrNull()?.size ?: 0 + if (totalSamples > 0) { + Log.d("DataManager", "当前总样本数: $totalSamples, 需要样本数: $minSamplesForMetrics") + } + + // 3. 检查处理条件:数据量足够且时间间隔合适 + val currentTime = System.currentTimeMillis() + val shouldProcess = totalSamples >= minSamplesForMetrics && + (currentTime - lastProcessTime) >= processingInterval + + if (shouldProcess) { + Log.d("DataManager", "满足处理条件,开始流式信号处理和指标计算") + processStreamingWindow() + lastProcessTime = currentTime + } else { + Log.d("DataManager", "不满足处理条件 - 数据量: ${totalSamples}/${minSamplesForMetrics}, 时间间隔: ${currentTime - lastProcessTime}ms/${processingInterval}ms") + } + + // 累积映射后的数据包列表(用于UI显示) + // 注意:这里累积所有批次的映射后数据包,用于统计和显示 + this.processedPackets.addAll(mappedPackets) + + // 添加调试信息(在后台线程中执行,避免阻塞UI) + Thread { + try { + Log.d("DataManager", "DEBUG: 当前批次映射结果 - 输入: ${packets.size}个数据包, 映射后: ${mappedPackets.size}个数据包") + Log.d("DataManager", "DEBUG: 累积映射后数据包总数: ${this.processedPackets.size}") + + // 统计当前批次的通道数量分布 + val ecg12LeadPackets = mappedPackets.filter { it.getDataType() == type.SensorData.DataType.ECG_12LEAD } + val packetsWith8Channels = ecg12LeadPackets.count { it.getChannelData()?.size == 8 } + val packetsWith12Channels = ecg12LeadPackets.count { it.getChannelData()?.size == 12 } + Log.d("DataManager", "DEBUG: 当前批次ECG_12LEAD统计 - 8通道: ${packetsWith8Channels}个, 12通道: ${packetsWith12Channels}个") + + // 统计累积的通道数量分布 + val totalEcg12LeadPackets = this.processedPackets.filter { it.getDataType() == type.SensorData.DataType.ECG_12LEAD } + val totalPacketsWith8Channels = totalEcg12LeadPackets.count { it.getChannelData()?.size == 8 } + val totalPacketsWith12Channels = totalEcg12LeadPackets.count { it.getChannelData()?.size == 12 } + Log.d("DataManager", "DEBUG: 累积ECG_12LEAD统计 - 8通道: ${totalPacketsWith8Channels}个, 12通道: ${totalPacketsWith12Channels}个") + } catch (e: Exception) { + Log.e("DataManager", "后台统计调试信息时发生错误: ${e.message}", e) + } + }.start() + } + + /** + * 处理流式数据窗口 + */ + private fun processStreamingWindow() { + ensureStreamingSignalProcessor() + if (!streamingSignalProcessorInitialized || streamingSignalProcessor == null) { + Log.w("DataManager", "流式处理器未初始化,跳过处理") + return + } + + try { + Log.d("DataManager", "开始处理流式数据窗口") + + // 获取采样率 + val sampleRate = when (currentDataType) { + type.SensorData.DataType.EEG -> 250.0 + type.SensorData.DataType.ECG_2LEAD, type.SensorData.DataType.ECG_12LEAD -> 250.0 + type.SensorData.DataType.PPG -> 50.0 + type.SensorData.DataType.STETHOSCOPE -> 8000.0 + type.SensorData.DataType.SNORE -> 8000.0 + type.SensorData.DataType.RESPIRATION -> 50.0 + else -> 250.0 + } + + Log.d("DataManager", "使用采样率: ${sampleRate}Hz,数据类型: $currentDataType") + + // 处理所有通道的数据 + val processedChannels = mutableListOf>() + var localProcessedSamples = 0L + + for ((channelIndex, channelData) in channelBuffers) { + if (channelData.size >= minSamplesForMetrics) { + Log.d("DataManager", "处理通道 $channelIndex,数据长度: ${channelData.size}") + + // 应用信号处理 + val processedData = streamingSignalProcessor!!.processStreamingDataWithParameters( + channelData, + sampleRate, + 40.0, // 低通滤波截止频率 + 50.0, // 陷波滤波频率 + 30.0 // 陷波滤波品质因数 + ) + + if (processedData.isNotEmpty()) { + processedChannels.add(processedData) + processedChannelBuffers[channelIndex]?.addAll(processedData) + localProcessedSamples += processedData.size + Log.d("DataManager", "通道 $channelIndex 处理完成,处理后数据长度: ${processedData.size}") + } else { + Log.w("DataManager", "通道 $channelIndex 处理失败,返回空数据") + processedChannels.add(channelData) // 使用原始数据 + } + } else { + Log.w("DataManager", "通道 $channelIndex 数据不足,跳过处理") + processedChannels.add(channelData) // 使用原始数据 + } + } + + Log.d("DataManager", "所有通道处理完成,总共处理了 $localProcessedSamples 个样本") + + // 计算指标(使用第一个处理后的通道) + if (processedChannels.isNotEmpty() && processedChannels[0].size >= minSamplesForMetrics) { + ensureIndicatorCalculator() + if (indicatorCalculatorInitialized && indicatorCalculator != null) { + try { + // 创建临时的SensorData用于指标计算 + val tempSensorData = SensorData() + tempSensorData.setDataType(currentDataType!!) + tempSensorData.setTimestamp(System.currentTimeMillis()) + tempSensorData.setPacketSn(0) + tempSensorData.setChannelData(processedChannels) + + val metrics = indicatorCalculator!!.processCompletePipeline(tempSensorData, sampleRate.toFloat()) + if (metrics != null && metrics.isNotEmpty()) { + Log.d("DataManager", "指标计算成功,获得 ${metrics.size} 个指标") + latestMetrics = metrics + calculatedMetrics.clear() + calculatedMetrics.add(metrics) + + // 记录处理统计 + this.totalProcessedSamples += localProcessedSamples + } else { + Log.w("DataManager", "指标计算返回空结果") + } + } catch (e: Exception) { + Log.e("DataManager", "指标计算失败: ${e.message}") + } + } + } + + // 清理缓冲区(保留最后一部分数据用于连续性) + val keepSamples = minSamplesForMetrics / 2 + channelBuffers.forEach { (channel, buffer) -> + if (buffer.size > keepSamples) { + val toRemove = buffer.size - keepSamples + repeat(toRemove) { + if (buffer.isNotEmpty()) { + buffer.removeAt(0) + } + } + } + } + + // 清理处理后的缓冲区 + processedChannelBuffers.forEach { (channel, buffer) -> + if (buffer.size > keepSamples) { + val toRemove = buffer.size - keepSamples + repeat(toRemove) { + if (buffer.isNotEmpty()) { + buffer.removeAt(0) + } + } + } + } + + Log.d("DataManager", "流式数据处理完成,保留 ${keepSamples} 个样本用于连续性") + + } catch (e: Exception) { + Log.e("DataManager", "流式数据处理异常: ${e.message}", e) + } + } + + /** + * 处理文件数据 + */ + fun processFileData(fileData: ByteArray, progressCallback: ((Int) -> Unit)? = null) { + Log.d("DataManager", "开始处理文件数据,数据大小: ${fileData.size} 字节") + + try { + val chunkSize = 1024 + var processedBytes = 0 + + for (i in fileData.indices step chunkSize) { + val endIndex = minOf(i + chunkSize, fileData.size) + val chunk = fileData.copyOfRange(i, endIndex) + + onBleNotify(chunk) + processedBytes += chunk.size + + // 更新进度 + val progress = (processedBytes * 100 / fileData.size).coerceAtMost(100) + progressCallback?.invoke(progress) + } + + Log.d("DataManager", "文件数据处理完成,总共处理了 $processedBytes 字节") + + } catch (e: Exception) { + Log.e("DataManager", "处理文件数据时发生错误: ${e.message}", e) + } + } + + // Getter方法 + fun getPacketBufferSize(): Int = packetBuffer.size + fun getProcessedPacketsSize(): Int = processedPackets.size + fun getCalculatedMetricsSize(): Int = calculatedMetrics.size + fun getRawStreamSize(): Int = rawStream.size() + fun getTotalPacketsParsed(): Long = totalPacketsParsed + fun getPacketBuffer(): List = packetBuffer + fun getProcessedPackets(): List = processedPackets + fun getCalculatedMetrics(): List> = calculatedMetrics + fun getLatestMetrics(): Map = latestMetrics + fun getChannelBuffersStatus(): Map = channelBuffers.mapValues { it.value.size } + + // 流式处理相关getter方法 + fun getProcessedChannelBuffersStatus(): Map = processedChannelBuffers.mapValues { it.value.size } + fun getTotalProcessedSamples(): Long = totalProcessedSamples + fun getCurrentDataType(): type.SensorData.DataType? = currentDataType + fun getProcessingStatus(): Map { + return mapOf( + "totalSamples" to (channelBuffers.values.firstOrNull()?.size ?: 0), + "minSamplesRequired" to minSamplesForMetrics, + "timeSinceLastProcess" to (System.currentTimeMillis() - lastProcessTime), + "processingInterval" to processingInterval, + "currentDataType" to (currentDataType?.name ?: "Unknown"), + "totalProcessedSamples" to totalProcessedSamples + ) + } + + /** + * 获取流式信号处理器状态 + */ + fun getStreamingSignalProcessorStatus(): Map { + return if (streamingSignalProcessorInitialized && streamingSignalProcessor != null) { + streamingSignalProcessor!!.getBufferStatus() + } else { + emptyMap() + } + } + + /** + * 清理缓冲区 + */ + fun cleanupBuffer() { + try { + // 清理原始数据包缓冲区,保留最新的100个数据包 + val maxKeepPackets = 100 + if (packetBuffer.size > maxKeepPackets) { + val toRemove = packetBuffer.size - maxKeepPackets + repeat(toRemove) { + if (packetBuffer.isNotEmpty()) { + packetBuffer.removeAt(0) + } + } + Log.d("DataManager", "清理了 $toRemove 个旧数据包,保留 $maxKeepPackets 个最新数据包") + } + + // 清理处理后数据包缓冲区,保留最新的50个数据包 + val maxKeepProcessedPackets = 50 + if (processedPackets.size > maxKeepProcessedPackets) { + val toRemove = processedPackets.size - maxKeepProcessedPackets + repeat(toRemove) { + if (processedPackets.isNotEmpty()) { + processedPackets.removeAt(0) + } + } + Log.d("DataManager", "清理了 $toRemove 个旧处理后数据包,保留 $maxKeepProcessedPackets 个最新数据包") + } + + } catch (e: Exception) { + Log.e("DataManager", "清理缓冲区时发生错误: ${e.message}") + } + } + + /** + * 计算信号质量 + */ + fun calculateSignalQuality(packet: SensorData): Float { + if (!streamingSignalProcessorInitialized || streamingSignalProcessor == null) return 0.0f + + try { + // 获取第一个通道的数据进行质量评估 + val channelData = packet.getChannelData() + if (channelData != null && channelData.isNotEmpty()) { + val firstChannel = channelData[0] + if (firstChannel != null) { + val signal = firstChannel.toFloatArray() + val quality = streamingSignalProcessor!!.calculateSignalQuality(signal) + + // 添加调试信息 + Log.d("DataManager", "信号质量计算: 通道0, 数据长度=${signal.size}, 质量=$quality") + return quality + } + } + } catch (e: Exception) { + Log.e("DataManager", "计算信号质量时发生错误: ${e.message}") + } + + return 0.0f + } + + /** + * 应用信号处理 + */ + fun applySignalProcessing(packets: List): List { + Log.d("DataManager", "开始应用信号处理,处理 ${packets.size} 个数据包") + + ensureStreamingSignalProcessor() + if (!streamingSignalProcessorInitialized || streamingSignalProcessor == null) { + Log.w("DataManager", "流式信号处理器未初始化,跳过信号处理") + return packets + } + + val processedPackets = mutableListOf() + + for (packet in packets) { + try { + val processedPacket = processSinglePacket(packet) + processedPackets.add(processedPacket) + } catch (e: Exception) { + Log.e("DataManager", "处理数据包时发生错误: ${e.message}") + processedPackets.add(packet) // 处理失败时返回原始数据包 + } + } + + Log.d("DataManager", "信号处理完成,处理了 ${processedPackets.size} 个数据包") + return processedPackets + } + + /** + * 处理单个数据包 + */ + private fun processSinglePacket(packet: SensorData): SensorData { + val channelData = packet.getChannelData() + if (channelData == null || channelData.isEmpty()) { + return packet + } + + val processedChannels = mutableListOf>() + + for (channel in channelData) { + val processedChannel = when (packet.getDataType()) { + type.SensorData.DataType.EEG -> applyEEGFilters(channel) + type.SensorData.DataType.ECG_2LEAD, type.SensorData.DataType.ECG_12LEAD -> applyECGFilters(channel) + type.SensorData.DataType.PPG -> applyPPGFilters(channel) + else -> channel // 其他类型暂时不处理 + } + processedChannels.add(processedChannel) + } + + // 创建处理后的数据包 + val processedPacket = SensorData() + processedPacket.setDataType(packet.getDataType()) + processedPacket.setTimestamp(packet.getTimestamp()) + processedPacket.setPacketSn(packet.getPacketSn()) + processedPacket.setChannelData(processedChannels) + + return processedPacket + } + + /** + * 应用EEG滤波器 + */ + private fun applyEEGFilters(channel: List): List { + val signal = channel.toFloatArray() + + try { + if (streamingSignalProcessor != null) { + // 1. 带通滤波 (1-40Hz,EEG主要频率范围) + val bandpassFiltered = streamingSignalProcessor!!.bandpassFilter( + signal, + filterSettings.sampleRate.toFloat(), + 1.0f, + 40.0f + ) + + if (bandpassFiltered != null) { + // 2. 幅度归一化 + val normalized = streamingSignalProcessor!!.normalizeAmplitude(bandpassFiltered) + + if (normalized != null) { + return normalized.toList() + } + } + } + } catch (e: Exception) { + Log.e("DataManager", "EEG滤波处理失败: ${e.message}") + } + + return channel // 处理失败时返回原始数据 + } + + /** + * 应用ECG滤波器 + */ + private fun applyECGFilters(channel: List): List { + val signal = channel.toFloatArray() + + try { + if (streamingSignalProcessor != null) { + // 1. 高通滤波 (0.5Hz,去除基线漂移) + val highpassFiltered = streamingSignalProcessor!!.highpassFilter( + signal, + filterSettings.sampleRate.toFloat(), + 0.5f + ) + + if (highpassFiltered != null) { + // 2. 低通滤波 (40Hz,去除高频噪声) + val lowpassFiltered = streamingSignalProcessor!!.lowpassFilter( + highpassFiltered, + filterSettings.sampleRate.toFloat(), + 40.0f + ) + + if (lowpassFiltered != null) { + // 3. 陷波滤波 (50Hz,去除工频干扰) + val notchFiltered = streamingSignalProcessor!!.notchFilter( + lowpassFiltered, + filterSettings.sampleRate.toFloat(), + 50.0f + ) + + if (notchFiltered != null) { + return notchFiltered.toList() + } + } + } + } + } catch (e: Exception) { + Log.e("DataManager", "ECG滤波处理失败: ${e.message}") + } + + return channel // 处理失败时返回原始数据 + } + + /** + * 应用PPG滤波器 + */ + private fun applyPPGFilters(channel: List): List { + val signal = channel.toFloatArray() + + try { + if (streamingSignalProcessor != null) { + // 1. 低通滤波 (8Hz,PPG主要频率范围) + val filtered = streamingSignalProcessor!!.lowpassFilter( + signal, + filterSettings.sampleRate.toFloat(), + 8.0f + ) + + if (filtered != null) { + // 2. 去运动伪影 + val motionArtifactRemoved = streamingSignalProcessor!!.processRealtimeChunk( + filtered, + filterSettings.sampleRate.toFloat() + ) + + if (motionArtifactRemoved != null) { + return motionArtifactRemoved.toList() + } + } + } + } catch (e: Exception) { + Log.e("DataManager", "PPG滤波处理失败: ${e.message}") + } + + return channel // 处理失败时返回原始数据 + } + + /** + * 清理资源 + */ + fun cleanup() { + try { + if (parserHandle != 0L) { + nativeCallback.destroyStreamParser(parserHandle) + parserHandle = 0L + } + + // 清理缓冲区 + packetBuffer.clear() + processedPackets.clear() + calculatedMetrics.clear() + channelBuffers.clear() + processedChannelBuffers.clear() + rawStream.reset() + + // 重置状态 + totalPacketsParsed = 0L + currentDataType = null + lastProcessTime = 0L + totalProcessedSamples = 0L + + Log.d("DataManager", "资源清理完成") + } catch (e: Exception) { + Log.e("DataManager", "清理资源时发生错误: ${e.message}") + } + } +} diff --git a/app/src/main/java/com/example/cmake_project_test/DataMapper.kt b/app/src/main/java/com/example/cmake_project_test/DataMapper.kt new file mode 100644 index 0000000..d29afc5 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/DataMapper.kt @@ -0,0 +1,159 @@ +package com.example.cmake_project_test + +import android.util.Log +import type.SensorData + +/** + * 数据映射器 - 负责通道映射和数据处理 + * 处理流程:原始数据 → 解析数据 → 通道映射 → 信号处理 → 指标计算 + */ +class DataMapper { + + companion object { + // 加载本地库 + init { + System.loadLibrary("cmake_project_test") + } + } + + // 本地映射器句柄 + private var mapperHandle: Long = 0 + + /** + * 初始化数据映射器 + */ + fun initialize(): Boolean { + try { + mapperHandle = createDataMapper() + if (mapperHandle != 0L) { + Log.d("DataMapper", "数据映射器初始化成功,句柄: $mapperHandle") + return true + } else { + Log.e("DataMapper", "数据映射器初始化失败") + return false + } + } catch (e: Exception) { + Log.e("DataMapper", "初始化数据映射器时发生错误: ${e.message}") + return false + } + } + + /** + * 映射单个传感器数据 + */ + fun mapSensorData(sensorData: SensorData): SensorData? { + Log.d("DataMapper", "DEBUG: DataMapper.mapSensorData被调用") + Log.d("DataMapper", "DEBUG: mapperHandle = $mapperHandle") + Log.d("DataMapper", "DEBUG: 输入数据类型 = ${sensorData.getDataType()}") + + if (mapperHandle == 0L) { + Log.w("DataMapper", "数据映射器未初始化") + return null + } + + return try { + Log.d("DataMapper", "DEBUG: 准备调用JNI mapSensorData") + val mappedData = mapSensorData(mapperHandle, sensorData) + Log.d("DataMapper", "DEBUG: JNI mapSensorData调用完成") + + if (mappedData != null) { + Log.d("DataMapper", "成功映射传感器数据: ${sensorData.getDataType()}") + Log.d("DataMapper", "DEBUG: 映射后数据类型 = ${mappedData.getDataType()}") + Log.d("DataMapper", "DEBUG: 映射后通道数量 = ${mappedData.getChannelData()?.size ?: 0}") + mappedData + } else { + Log.w("DataMapper", "映射传感器数据失败,返回原始数据") + sensorData + } + } catch (e: Exception) { + Log.e("DataMapper", "映射传感器数据时发生错误: ${e.message}") + Log.e("DataMapper", "DEBUG: 映射异常堆栈", e) + sensorData // 出错时返回原始数据 + } + } + + /** + * 批量映射传感器数据列表 + */ + fun mapSensorDataList(sensorDataList: List): List { + Log.d("DataMapper", "DEBUG: DataMapper.mapSensorDataList被调用") + Log.d("DataMapper", "DEBUG: mapperHandle = $mapperHandle") + Log.d("DataMapper", "DEBUG: 输入数据包数量 = ${sensorDataList.size}") + + if (mapperHandle == 0L) { + Log.w("DataMapper", "数据映射器未初始化") + return sensorDataList + } + + return try { + Log.d("DataMapper", "DEBUG: 准备调用JNI mapSensorDataList") + val mappedList = mapSensorDataList(mapperHandle, sensorDataList) + Log.d("DataMapper", "DEBUG: JNI mapSensorDataList调用完成") + + if (mappedList != null) { + Log.d("DataMapper", "成功批量映射 ${sensorDataList.size} 个传感器数据") + Log.d("DataMapper", "DEBUG: 映射后数据包数量 = ${mappedList.size}") + mappedList + } else { + Log.w("DataMapper", "批量映射失败,返回原始数据") + sensorDataList + } + } catch (e: Exception) { + Log.e("DataMapper", "批量映射传感器数据时发生错误: ${e.message}") + Log.e("DataMapper", "DEBUG: 批量映射异常堆栈", e) + sensorDataList // 出错时返回原始数据 + } + } + + /** + * 清理资源 + */ + fun cleanup() { + if (mapperHandle != 0L) { + try { + destroyDataMapper(mapperHandle) + mapperHandle = 0L + Log.d("DataMapper", "数据映射器资源已清理") + } catch (e: Exception) { + Log.e("DataMapper", "清理数据映射器资源时发生错误: ${e.message}") + } + } + } + + /** + * 检查映射器是否已初始化 + */ + fun isInitialized(): Boolean = mapperHandle != 0L + + // ============================================================================ + // JNI函数声明 + // ============================================================================ + + /** + * 创建数据映射器实例 + * @return 映射器句柄,0表示失败 + */ + private external fun createDataMapper(): Long + + /** + * 销毁数据映射器实例 + * @param mapperHandle 映射器句柄 + */ + private external fun destroyDataMapper(mapperHandle: Long) + + /** + * 映射单个传感器数据 + * @param mapperHandle 映射器句柄 + * @param sensorData 传感器数据 + * @return 映射后的传感器数据,null表示失败 + */ + private external fun mapSensorData(mapperHandle: Long, sensorData: SensorData): SensorData? + + /** + * 批量映射传感器数据列表 + * @param mapperHandle 映射器句柄 + * @param sensorDataList 传感器数据列表 + * @return 映射后的传感器数据列表,null表示失败 + */ + private external fun mapSensorDataList(mapperHandle: Long, sensorDataList: List): List? +} diff --git a/app/src/main/java/com/example/cmake_project_test/DeviceTypeHelper.kt b/app/src/main/java/com/example/cmake_project_test/DeviceTypeHelper.kt new file mode 100644 index 0000000..a210b68 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/DeviceTypeHelper.kt @@ -0,0 +1,79 @@ +package com.example.cmake_project_test + +import type.SensorData + +/** + * 设备类型帮助类 + * 负责设备类型名称映射和通道数据详情构建 + */ +object DeviceTypeHelper { + + /** + * 根据数据类型获取设备名称 + */ + fun getDeviceName(dataType: SensorData.DataType): String { + return when (dataType) { + SensorData.DataType.EEG -> "脑电设备" + SensorData.DataType.ECG_2LEAD -> "胸腹设备" + SensorData.DataType.PPG -> "血氧设备" + SensorData.DataType.ECG_12LEAD -> "12导联心电" + SensorData.DataType.STETHOSCOPE -> "数字听诊" + SensorData.DataType.SNORE -> "鼾声设备" + SensorData.DataType.RESPIRATION -> "呼吸/姿态" + SensorData.DataType.MIT_BIH -> "MIT-BIH" + else -> "未知设备" + } + } + + /** + * 构建通道数据详情字符串 + */ + fun buildChannelDetails( + data: List, + maxPackets: Int = Constants.MAX_DETAIL_PACKETS, + maxChannels: Int = Constants.MAX_DISPLAY_CHANNELS, + maxSamples: Int = Constants.MAX_DISPLAY_SAMPLES + ): String { + if (data.isEmpty()) { + return "无通道数据" + } + + val details = mutableListOf() + + data.take(maxPackets).forEachIndexed { packetIndex, sensorData -> + if (sensorData.channelData.isNullOrEmpty()) { + details.add("数据包 ${packetIndex + 1}: 无通道数据") + return@forEachIndexed + } + + details.add("数据包 ${packetIndex + 1} (${getDeviceName(sensorData.dataType ?: SensorData.DataType.EEG)}):") + + sensorData.channelData.take(maxChannels).forEachIndexed { channelIndex, channel -> + if (channel.isNullOrEmpty()) { + details.add(" 通道 ${channelIndex + 1}: 空数据") + return@forEachIndexed + } + + // 只显示前几个采样点 + val sampleCount = minOf(maxSamples, channel.size) + val channelDataStr = channel.take(sampleCount).joinToString(", ") { "%.1f".format(it) } + + details.add(" 通道 ${channelIndex + 1}: ${sampleCount}/${channel.size} 采样点") + details.add(" 示例: $channelDataStr${if (channel.size > sampleCount) "..." else ""}") + } + + if (sensorData.channelData.size > maxChannels) { + details.add(" ... 还有 ${sensorData.channelData.size - maxChannels} 个通道") + } + + // 添加分隔线 + details.add("") + } + + if (data.size > maxPackets) { + details.add("... 还有 ${data.size - maxPackets} 个数据包") + } + + return details.joinToString("\n") + } +} diff --git a/app/src/main/java/com/example/cmake_project_test/FileHelper.kt b/app/src/main/java/com/example/cmake_project_test/FileHelper.kt new file mode 100644 index 0000000..7978707 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/FileHelper.kt @@ -0,0 +1,40 @@ +package com.example.cmake_project_test + +import android.content.Context +import android.util.Log +import java.io.IOException + +/** + * 文件帮助类 + * 负责文件读取操作 + */ +object FileHelper { + + /** + * 从 assets 文件夹读取文件到字节数组 + */ + fun readAssetFile(context: Context, fileName: String): ByteArray? { + return try { + context.assets.open(fileName).use { inputStream -> + // 使用更可靠的文件读取方式 + val bytes = mutableListOf() + val buffer = ByteArray(1024) + var bytesRead: Int + + while (inputStream.read(buffer).also { bytesRead = it } != -1) { + for (i in 0 until bytesRead) { + bytes.add(buffer[i]) + } + } + + bytes.toByteArray() + } + } catch (e: IOException) { + Log.e("FileHelper", "Error reading asset file: $fileName", e) + null + } catch (e: Exception) { + Log.e("FileHelper", "Unexpected error reading asset file: $fileName", e) + null + } + } +} diff --git a/app/src/main/java/com/example/cmake_project_test/FilterSettings.kt b/app/src/main/java/com/example/cmake_project_test/FilterSettings.kt new file mode 100644 index 0000000..3b46e05 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/FilterSettings.kt @@ -0,0 +1,129 @@ +package com.example.cmake_project_test + +/** + * 滤波器设置类 + * 管理各种信号处理的参数配置 + */ +data class FilterSettings( + // 采样率设置 + val sampleRate: Double = 1000.0, + + // EEG滤波器参数 + val eegLowPass: Double = 40.0, + val eegHighPass: Double = 1.0, + + // ECG滤波器参数 + val ecgHighPass: Double = 0.5, // 高通滤波,去除基线漂移 + val ecgLowPass: Double = 40.0, // 低通滤波,去除高频噪声 + val ecgNotchFreq: Double = 50.0, // 陷波频率,去除工频干扰 + val ecgNotchQ: Double = 30.0, // 陷波品质因数 + + // PPG滤波器参数 + val ppgLowPass: Double = 8.0, // 低通滤波,PPG主要频率范围 + + // 通用滤波器参数 + val generalLowPass: Double = 100.0, + val generalHighPass: Double = 0.1, + val generalNotchFreq: Double = 50.0, + val generalNotchQ: Double = 30.0 +) { + companion object { + // 预设配置 + val EEG_DEFAULT = FilterSettings( + sampleRate = 1000.0, + eegLowPass = 40.0, + eegHighPass = 1.0 + ) + + val ECG_DEFAULT = FilterSettings( + sampleRate = 1000.0, + ecgHighPass = 0.5, + ecgLowPass = 40.0, + ecgNotchFreq = 50.0, + ecgNotchQ = 30.0 + ) + + val PPG_DEFAULT = FilterSettings( + sampleRate = 1000.0, + ppgLowPass = 8.0 + ) + + val HIGH_QUALITY = FilterSettings( + sampleRate = 1000.0, + eegLowPass = 50.0, + eegHighPass = 0.5, + ecgHighPass = 0.3, + ecgLowPass = 50.0, + ecgNotchFreq = 50.0, + ecgNotchQ = 50.0, + ppgLowPass = 10.0 + ) + + val REAL_TIME = FilterSettings( + sampleRate = 1000.0, + eegLowPass = 30.0, + eegHighPass = 2.0, + ecgHighPass = 1.0, + ecgLowPass = 30.0, + ecgNotchFreq = 50.0, + ecgNotchQ = 20.0, + ppgLowPass = 6.0 + ) + } + + /** + * 根据数据类型获取相应的滤波器设置 + */ + fun getSettingsForDataType(dataType: type.SensorData.DataType): FilterSettings { + return when (dataType) { + type.SensorData.DataType.EEG -> EEG_DEFAULT + type.SensorData.DataType.ECG_2LEAD, type.SensorData.DataType.ECG_12LEAD -> ECG_DEFAULT + type.SensorData.DataType.PPG -> PPG_DEFAULT + else -> this + } + } + + /** + * 更新采样率 + */ + fun updateSampleRate(newSampleRate: Double): FilterSettings { + return copy(sampleRate = newSampleRate) + } + + /** + * 更新EEG滤波器参数 + */ + fun updateEEGSettings( + lowPass: Double? = null, + highPass: Double? = null + ): FilterSettings { + return copy( + eegLowPass = lowPass ?: eegLowPass, + eegHighPass = highPass ?: eegHighPass + ) + } + + /** + * 更新ECG滤波器参数 + */ + fun updateECGSettings( + highPass: Double? = null, + lowPass: Double? = null, + notchFreq: Double? = null, + notchQ: Double? = null + ): FilterSettings { + return copy( + ecgHighPass = highPass ?: ecgHighPass, + ecgLowPass = lowPass ?: ecgLowPass, + ecgNotchFreq = notchFreq ?: ecgNotchFreq, + ecgNotchQ = notchQ ?: ecgNotchQ + ) + } + + /** + * 更新PPG滤波器参数 + */ + fun updatePPGSettings(lowPass: Double? = null): FilterSettings { + return copy(ppgLowPass = lowPass ?: ppgLowPass) + } +} diff --git a/app/src/main/java/com/example/cmake_project_test/IndicatorCalculator.kt b/app/src/main/java/com/example/cmake_project_test/IndicatorCalculator.kt new file mode 100644 index 0000000..4f6df69 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/IndicatorCalculator.kt @@ -0,0 +1,209 @@ +package com.example.cmake_project_test + +import android.util.Log +import type.SensorData +import java.util.* + +/** + * 指标计算器 - 负责计算各种生理指标 + * 处理流程:原始数据 → 解析数据 → 通道映射 → 信号处理 → 指标计算 + */ +class IndicatorCalculator { + + companion object { + // 加载本地库 + init { + System.loadLibrary("cmake_project_test") + } + } + + // 本地计算器句柄 + private var calculatorHandle: Long = 0 + + /** + * 初始化指标计算器 + */ + fun initialize(): Boolean { + try { + calculatorHandle = createIndicatorCalculator() + if (calculatorHandle != 0L) { + Log.d("IndicatorCalculator", "指标计算器初始化成功,句柄: $calculatorHandle") + return true + } else { + Log.e("IndicatorCalculator", "指标计算器初始化失败") + return false + } + } catch (e: Exception) { + Log.e("IndicatorCalculator", "初始化指标计算器时发生错误: ${e.message}") + return false + } + } + + /** + * 完整数据处理流程 + * 输入原始数据,输出所有计算的指标 + */ + fun processCompletePipeline(sensorData: SensorData, sampleRate: Float): Map? { + if (calculatorHandle == 0L) { + Log.w("IndicatorCalculator", "指标计算器未初始化") + return null + } + + return try { + val metrics = processCompletePipeline(calculatorHandle, sensorData, sampleRate) + if (metrics != null) { + Log.d("IndicatorCalculator", "完整数据处理流程完成,计算了 ${metrics.size} 个指标") + metrics + } else { + Log.w("IndicatorCalculator", "完整数据处理流程失败") + null + } + } catch (e: Exception) { + Log.e("IndicatorCalculator", "完整数据处理流程时发生错误: ${e.message}") + null + } + } + + /** + * 计算ECG指标 + */ + fun calculateECGMetrics(sensorData: SensorData, sampleRate: Float): Map? { + if (calculatorHandle == 0L) { + Log.w("IndicatorCalculator", "指标计算器未初始化") + return null + } + + return try { + val metrics = calculateECGMetrics(calculatorHandle, sensorData, sampleRate) + if (metrics != null) { + Log.d("IndicatorCalculator", "ECG指标计算完成,计算了 ${metrics.size} 个指标") + metrics + } else { + Log.w("IndicatorCalculator", "ECG指标计算失败") + null + } + } catch (e: Exception) { + Log.e("IndicatorCalculator", "ECG指标计算时发生错误: ${e.message}") + null + } + } + + /** + * 计算PPG指标 + */ + fun calculatePPGMetrics(sensorData: SensorData, sampleRate: Float): Map? { + if (calculatorHandle == 0L) { + Log.w("IndicatorCalculator", "指标计算器未初始化") + return null + } + + return try { + val metrics = calculatePPGMetrics(calculatorHandle, sensorData, sampleRate) + if (metrics != null) { + Log.d("IndicatorCalculator", "PPG指标计算完成,计算了 ${metrics.size} 个指标") + metrics + } else { + Log.w("IndicatorCalculator", "PPG指标计算失败") + null + } + } catch (e: Exception) { + Log.e("IndicatorCalculator", "PPG指标计算时发生错误: ${e.message}") + null + } + } + + /** + * 计算HRV指标 + */ + fun calculateHRVMetrics(rrIntervals: List): Map? { + if (calculatorHandle == 0L) { + Log.w("IndicatorCalculator", "指标计算器未初始化") + return null + } + + return try { + val metrics = calculateHRVMetrics(calculatorHandle, rrIntervals) + if (metrics != null) { + Log.d("IndicatorCalculator", "HRV指标计算完成,计算了 ${metrics.size} 个指标") + metrics + } else { + Log.w("IndicatorCalculator", "HRV指标计算失败") + null + } + } catch (e: Exception) { + Log.e("IndicatorCalculator", "HRV指标计算时发生错误: ${e.message}") + null + } + } + + /** + * 清理资源 + */ + fun cleanup() { + if (calculatorHandle != 0L) { + try { + destroyIndicatorCalculator(calculatorHandle) + calculatorHandle = 0L + Log.d("IndicatorCalculator", "指标计算器资源已清理") + } catch (e: Exception) { + Log.e("IndicatorCalculator", "清理指标计算器资源时发生错误: ${e.message}") + } + } + } + + /** + * 检查计算器是否已初始化 + */ + fun isInitialized(): Boolean = calculatorHandle != 0L + + // ============================================================================ + // JNI函数声明 + // ============================================================================ + + /** + * 创建指标计算器实例 + * @return 计算器句柄,0表示失败 + */ + private external fun createIndicatorCalculator(): Long + + /** + * 销毁指标计算器实例 + * @param calculatorHandle 计算器句柄 + */ + private external fun destroyIndicatorCalculator(calculatorHandle: Long) + + /** + * 完整数据处理流程 + * @param calculatorHandle 计算器句柄 + * @param sensorData 传感器数据 + * @param sampleRate 采样率 + * @return 计算的指标映射,null表示失败 + */ + private external fun processCompletePipeline(calculatorHandle: Long, sensorData: SensorData, sampleRate: Float): Map? + + /** + * 计算ECG指标 + * @param calculatorHandle 计算器句柄 + * @param sensorData 传感器数据 + * @param sampleRate 采样率 + * @return 计算的ECG指标映射,null表示失败 + */ + private external fun calculateECGMetrics(calculatorHandle: Long, sensorData: SensorData, sampleRate: Float): Map? + + /** + * 计算PPG指标 + * @param calculatorHandle 计算器句柄 + * @param sensorData 传感器数据 + * @param sampleRate 采样率 + * @return 计算的PPG指标映射,null表示失败 + */ + private external fun calculatePPGMetrics(calculatorHandle: Long, sensorData: SensorData, sampleRate: Float): Map? + + /** + * 计算HRV指标 + * @param calculatorHandle 计算器句柄 + * @param rrIntervals RR间期列表 + * @return 计算的HRV指标映射,null表示失败 + */ + private external fun calculateHRVMetrics(calculatorHandle: Long, rrIntervals: List): Map? +} diff --git a/app/src/main/java/com/example/cmake_project_test/MainActivity.kt b/app/src/main/java/com/example/cmake_project_test/MainActivity.kt index 251df89..fb4af4a 100644 --- a/app/src/main/java/com/example/cmake_project_test/MainActivity.kt +++ b/app/src/main/java/com/example/cmake_project_test/MainActivity.kt @@ -1,25 +1,16 @@ package com.example.cmake_project_test -import type.SensorData import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import com.example.cmake_project_test.databinding.ActivityMainBinding -import java.io.InputStream -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.util.concurrent.atomic.AtomicBoolean +import type.SensorData -class MainActivity : AppCompatActivity() { +class MainActivity : AppCompatActivity(), NativeMethodCallback { private lateinit var binding: ActivityMainBinding - private var parserHandle: Long = 0L - private val rawStream = ByteArrayOutputStream(4096) - private val uiUpdatePending = AtomicBoolean(false) - private val packetBuffer = mutableListOf() - private var lastUpdateTime = 0L - private val UPDATE_INTERVAL = 500L // 每500毫秒更新一次UI - private var totalPacketsParsed = 0L // 总共解析的数据包数量 + private lateinit var dataManager: DataManager + private lateinit var uiManager: UiManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -27,304 +18,322 @@ class MainActivity : AppCompatActivity() { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - // 初始化UI - binding.sampleText.text = "正在初始化..." + // 初始化管理器 + dataManager = DataManager(this) // 传入this作为NativeMethodCallback + uiManager = UiManager() - // 设置按钮点击事件 - binding.btnReset.setOnClickListener { - resetData() - } - - binding.btnReload.setOnClickListener { - reloadData() - } + // 初始化UI + binding.sampleText.text = "正在初始化...\n\n请稍候,正在加载数据文件..." + + // 移除按钮点击事件,只保留流式读取功能 // 在后台线程处理数据加载和解析 Thread { try { + Log.d("MainActivity", "开始加载数据文件...") + // 从 assets 文件夹读取文件 - val fileData = readAssetFile("data1.dat") + val fileData = FileHelper.readAssetFile(this, Constants.DEFAULT_DATA_FILE) + Log.d("MainActivity", "文件读取结果: ${if (fileData != null) "成功,大小: ${fileData.size} 字节" else "失败"}") if (fileData != null) { - // 使用真实流式解析路径:按块喂数据 -> drain -> 展示 - ensureParser() - rawStream.reset() - val chunkSize = 64 - var offset = 0 - - while (offset < fileData.size) { - val n = minOf(chunkSize, fileData.size - offset) - val chunk = fileData.copyOfRange(offset, offset + n) - onBleNotify(chunk) // 复用同一流式路径 - offset += n - - // 添加小延迟,模拟真实蓝牙数据传输 - Thread.sleep(10) + // 先显示文件读取成功的信息 + runOnUiThread { + binding.sampleText.text = "文件读取成功!\n文件大小: ${fileData.size} 字节\n\n正在处理数据,请稍候..." } - - // 最后拉取所有剩余包 - val packets = streamParserDrainPackets(parserHandle) - if (!packets.isNullOrEmpty()) { - totalPacketsParsed += packets.size // 更新总计数 - packetBuffer.addAll(packets) - scheduleUiUpdate() + + // 使用真实流式解析路径:按块喂数据 -> drain -> 展示 + Log.d("MainActivity", "开始处理文件数据...") + try { + // 添加进度回调 + dataManager.processFileData(fileData) { progress -> + // 进度更新回调 + runOnUiThread { + val progressText = buildString { + append("文件读取成功!\n") + append("文件大小: ${fileData.size} 字节\n\n") + append("正在处理数据...\n") + append("进度: $progress%\n") + append("进度条: ${"█".repeat(progress / 5)}${"░".repeat(20 - (progress / 5))}\n") + append("请稍候...") + } + binding.sampleText.text = progressText + } + } + Log.d("MainActivity", "文件数据处理完成") + + // 直接更新UI,显示加载结果 + runOnUiThread { + try { + Log.d("MainActivity", "开始UI更新流程") + + // 暂时禁用测试指标计算功能,避免闪退 + Log.d("MainActivity", "跳过测试指标计算功能") + /* + try { + dataManager.testIndicatorCalculation() + Log.d("MainActivity", "测试指标计算完成") + } catch (e: Exception) { + Log.e("MainActivity", "测试指标计算失败: ${e.message}", e) + } + */ + + Log.d("MainActivity", "开始构建显示内容") + val displayContent = try { + // 构建详细的显示内容,包括映射信息 + val detailedContent = buildString { + append("=== 设备数据概览 ===\n") + append("原始数据包: ${dataManager.getPacketBufferSize()} 个\n") + append("映射后数据包: ${dataManager.getProcessedPacketsSize()} 个\n") + append("计算指标数: ${dataManager.getCalculatedMetricsSize()} 个\n") + append("总数据量: ${dataManager.getRawStreamSize()} 字节\n") + append("总共解析: ${dataManager.getTotalPacketsParsed()} 个数据包\n") + + // 显示原始数据包信息 + if (dataManager.getPacketBufferSize() > 0) { + append("\n=== 原始数据信息 ===\n") + try { + val firstPacket = dataManager.getPacketBuffer().firstOrNull() + if (firstPacket != null) { + append("第一个数据包类型: ${firstPacket.getDataType()}\n") + append("第一个数据包序号: ${firstPacket.getPacketSn()}\n") + append("第一个数据包时间戳: ${firstPacket.getTimestamp()}\n") + + val channelData = firstPacket.getChannelData() + if (channelData != null) { + append("通道数量: ${channelData.size}\n") + if (channelData.isNotEmpty()) { + val firstChannel = channelData[0] + append("第一个通道数据点数: ${firstChannel.size}\n") + if (firstChannel.isNotEmpty()) { + append("第一个通道前3个值: ${firstChannel.take(3).joinToString(", ")}\n") + } + } + } + } + } catch (e: Exception) { + append("获取原始数据包信息时出错: ${e.message}\n") + } + } + + // 显示映射后的数据包信息 + if (dataManager.getProcessedPacketsSize() > 0) { + append("\n=== 映射后数据信息 ===\n") + try { + val processedPackets = dataManager.getProcessedPackets() + append("DEBUG: 处理后数据包总数: ${processedPackets.size}\n") + + // 统计ECG_12LEAD数据包的通道数量分布 + val ecg12LeadPackets = processedPackets.filter { it.getDataType() == type.SensorData.DataType.ECG_12LEAD } + val packetsWith8Channels = ecg12LeadPackets.count { it.getChannelData()?.size == 8 } + val packetsWith12Channels = ecg12LeadPackets.count { it.getChannelData()?.size == 12 } + append("DEBUG: ECG_12LEAD数据包统计 - 8通道: ${packetsWith8Channels}个, 12通道: ${packetsWith12Channels}个\n") + + // 详细统计前10个ECG_12LEAD数据包 + val first10EcgPackets = ecg12LeadPackets.take(10) + append("DEBUG: 前10个ECG_12LEAD数据包详情:\n") + first10EcgPackets.forEachIndexed { index, packet -> + append(" 数据包${index + 1}: 序号=${packet.getPacketSn()}, 通道数=${packet.getChannelData()?.size}\n") + } + + // 查找第一个映射成功的ECG_12LEAD数据包(12通道) + val mappedEcg12LeadPacket = processedPackets.find { packet -> + packet.getDataType() == type.SensorData.DataType.ECG_12LEAD && + packet.getChannelData()?.size == 12 + } + val firstMappedPacket = mappedEcg12LeadPacket ?: processedPackets.firstOrNull() + + if (firstMappedPacket != null) { + append("选择的数据包类型: ${firstMappedPacket.getDataType()}\n") + append("选择的数据包序号: ${firstMappedPacket.getPacketSn()}\n") + append("选择的数据包时间戳: ${firstMappedPacket.getTimestamp()}\n") + + val mappedChannelData = firstMappedPacket.getChannelData() + if (mappedChannelData != null) { + append("映射后通道数量: ${mappedChannelData.size}\n") + append("DEBUG: 通道数据详情: ${mappedChannelData}\n") + if (mappedChannelData.isNotEmpty()) { + val firstMappedChannel = mappedChannelData[0] + append("第一个映射通道数据点数: ${firstMappedChannel.size}\n") + if (firstMappedChannel.isNotEmpty()) { + append("第一个映射通道前3个值: ${firstMappedChannel.take(3).joinToString(", ")}\n") + } + + // 显示第二个通道数据 + if (mappedChannelData.size > 1) { + val secondMappedChannel = mappedChannelData[1] + append("第二个映射通道数据点数: ${secondMappedChannel.size}\n") + if (secondMappedChannel.isNotEmpty()) { + append("第二个映射通道前3个值: ${secondMappedChannel.take(3).joinToString(", ")}\n") + } + } + } + } + } + } catch (e: Exception) { + append("获取映射数据包信息时出错: ${e.message}\n") + } + } + + // 显示计算出的指标 + if (dataManager.getCalculatedMetricsSize() > 0) { + append("\n=== 计算指标信息 ===\n") + try { + val latestMetrics = dataManager.getLatestMetrics() + if (latestMetrics != null) { + append("最新指标数量: ${latestMetrics.size}\n") + append("指标详情:\n") + latestMetrics.forEach { (key, value) -> + append(" $key: ${String.format("%.2f", value)}\n") + } + } + + // 显示所有指标的平均值 + val allMetrics = dataManager.getCalculatedMetrics() + if (allMetrics.isNotEmpty()) { + val heartRates = allMetrics.mapNotNull { metrics -> metrics["heart_rate"] }.filter { it > 0 } + val qualities = allMetrics.mapNotNull { metrics -> metrics["signal_quality"] }.filter { it >= 0 } + + if (heartRates.isNotEmpty()) { + val avgHeartRate = heartRates.average() + append("平均心率: ${String.format("%.1f", avgHeartRate)} bpm\n") + } + + if (qualities.isNotEmpty()) { + val avgQuality = qualities.average() + append("平均信号质量: ${String.format("%.2f", avgQuality)}\n") + } + } + } catch (e: Exception) { + append("获取指标信息时出错: ${e.message}\n") + } + } + + append("\n=== 状态信息 ===\n") + append("数据解析完成 ✓\n") + append("数据映射完成 ✓\n") + append("信号处理完成 ✓\n") + append("指标计算完成 ✓\n") + append("应用已就绪,可以开始使用\n") + + // 显示流式处理状态 + val processingStatus = dataManager.getProcessingStatus() + append("\n=== 流式处理状态 ===\n") + append("当前数据类型: ${processingStatus["currentDataType"]}\n") + append("总样本数: ${processingStatus["totalSamples"]}/${processingStatus["minSamplesRequired"]}\n") + append("距离上次处理: ${processingStatus["timeSinceLastProcess"]}ms/${processingStatus["processingInterval"]}ms\n") + append("总处理样本数: ${processingStatus["totalProcessedSamples"]}\n") + + // 显示流式指标计算结果 + val latestMetrics = dataManager.getLatestMetrics() + if (latestMetrics.isNotEmpty()) { + append("\n=== 流式指标计算结果 ===\n") + append("基于流式数据处理的最新指标:\n") + latestMetrics.forEach { (key, value) -> + append(" $key: $value\n") + } + } else { + append("\n=== 流式指标计算结果 ===\n") + append("暂无流式指标计算结果\n") + } + + // 显示原始通道缓冲区状态 + val channelBuffers = dataManager.getChannelBuffersStatus() + if (channelBuffers.isNotEmpty()) { + append("\n=== 原始通道缓冲区状态 ===\n") + channelBuffers.forEach { (channel, sampleCount) -> + append(" 通道 $channel: $sampleCount 个样本\n") + } + } + + // 显示处理后通道缓冲区状态 + val processedChannelBuffers = dataManager.getProcessedChannelBuffersStatus() + if (processedChannelBuffers.isNotEmpty()) { + append("\n=== 处理后通道缓冲区状态 ===\n") + processedChannelBuffers.forEach { (channel, sampleCount) -> + append(" 通道 $channel: $sampleCount 个样本\n") + } + } + } + detailedContent + } catch (e: Exception) { + Log.e("MainActivity", "构建显示内容失败: ${e.message}", e) + "构建显示内容失败: ${e.message}\n\n请检查数据格式。" + } + Log.d("MainActivity", "显示内容构建完成,长度: ${displayContent.length}") + + try { + binding.sampleText.text = displayContent + Log.d("MainActivity", "UI文本设置成功") + } catch (e: Exception) { + Log.e("MainActivity", "设置UI文本失败: ${e.message}", e) + binding.sampleText.text = "设置UI文本失败: ${e.message}" + } + + // 记录日志 + Log.d("MainActivity", "UI更新完成,数据包数量: ${dataManager.getPacketBufferSize()}, 总解析: ${dataManager.getTotalPacketsParsed()}") + } catch (e: Exception) { + Log.e("MainActivity", "UI更新失败: ${e.message}", e) + val errorMessage = buildString { + append("UI更新失败: ${e.message}\n\n") + append("数据包数量: ${dataManager.getPacketBufferSize()}\n") + append("总解析: ${dataManager.getTotalPacketsParsed()}\n") + append("错误详情: ${e.stackTraceToString()}") + } + try { + binding.sampleText.text = errorMessage + } catch (e2: Exception) { + Log.e("MainActivity", "设置错误信息也失败: ${e2.message}", e2) + } + } + } + } catch (e: Exception) { + Log.e("MainActivity", "数据处理失败: ${e.message}", e) + runOnUiThread { + binding.sampleText.text = "数据处理失败: ${e.message}\n\n请检查数据文件格式是否正确。" + } } } else { runOnUiThread { - binding.sampleText.text = "读取文件失败" + binding.sampleText.text = "读取文件失败:无法从assets文件夹读取${Constants.DEFAULT_DATA_FILE}\n\n请检查:\n1. assets文件夹中是否存在该文件\n2. 文件名是否正确\n3. 文件是否损坏" } } } catch (e: Exception) { Log.e("MainActivity", "Error processing data", e) runOnUiThread { - binding.sampleText.text = "错误: ${e.message}" + binding.sampleText.text = "错误: ${e.message}\n\n请检查assets文件夹中是否存在${Constants.DEFAULT_DATA_FILE}文件" } } + }.apply { + // 设置线程名称,便于调试 + name = "DataLoadingThread" + // 设置为守护线程,避免阻塞应用退出 + isDaemon = true }.start() } override fun onStart() { super.onStart() - ensureParser() + dataManager.ensureParser() } override fun onStop() { super.onStop() - if (parserHandle != 0L) { - destroyStreamParser(parserHandle) - parserHandle = 0L - } - } - - private fun ensureParser() { - if (parserHandle == 0L) { - parserHandle = createStreamParser() - } + dataManager.cleanup() } // 蓝牙通知回调时调用:将 chunk 追加到解析器并拉取新包 - private fun onBleNotify(chunk: ByteArray) { - if (chunk.isEmpty()) return - ensureParser() - rawStream.write(chunk) - streamParserAppend(parserHandle, chunk) - - // 拉取解析出的数据包,但不立即更新UI - val packets = streamParserDrainPackets(parserHandle) - if (!packets.isNullOrEmpty()) { - totalPacketsParsed += packets.size // 更新总计数 - packetBuffer.addAll(packets) - scheduleUiUpdate() - } - } - - // 计划UI更新,避免频繁刷新 - private fun scheduleUiUpdate() { - val currentTime = System.currentTimeMillis() - if (currentTime - lastUpdateTime >= UPDATE_INTERVAL && uiUpdatePending.compareAndSet(false, true)) { - lastUpdateTime = currentTime - runOnUiThread { - updateUiWithBufferedPackets() - uiUpdatePending.set(false) - } - } - } - - // 使用缓冲的数据包更新UI - private fun updateUiWithBufferedPackets() { - if (packetBuffer.isEmpty()) return - - // 获取设备类型信息 - val deviceTypes = packetBuffer.mapNotNull { it.dataType }.distinct() - val deviceInfo = deviceTypes.joinToString(", ") { getDeviceName(it) } - - // 构建统计信息 - val stats = buildString { - append("=== 设备数据概览 ===\n") - append("当前缓冲区: ${packetBuffer.size} 个数据包\n") - append("总共解析: ${totalPacketsParsed} 个数据包\n") - append("设备类型: $deviceInfo\n") - append("总数据量: ${rawStream.size()} 字节\n\n") - } - - // 只显示最新的一些数据包详情 - val recentPackets = packetBuffer.takeLast(5) // 只显示最后5个包 - val has12Lead = recentPackets.any { it.dataType == SensorData.DataType.ECG_12LEAD } - val channelDetails = if (has12Lead) { - buildChannelDetails(recentPackets, maxPackets = 3, maxChannels = 6, maxSamples = 10) - } else { - buildChannelDetails(recentPackets, maxPackets = 3, maxChannels = 4, maxSamples = 10) - } - - // 更新UI - binding.sampleText.text = stats + channelDetails - - // 智能清理缓冲区:如果数据包数量过多,保留更多用于统计 - if (packetBuffer.size > 50) { - val keepCount = minOf(30, packetBuffer.size / 2) // 保留30个或一半,取较小值 - packetBuffer.subList(0, packetBuffer.size - keepCount).clear() - Log.d("MainActivity", "缓冲区已清理,保留 $keepCount 个数据包") - } + fun onBleNotify(chunk: ByteArray) { + dataManager.onBleNotify(chunk) - // 添加调试信息:显示实际的数据包数量变化 - Log.d("MainActivity", "当前缓冲区数据包数量: ${packetBuffer.size}, 总共解析: $totalPacketsParsed") - } - - /** - * 根据数据类型获取设备名称 - */ - private fun getDeviceName(dataType: SensorData.DataType): String { - return when (dataType) { - SensorData.DataType.EEG -> "脑电设备" - SensorData.DataType.ECG_2LEAD -> "胸腹设备" - SensorData.DataType.PPG -> "血氧设备" - SensorData.DataType.ECG_12LEAD -> "12导联心电" - SensorData.DataType.STETHOSCOPE -> "数字听诊" - SensorData.DataType.SNORE -> "鼾声设备" - SensorData.DataType.RESPIRATION -> "呼吸/姿态" - SensorData.DataType.MIT_BIH -> "MIT-BIH" - else -> "未知设备" - } - } - - /** - * 从 assets 文件夹读取文件到字节数组 - */ - private fun readAssetFile(fileName: String): ByteArray? { - return try { - assets.open(fileName).use { inputStream -> - ByteArray(inputStream.available()).also { - inputStream.read(it) - } + // 触发UI更新 + uiManager.scheduleUiUpdate(dataManager) { + uiManager.updateDisplay(dataManager) { text -> + binding.sampleText.text = text } - } catch (e: IOException) { - Log.e("MainActivity", "Error reading asset file", e) - null } } - /** - * 构建通道数据详情字符串 - */ - private fun buildChannelDetails(data: List, maxPackets: Int = 3, maxChannels: Int = 2, maxSamples: Int = 10): String { - if (data.isEmpty()) { - return "无通道数据" - } - - val details = mutableListOf() - - data.take(maxPackets).forEachIndexed { packetIndex, sensorData -> - if (sensorData.channelData.isNullOrEmpty()) { - details.add("数据包 ${packetIndex + 1}: 无通道数据") - return@forEachIndexed - } - - details.add("数据包 ${packetIndex + 1} (${getDeviceName(sensorData.dataType ?: SensorData.DataType.EEG)}):") - - sensorData.channelData.take(maxChannels).forEachIndexed { channelIndex, channel -> - // 只显示前几个采样点 - val sampleCount = minOf(maxSamples, channel.size) - val channelDataStr = channel.take(sampleCount).joinToString(", ") { "%.1f".format(it) } - - details.add(" 通道 ${channelIndex + 1}: ${sampleCount}/${channel.size} 采样点") - details.add(" 示例: $channelDataStr${if (channel.size > sampleCount) "..." else ""}") - } - - if (sensorData.channelData.size > maxChannels) { - details.add(" ... 还有 ${sensorData.channelData.size - maxChannels} 个通道") - } - - // 添加分隔线 - details.add("") - } - - if (data.size > maxPackets) { - details.add("... 还有 ${data.size - maxPackets} 个数据包") - } - - return details.joinToString("\n") - } - - /** - * A native method that is implemented by the 'cmake_project_test' native library, - * which is packaged with this application. - */ - external fun stringFromJNI(): String - external fun addFromJNI(a: Int, b: Int): Int - external fun parseDeviceDataFromJNI(fileData: ByteArray): List? - external fun createStreamParser(): Long - external fun destroyStreamParser(handle: Long) - external fun streamParserAppend(handle: Long, chunk: ByteArray) - external fun streamParserDrainPackets(handle: Long): List? - external fun parseStreamFromBytes(data: ByteArray, chunkSize: Int): List? - - /** - * 重置所有数据 - */ - private fun resetData() { - packetBuffer.clear() - rawStream.reset() - totalPacketsParsed = 0L // 重置总计数 - binding.sampleText.text = "数据已重置" - Log.d("MainActivity", "数据已重置,当前数据包数量: ${packetBuffer.size}, 总计数: $totalPacketsParsed") - } - - /** - * 重新加载数据 - */ - private fun reloadData() { - binding.sampleText.text = "正在重新加载数据..." - - // 重置解析器 - if (parserHandle != 0L) { - destroyStreamParser(parserHandle) - parserHandle = 0L - } - - // 清空缓冲区和重置计数 - packetBuffer.clear() - rawStream.reset() - totalPacketsParsed = 0L - - // 在后台线程重新加载数据 - Thread { - try { - val fileData = readAssetFile("data1.dat") - - if (fileData != null) { - ensureParser() - val chunkSize = 64 - var offset = 0 - - while (offset < fileData.size) { - val n = minOf(chunkSize, fileData.size - offset) - val chunk = fileData.copyOfRange(offset, offset + n) - onBleNotify(chunk) - offset += n - Thread.sleep(10) - } - - // 最后拉取所有剩余包 - val packets = streamParserDrainPackets(parserHandle) - if (!packets.isNullOrEmpty()) { - packetBuffer.addAll(packets) - scheduleUiUpdate() - } - - Log.d("MainActivity", "数据重新加载完成,当前数据包数量: ${packetBuffer.size}") - } else { - runOnUiThread { - binding.sampleText.text = "重新加载失败:文件读取失败" - } - } - } catch (e: Exception) { - Log.e("MainActivity", "Error reloading data", e) - runOnUiThread { - binding.sampleText.text = "重新加载错误: ${e.message}" - } - } - }.start() - } companion object { // Used to load the 'cmake_project_test' library on application startup. @@ -332,4 +341,10 @@ class MainActivity : AppCompatActivity() { System.loadLibrary("cmake_project_test") } } + + // 原生方法声明 - 保持原来的JNI函数名 + override external fun createStreamParser(): Long + override external fun destroyStreamParser(handle: Long) + override external fun streamParserAppend(handle: Long, chunk: ByteArray) + override external fun streamParserDrainPackets(handle: Long): List? } \ No newline at end of file diff --git a/app/src/main/java/com/example/cmake_project_test/NativeMethodCallback.kt b/app/src/main/java/com/example/cmake_project_test/NativeMethodCallback.kt new file mode 100644 index 0000000..9c3c5fc --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/NativeMethodCallback.kt @@ -0,0 +1,14 @@ +package com.example.cmake_project_test + +import type.SensorData + +/** + * 原生方法回调接口 + * 用于DataManager调用MainActivity中的原生方法 + */ +interface NativeMethodCallback { + fun createStreamParser(): Long + fun destroyStreamParser(handle: Long) + fun streamParserAppend(handle: Long, chunk: ByteArray) + fun streamParserDrainPackets(handle: Long): List? +} diff --git a/app/src/main/java/com/example/cmake_project_test/SignalProcessingExample.kt b/app/src/main/java/com/example/cmake_project_test/SignalProcessingExample.kt new file mode 100644 index 0000000..7552ce5 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/SignalProcessingExample.kt @@ -0,0 +1,206 @@ +package com.example.cmake_project_test + +import android.util.Log +import type.SensorData + +/** + * 信号处理使用示例 + * 展示如何在现有流式数据读取代码结构中使用信号处理功能 + */ +class SignalProcessingExample(private val dataManager: DataManager) { + + companion object { + private const val TAG = "SignalProcessingExample" + } + + /** + * 示例1:实时信号质量监控 + * 在流式数据处理过程中实时监控信号质量 + */ + fun demonstrateRealTimeQualityMonitoring() { + Log.d(TAG, "=== 开始实时信号质量监控演示 ===") + + val packets = dataManager.getPacketBuffer() + if (packets.isEmpty()) { + Log.w(TAG, "没有数据包可供监控") + return + } + + // 监控前10个数据包的质量 + val packetsToMonitor = packets.take(10) + var totalQuality = 0.0f + var validPackets = 0 + + for ((index, packet) in packetsToMonitor.withIndex()) { + val quality = dataManager.calculateSignalQuality(packet) + if (quality > 0) { + totalQuality += quality + validPackets++ + Log.d(TAG, "数据包 $index (${packet.dataType}): 质量 = $quality") + } + } + + if (validPackets > 0) { + val averageQuality = totalQuality / validPackets + Log.d(TAG, "平均信号质量: $averageQuality") + + // 根据质量决定是否需要调整滤波器参数 + if (averageQuality < 0.5f) { + Log.w(TAG, "信号质量较低,建议检查传感器连接或调整滤波器参数") + } else if (averageQuality > 0.8f) { + Log.i(TAG, "信号质量良好") + } + } + } + + /** + * 示例2:按设备类型应用不同的信号处理策略 + */ + fun demonstrateDeviceSpecificProcessing() { + Log.d(TAG, "=== 开始设备特定信号处理演示 ===") + + val packets = dataManager.getPacketBuffer() + if (packets.isEmpty()) { + Log.w(TAG, "没有数据包可供处理") + return + } + + // 按设备类型分组 + val packetsByType = packets.groupBy { it.dataType } + + for ((dataType, typePackets) in packetsByType) { + Log.d(TAG, "处理 ${dataType.name} 类型数据,共 ${typePackets.size} 个数据包") + + when (dataType) { + SensorData.DataType.EEG -> { + Log.d(TAG, "应用EEG专用滤波器:带通滤波(1-40Hz) + 幅度归一化") + } + SensorData.DataType.ECG_2LEAD, SensorData.DataType.ECG_12LEAD -> { + Log.d(TAG, "应用ECG专用滤波器:高通(0.5Hz) + 低通(40Hz) + 陷波(50Hz)") + } + SensorData.DataType.PPG -> { + Log.d(TAG, "应用PPG专用滤波器:低通(8Hz) + 运动伪影去除") + } + else -> { + Log.d(TAG, "数据类型 ${dataType.name} 暂不支持专用处理") + } + } + } + } + + /** + * 示例3:批量信号处理 + * 对大量数据进行批量信号处理 + */ + fun demonstrateBatchProcessing() { + Log.d(TAG, "=== 开始批量信号处理演示 ===") + + val packets = dataManager.getPacketBuffer() + if (packets.isEmpty()) { + Log.w(TAG, "没有数据包可供批量处理") + return + } + + Log.d(TAG, "开始批量处理 ${packets.size} 个数据包...") + + // 记录处理开始时间 + val startTime = System.currentTimeMillis() + + // 应用信号处理 + val processedPackets = dataManager.applySignalProcessing(packets) + + // 记录处理结束时间 + val endTime = System.currentTimeMillis() + val processingTime = endTime - startTime + + Log.d(TAG, "批量处理完成!") + Log.d(TAG, "处理时间: ${processingTime}ms") + Log.d(TAG, "处理数据包: ${processedPackets.size} 个") + Log.d(TAG, "平均处理速度: ${packets.size * 1000.0 / processingTime} 包/秒") + + // 显示处理后的信号质量统计 + if (processedPackets.isNotEmpty()) { + val qualityScores = mutableListOf() + for (packet in processedPackets.take(20)) { // 只检查前20个包 + val quality = dataManager.calculateSignalQuality(packet) + if (quality > 0) { + qualityScores.add(quality) + } + } + + if (qualityScores.isNotEmpty()) { + val avgQuality = qualityScores.average() + val maxQuality = qualityScores.maxOrNull() ?: 0f + val minQuality = qualityScores.minOrNull() ?: 0f + + Log.d(TAG, "处理后信号质量统计:") + Log.d(TAG, " 平均质量: ${String.format("%.3f", avgQuality)}") + Log.d(TAG, " 最高质量: ${String.format("%.3f", maxQuality)}") + Log.d(TAG, " 最低质量: ${String.format("%.3f", minQuality)}") + } + } + } + + /** + * 示例4:自适应滤波器参数调整 + * 根据信号质量自动调整滤波器参数 + */ + fun demonstrateAdaptiveFilterAdjustment() { + Log.d(TAG, "=== 开始自适应滤波器参数调整演示 ===") + + val packets = dataManager.getPacketBuffer() + if (packets.isEmpty()) { + Log.w(TAG, "没有数据包可供自适应处理") + return + } + + // 获取第一个数据包进行质量评估 + val firstPacket = packets.first() + val initialQuality = dataManager.calculateSignalQuality(firstPacket) + + Log.d(TAG, "初始信号质量: $initialQuality") + + if (initialQuality < 0.3f) { + Log.w(TAG, "信号质量过低,建议检查硬件连接") + } else if (initialQuality < 0.6f) { + Log.i(TAG, "信号质量一般,可以尝试调整滤波器参数") + + // 这里可以添加自适应参数调整逻辑 + // 例如:根据噪声水平调整截止频率 + when (firstPacket.dataType) { + SensorData.DataType.EEG -> { + Log.d(TAG, "建议调整EEG滤波器:降低高通截止频率到0.5Hz,提高低通截止频率到50Hz") + } + SensorData.DataType.ECG_2LEAD, SensorData.DataType.ECG_12LEAD -> { + Log.d(TAG, "建议调整ECG滤波器:提高高通截止频率到1Hz,降低低通截止频率到30Hz") + } + SensorData.DataType.PPG -> { + Log.d(TAG, "建议调整PPG滤波器:降低低通截止频率到6Hz") + } + else -> { + Log.d(TAG, "该数据类型暂不支持自适应调整") + } + } + } else { + Log.i(TAG, "信号质量良好,当前滤波器参数合适") + } + } + + /** + * 运行所有演示 + */ + fun runAllDemonstrations() { + Log.d(TAG, "开始运行所有信号处理演示...") + + try { + demonstrateRealTimeQualityMonitoring() + demonstrateDeviceSpecificProcessing() + demonstrateBatchProcessing() + demonstrateAdaptiveFilterAdjustment() + + Log.d(TAG, "所有演示完成!") + } catch (e: Exception) { + Log.e(TAG, "演示过程中发生错误", e) + } + } +} diff --git a/app/src/main/java/com/example/cmake_project_test/SignalProcessorExample.kt b/app/src/main/java/com/example/cmake_project_test/SignalProcessorExample.kt new file mode 100644 index 0000000..bd13998 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/SignalProcessorExample.kt @@ -0,0 +1,271 @@ +package com.example.cmake_project_test + +import android.util.Log +import kotlin.math.PI +import kotlin.math.sin + +/** + * 信号处理使用示例类 + * 展示如何使用JNI信号处理功能 + */ +class SignalProcessorExample { + + private val signalProcessor = SignalProcessorJNI() + private val sampleRate = 1000.0 // 1kHz采样率 + + init { + // 初始化信号处理器 + if (!signalProcessor.createProcessor()) { + Log.e("SignalProcessorExample", "Failed to create signal processor") + } + } + + /** + * 生成测试信号:正弦波 + 噪声 + */ + fun generateTestSignal(frequency: Double, duration: Double, noiseLevel: Double = 0.1): FloatArray { + val numSamples = (sampleRate * duration).toInt() + val signal = FloatArray(numSamples) + + for (i in 0 until numSamples) { + val time = i / sampleRate + val sineWave = sin(2 * PI * frequency * time).toFloat() + val noise = (Math.random() * 2 - 1).toFloat() * noiseLevel + signal[i] = (sineWave + noise).toFloat() + } + + return signal + } + + /** + * 演示带通滤波 + */ + fun demonstrateBandpassFilter() { + Log.d("SignalProcessorExample", "=== 带通滤波演示 ===") + + // 生成包含多个频率的测试信号 + val signal = generateTestSignal(50.0, 1.0, 0.2) // 50Hz + 噪声 + + // 应用带通滤波 (40-60Hz) + val filteredSignal = signalProcessor.bandpassFilter(signal, sampleRate, 40.0, 60.0) + + if (filteredSignal != null) { + Log.d("SignalProcessorExample", "滤波成功!原始信号长度: ${signal.size}, 滤波后长度: ${filteredSignal.size}") + + // 计算信号质量 + val originalQuality = signalProcessor.calculateSignalQuality(signal) + val filteredQuality = signalProcessor.calculateSignalQuality(filteredSignal) + + Log.d("SignalProcessorExample", "原始信号质量: $originalQuality") + Log.d("SignalProcessorExample", "滤波后信号质量: $filteredQuality") + } else { + Log.e("SignalProcessorExample", "带通滤波失败") + } + } + + /** + * 演示低通滤波 + */ + fun demonstrateLowpassFilter() { + Log.d("SignalProcessorExample", "=== 低通滤波演示 ===") + + // 生成高频信号 + val signal = generateTestSignal(200.0, 1.0, 0.1) + + // 应用低通滤波 (100Hz截止) + val filteredSignal = signalProcessor.lowpassFilter(signal, sampleRate, 100.0) + + if (filteredSignal != null) { + Log.d("SignalProcessorExample", "低通滤波成功!") + + // 计算信号质量 + val originalQuality = signalProcessor.calculateSignalQuality(signal) + val filteredQuality = signalProcessor.calculateSignalQuality(filteredSignal) + + Log.d("SignalProcessorExample", "原始信号质量: $originalQuality") + Log.d("SignalProcessorExample", "滤波后信号质量: $filteredQuality") + } else { + Log.e("SignalProcessorExample", "低通滤波失败") + } + } + + /** + * 演示高通滤波 + */ + fun demonstrateHighpassFilter() { + Log.d("SignalProcessorExample", "=== 高通滤波演示 ===") + + // 生成包含低频和高频的信号 + val signal = generateTestSignal(10.0, 1.0, 0.1) // 10Hz低频信号 + + // 应用高通滤波 (50Hz截止) + val filteredSignal = signalProcessor.highpassFilter(signal, sampleRate, 50.0) + + if (filteredSignal != null) { + Log.d("SignalProcessorExample", "高通滤波成功!") + + // 计算信号质量 + val originalQuality = signalProcessor.calculateSignalQuality(signal) + val filteredQuality = signalProcessor.calculateSignalQuality(filteredSignal) + + Log.d("SignalProcessorExample", "原始信号质量: $originalQuality") + Log.d("SignalProcessorExample", "滤波后信号质量: $filteredQuality") + } else { + Log.e("SignalProcessorExample", "高通滤波失败") + } + } + + /** + * 演示陷波滤波(去除工频干扰) + */ + fun demonstrateNotchFilter() { + Log.d("SignalProcessorExample", "=== 陷波滤波演示 ===") + + // 生成包含工频干扰的信号 + val signal = generateTestSignal(50.0, 1.0, 0.3) // 50Hz工频 + 噪声 + + // 应用陷波滤波 (50Hz陷波) + val filteredSignal = signalProcessor.notchFilter(signal, sampleRate, 50.0, 30.0) + + if (filteredSignal != null) { + Log.d("SignalProcessorExample", "陷波滤波成功!") + + // 计算信号质量 + val originalQuality = signalProcessor.calculateSignalQuality(signal) + val filteredQuality = signalProcessor.calculateSignalQuality(filteredSignal) + + Log.d("SignalProcessorExample", "原始信号质量: $originalQuality") + Log.d("SignalProcessorExample", "滤波后信号质量: $filteredQuality") + } else { + Log.e("SignalProcessorExample", "陷波滤波失败") + } + } + + /** + * 演示ECG信号质量评估 + */ + fun demonstrateECGSQI() { + Log.d("SignalProcessorExample", "=== ECG信号质量评估演示 ===") + + // 生成模拟ECG信号 + val ecgSignal = generateTestSignal(1.0, 2.0, 0.05) // 1Hz心跳 + 低噪声 + + // 计算ECG信号质量指数 + val sqi = signalProcessor.calculateECGSQI(ecgSignal, sampleRate) + + Log.d("SignalProcessorExample", "ECG信号质量指数: $sqi") + + if (sqi > 0.7f) { + Log.d("SignalProcessorExample", "ECG信号质量良好") + } else if (sqi > 0.4f) { + Log.d("SignalProcessorExample", "ECG信号质量一般") + } else { + Log.d("SignalProcessorExample", "ECG信号质量较差") + } + } + + /** + * 演示信号特征提取 + */ + fun demonstrateFeatureExtraction() { + Log.d("SignalProcessorExample", "=== 信号特征提取演示 ===") + + // 生成测试信号 + val signal = generateTestSignal(100.0, 0.5, 0.15) + + // 提取特征 + val features = signalProcessor.extractFeatures(signal, sampleRate) + + if (features != null) { + Log.d("SignalProcessorExample", "特征提取成功!特征数量: ${features.size}") + + // 显示前几个特征值 + for (i in 0 until minOf(5, features.size)) { + Log.d("SignalProcessorExample", "特征 $i: ${features[i]}") + } + } else { + Log.e("SignalProcessorExample", "特征提取失败") + } + } + + /** + * 演示信号归一化 + */ + fun demonstrateNormalization() { + Log.d("SignalProcessorExample", "=== 信号归一化演示 ===") + + // 生成幅度较大的信号 + val signal = generateTestSignal(75.0, 0.5, 0.2) + + // 找到最大绝对值 + val maxAbs = signal.maxOfOrNull { kotlin.math.abs(it) } ?: 0f + Log.d("SignalProcessorExample", "归一化前最大绝对值: $maxAbs") + + // 归一化 + signalProcessor.normalizeAmplitude(signal) + + // 检查归一化结果 + val normalizedMaxAbs = signal.maxOfOrNull { kotlin.math.abs(it) } ?: 0f + Log.d("SignalProcessorExample", "归一化后最大绝对值: $normalizedMaxAbs") + + if (kotlin.math.abs(normalizedMaxAbs - 1.0f) < 0.01f) { + Log.d("SignalProcessorExample", "归一化成功!") + } else { + Log.e("SignalProcessorExample", "归一化失败") + } + } + + /** + * 演示相关性计算 + */ + fun demonstrateCorrelation() { + Log.d("SignalProcessorExample", "=== 相关性计算演示 ===") + + // 生成两个相关信号 + val signal1 = generateTestSignal(50.0, 1.0, 0.1) + val signal2 = generateTestSignal(50.0, 1.0, 0.1) // 相同频率 + + // 计算相关性 + val correlation = signalProcessor.calculateCorrelation(signal1, signal2) + + Log.d("SignalProcessorExample", "信号相关性: $correlation") + + if (correlation > 0.8f) { + Log.d("SignalProcessorExample", "信号高度相关") + } else if (correlation > 0.5f) { + Log.d("SignalProcessorExample", "信号中等相关") + } else { + Log.d("SignalProcessorExample", "信号相关性较低") + } + } + + /** + * 运行所有演示 + */ + fun runAllDemonstrations() { + Log.d("SignalProcessorExample", "开始运行所有信号处理演示...") + + try { + demonstrateBandpassFilter() + demonstrateLowpassFilter() + demonstrateHighpassFilter() + demonstrateNotchFilter() + demonstrateECGSQI() + demonstrateFeatureExtraction() + demonstrateNormalization() + demonstrateCorrelation() + + Log.d("SignalProcessorExample", "所有演示完成!") + } catch (e: Exception) { + Log.e("SignalProcessorExample", "演示过程中发生错误", e) + } + } + + /** + * 清理资源 + */ + fun cleanup() { + signalProcessor.destroyProcessor() + Log.d("SignalProcessorExample", "信号处理器已销毁") + } +} diff --git a/app/src/main/java/com/example/cmake_project_test/SignalProcessorJNI.kt b/app/src/main/java/com/example/cmake_project_test/SignalProcessorJNI.kt new file mode 100644 index 0000000..d9ba3c7 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/SignalProcessorJNI.kt @@ -0,0 +1,259 @@ +package com.example.cmake_project_test + +import android.util.Log +import java.nio.ByteBuffer +import java.nio.ByteOrder + +/** + * 信号处理JNI接口类 + * 提供C++信号处理功能的Java封装 + */ +class SignalProcessorJNI { + + companion object { + // 加载原生库 + init { + System.loadLibrary("cmake_project_test") + } + } + + private var processorId: Long = -1 + + /** + * 创建信号处理器实例 + */ + fun createProcessor(): Boolean { + processorId = createSignalProcessor() + return processorId != -1L + } + + /** + * 销毁信号处理器实例 + */ + fun destroyProcessor() { + if (processorId != -1L) { + destroySignalProcessor(processorId) + processorId = -1L + } + } + + /** + * 带通滤波 + * @param signal 输入信号数据 + * @param sampleRate 采样率 + * @param lowFreq 低频截止频率 + * @param highFreq 高频截止频率 + * @return 滤波后的信号数据 + */ + fun bandpassFilter(signal: FloatArray, sampleRate: Double, lowFreq: Double, highFreq: Double): FloatArray? { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return null + } + + val signalBytes = floatArrayToByteArray(signal) + val resultBytes = bandpassFilter(processorId, signalBytes, sampleRate, lowFreq, highFreq) + return resultBytes?.let { byteArrayToFloatArray(it) } + } + + /** + * 低通滤波 + * @param signal 输入信号数据 + * @param sampleRate 采样率 + * @param cutoffFreq 截止频率 + * @return 滤波后的信号数据 + */ + fun lowpassFilter(signal: FloatArray, sampleRate: Double, cutoffFreq: Double): FloatArray? { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return null + } + + val signalBytes = floatArrayToByteArray(signal) + val resultBytes = lowpassFilter(processorId, signalBytes, sampleRate, cutoffFreq) + return resultBytes?.let { byteArrayToFloatArray(it) } + } + + /** + * 高通滤波 + * @param signal 输入信号数据 + * @param sampleRate 采样率 + * @param cutoffFreq 截止频率 + * @return 滤波后的信号数据 + */ + fun highpassFilter(signal: FloatArray, sampleRate: Double, cutoffFreq: Double): FloatArray? { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return null + } + + val signalBytes = floatArrayToByteArray(signal) + val resultBytes = highpassFilter(processorId, signalBytes, sampleRate, cutoffFreq) + return resultBytes?.let { byteArrayToFloatArray(it) } + } + + /** + * 陷波滤波 + * @param signal 输入信号数据 + * @param sampleRate 采样率 + * @param notchFreq 陷波频率 + * @param qFactor 品质因数 + * @return 滤波后的信号数据 + */ + fun notchFilter(signal: FloatArray, sampleRate: Double, notchFreq: Double, qFactor: Double = 30.0): FloatArray? { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return null + } + + val signalBytes = floatArrayToByteArray(signal) + val resultBytes = notchFilter(processorId, signalBytes, sampleRate, notchFreq, qFactor) + return resultBytes?.let { byteArrayToFloatArray(it) } + } + + /** + * 计算信号质量 + * @param signal 输入信号数据 + * @return 信号质量指数 (0.0 - 1.0) + */ + fun calculateSignalQuality(signal: FloatArray): Float { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return 0.0f + } + + val signalBytes = floatArrayToByteArray(signal) + return calculateSignalQuality(processorId, signalBytes) + } + + /** + * 计算ECG信号质量指数 + * @param signal 输入信号数据 + * @param sampleRate 采样率 + * @return ECG信号质量指数 (0.0 - 1.0) + */ + fun calculateECGSQI(signal: FloatArray, sampleRate: Double): Float { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return 0.0f + } + + val signalBytes = floatArrayToByteArray(signal) + return calculateECGSQI(processorId, signalBytes, sampleRate) + } + + /** + * 计算两个信号的相关性 + * @param x 第一个信号 + * @param y 第二个信号 + * @return 相关系数 (-1.0 - 1.0) + */ + fun calculateCorrelation(x: FloatArray, y: FloatArray): Float { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return 0.0f + } + + val xBytes = floatArrayToByteArray(x) + val yBytes = floatArrayToByteArray(y) + return calculateCorrelation(processorId, xBytes, yBytes) + } + + /** + * 归一化信号幅度 + * @param signal 输入信号数据(将被修改) + */ + fun normalizeAmplitude(signal: FloatArray) { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return + } + + val signalBytes = floatArrayToByteArray(signal) + normalizeAmplitude(processorId, signalBytes) + + // 将处理后的数据写回原数组 + val processedSignal = byteArrayToFloatArray(signalBytes) + processedSignal.copyInto(signal) + } + + /** + * 提取信号特征 + * @param signal 输入信号数据 + * @param sampleRate 采样率 + * @return 特征向量 + */ + fun extractFeatures(signal: FloatArray, sampleRate: Double): FloatArray? { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return null + } + + val signalBytes = floatArrayToByteArray(signal) + val resultBytes = extractFeatures(processorId, signalBytes, sampleRate) + return resultBytes?.let { byteArrayToFloatArray(it) } + } + + /** + * 重置滤波器状态 + */ + fun resetFilters() { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return + } + + resetFilters(processorId) + } + + /** + * 实时处理数据块 + * @param chunk 输入数据块 + * @param sampleRate 采样率 + * @return 处理后的数据块 + */ + fun processRealtimeChunk(chunk: FloatArray, sampleRate: Double): FloatArray? { + if (processorId == -1L) { + Log.e("SignalProcessorJNI", "Processor not initialized") + return null + } + + val chunkBytes = floatArrayToByteArray(chunk) + val resultBytes = processRealtimeChunk(processorId, chunkBytes, sampleRate) + return resultBytes?.let { byteArrayToFloatArray(it) } + } + + // 辅助方法:FloatArray转ByteArray + private fun floatArrayToByteArray(floatArray: FloatArray): ByteArray { + val buffer = ByteBuffer.allocate(floatArray.size * 4) + buffer.order(ByteOrder.LITTLE_ENDIAN) + floatArray.forEach { buffer.putFloat(it) } + return buffer.array() + } + + // 辅助方法:ByteArray转FloatArray + private fun byteArrayToFloatArray(byteArray: ByteArray): FloatArray { + val buffer = ByteBuffer.wrap(byteArray) + buffer.order(ByteOrder.LITTLE_ENDIAN) + val floatArray = FloatArray(byteArray.size / 4) + for (i in floatArray.indices) { + floatArray[i] = buffer.float + } + return floatArray + } + + // 原生方法声明 + private external fun createSignalProcessor(): Long + private external fun destroySignalProcessor(processorId: Long) + private external fun bandpassFilter(processorId: Long, signal: ByteArray, sampleRate: Double, lowFreq: Double, highFreq: Double): ByteArray? + private external fun lowpassFilter(processorId: Long, signal: ByteArray, sampleRate: Double, cutoffFreq: Double): ByteArray? + private external fun highpassFilter(processorId: Long, signal: ByteArray, sampleRate: Double, cutoffFreq: Double): ByteArray? + private external fun notchFilter(processorId: Long, signal: ByteArray, sampleRate: Double, notchFreq: Double, qFactor: Double): ByteArray? + private external fun calculateSignalQuality(processorId: Long, signal: ByteArray): Float + private external fun calculateECGSQI(processorId: Long, signal: ByteArray, sampleRate: Double): Float + private external fun calculateCorrelation(processorId: Long, x: ByteArray, y: ByteArray): Float + private external fun normalizeAmplitude(processorId: Long, signal: ByteArray) + private external fun extractFeatures(processorId: Long, signal: ByteArray, sampleRate: Double): ByteArray? + private external fun resetFilters(processorId: Long) + private external fun processRealtimeChunk(processorId: Long, chunk: ByteArray, sampleRate: Double): ByteArray? +} diff --git a/app/src/main/java/com/example/cmake_project_test/StreamingSignalProcessor.kt b/app/src/main/java/com/example/cmake_project_test/StreamingSignalProcessor.kt new file mode 100644 index 0000000..87dd351 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/StreamingSignalProcessor.kt @@ -0,0 +1,414 @@ +package com.example.cmake_project_test + +import android.util.Log +import type.SensorData +import java.util.* + +/** + * 流式信号处理器 + * 支持窗口式实时信号处理 + */ +class StreamingSignalProcessor { + + private val signalProcessor = SignalProcessorJNI() + private var processorId: Long = -1L + private var signalProcessorInitialized = false + + // 窗口参数 + private var windowSize = 14 // 窗口大小(样本数)- 适配ECG数据包大小 + private var overlapSize = 0 // 重叠大小(样本数) + private var stepSize = windowSize - overlapSize // 步长 + + // 数据缓冲区 + private val dataBuffer = mutableListOf() + private val processedData = mutableListOf() + private var lastProcessedIndex = 0 + + // 滤波器参数 + private var sampleRate = 50.0 // 默认采样率 + private var lowpassCutoff = 40.0 // 低通滤波截止频率 + private var notchFreq = 50.0 // 陷波滤波频率 + private var notchQ = 30.0 // 陷波滤波品质因数 + + // 设备采样率映射 + private val deviceSampleRates = mapOf( + // 0x4230: EEG, EOG - 250Hz + 0x4230 to 250.0, + // 0x4211: ECG1, ECG2, EMG1, EMG2 - 250Hz, BR_TEMPERATURE - 50Hz + 0x4211 to 250.0, // 主要采样率,温度单独处理 + // 0x4402: ECG - 250Hz + 0x4402 to 250.0, + // 0x4302: RED_DATA, IR_DATA - 50Hz + 0x4302 to 50.0, + // 0x1102: CH_SOUND - 8000Hz + 0x1102 to 8000.0 + ) + + /** + * 初始化信号处理器 + */ + fun initialize(): Boolean { + try { + signalProcessorInitialized = signalProcessor.createProcessor() + if (signalProcessorInitialized) { + processorId = 1L // 使用固定ID + Log.d("StreamingSignalProcessor", "流式信号处理器初始化成功") + } else { + Log.e("StreamingSignalProcessor", "流式信号处理器初始化失败") + } + return signalProcessorInitialized + } catch (e: Exception) { + Log.e("StreamingSignalProcessor", "信号处理器初始化异常: ${e.message}") + return false + } + } + + /** + * 处理新的数据流(使用默认参数) + * @param newData 新的数据样本 + * @return 处理后的数据样本 + */ + fun processStreamingData(newData: List): List { + return processStreamingDataWithParameters(newData, sampleRate, lowpassCutoff, notchFreq, notchQ) + } + + /** + * 处理新的数据流(使用指定参数) + * @param newData 新的数据样本 + * @param sampleRate 采样率 + * @param lowpassCutoff 低通滤波截止频率 + * @param notchFreq 陷波滤波频率 + * @param notchQ 陷波滤波品质因数 + * @return 处理后的数据样本 + */ + fun processStreamingDataWithParameters( + newData: List, + sampleRate: Double, + lowpassCutoff: Double, + notchFreq: Double, + notchQ: Double + ): List { + if (processorId == -1L) { + Log.w("StreamingSignalProcessor", "信号处理器未初始化") + return newData + } + + Log.d("StreamingSignalProcessor", "开始处理数据流,输入数据长度: ${newData.size}") + Log.d("StreamingSignalProcessor", "输入数据前3个值: ${newData.take(3).joinToString(", ")}") + Log.d("StreamingSignalProcessor", "使用参数 - 采样率: ${sampleRate}Hz, 低通: ${lowpassCutoff}Hz, 陷波: ${notchFreq}Hz") + + // 添加新数据到缓冲区 + dataBuffer.addAll(newData) + Log.d("StreamingSignalProcessor", "缓冲区大小: ${dataBuffer.size}, 窗口大小: $windowSize") + + val processedSamples = mutableListOf() + + // 当缓冲区有足够数据时进行窗口处理 + while (dataBuffer.size >= windowSize) { + // 提取当前窗口数据 + val windowData = dataBuffer.take(windowSize).toFloatArray() + Log.d("StreamingSignalProcessor", "提取窗口数据,长度: ${windowData.size}") + + // 应用信号处理 + val processedWindow = processWindowWithParameters(windowData, sampleRate, lowpassCutoff, notchFreq, notchQ) + Log.d("StreamingSignalProcessor", "窗口处理完成,结果长度: ${processedWindow.size}") + + // 只保留非重叠部分的结果 + val nonOverlapSize = stepSize + val nonOverlapData = processedWindow.take(nonOverlapSize) + processedSamples.addAll(nonOverlapData) + Log.d("StreamingSignalProcessor", "添加非重叠数据,长度: ${nonOverlapData.size}") + + // 移除已处理的数据(保留重叠部分) + repeat(stepSize) { + if (dataBuffer.isNotEmpty()) { + dataBuffer.removeAt(0) + } + } + } + + Log.d("StreamingSignalProcessor", "数据流处理完成,输出长度: ${processedSamples.size}") + if (processedSamples.isNotEmpty()) { + Log.d("StreamingSignalProcessor", "输出数据前3个值: ${processedSamples.take(3).joinToString(", ")}") + } + + return processedSamples + } + + /** + * 处理单个窗口的数据(使用默认参数) + */ + private fun processWindow(windowData: FloatArray): List { + return processWindowWithParameters(windowData, sampleRate, lowpassCutoff, notchFreq, notchQ) + } + + /** + * 处理单个窗口的数据(使用指定参数) + */ + private fun processWindowWithParameters( + windowData: FloatArray, + sampleRate: Double, + lowpassCutoff: Double, + notchFreq: Double, + notchQ: Double + ): List { + try { + Log.d("StreamingSignalProcessor", "开始处理窗口数据,数据长度: ${windowData.size}") + Log.d("StreamingSignalProcessor", "窗口数据前3个值: ${windowData.take(3).joinToString(", ")}") + Log.d("StreamingSignalProcessor", "使用参数 - 采样率: ${sampleRate}Hz, 低通: ${lowpassCutoff}Hz, 陷波: ${notchFreq}Hz") + + // 1. 低通滤波 + var filtered = signalProcessor.lowpassFilter(windowData, sampleRate, lowpassCutoff) + if (filtered == null) { + Log.w("StreamingSignalProcessor", "低通滤波失败,使用原始数据") + filtered = windowData + } else { + Log.d("StreamingSignalProcessor", "低通滤波成功,结果前3个值: ${filtered.take(3).joinToString(", ")}") + } + + // 2. 陷波滤波(去除工频干扰)- 只有当陷波频率大于0时才应用 + if (notchFreq > 0) { + val notchFiltered = signalProcessor.notchFilter(filtered, sampleRate, notchFreq, notchQ) + if (notchFiltered == null) { + Log.w("StreamingSignalProcessor", "陷波滤波失败,使用低通滤波结果") + Log.d("StreamingSignalProcessor", "返回低通滤波结果,前3个值: ${filtered.take(3).joinToString(", ")}") + return filtered.toList() + } else { + Log.d("StreamingSignalProcessor", "陷波滤波成功,结果前3个值: ${notchFiltered.take(3).joinToString(", ")}") + return notchFiltered.toList() + } + } else { + Log.d("StreamingSignalProcessor", "跳过陷波滤波(陷波频率为0),返回低通滤波结果") + return filtered.toList() + } + + } catch (e: Exception) { + Log.e("StreamingSignalProcessor", "窗口处理异常: ${e.message}") + return windowData.toList() + } + } + + /** + * 根据数据类型获取合适的采样率 + */ + private fun getSampleRateForDataType(dataType: type.SensorData.DataType): Double { + return when (dataType) { + type.SensorData.DataType.EEG -> 250.0 + type.SensorData.DataType.ECG_2LEAD, type.SensorData.DataType.ECG_12LEAD -> 250.0 + type.SensorData.DataType.PPG -> 50.0 + type.SensorData.DataType.STETHOSCOPE -> 8000.0 + type.SensorData.DataType.SNORE -> 8000.0 + type.SensorData.DataType.RESPIRATION -> 50.0 + else -> 50.0 // 默认采样率 + } + } + + /** + * 根据数据类型获取合适的滤波器参数 + */ + private fun getFilterParametersForDataType(dataType: type.SensorData.DataType): Triple { + return when (dataType) { + type.SensorData.DataType.EEG -> { + // EEG: 低通40Hz, 陷波50Hz + Triple(40.0, 50.0, 30.0) + } + type.SensorData.DataType.ECG_2LEAD, type.SensorData.DataType.ECG_12LEAD -> { + // ECG: 低通40Hz, 陷波50Hz + Triple(40.0, 50.0, 30.0) + } + type.SensorData.DataType.PPG -> { + // PPG: 低通10Hz, 陷波50Hz + Triple(10.0, 50.0, 30.0) + } + type.SensorData.DataType.MIT_BIH -> { + // MIT-BIH: 低通40Hz, 陷波50Hz + Triple(40.0, 50.0, 30.0) + } + type.SensorData.DataType.STETHOSCOPE -> { + // 听诊器: 低通4000Hz, 陷波50Hz + Triple(4000.0, 50.0, 30.0) + } + type.SensorData.DataType.SNORE -> { + // 鼾声: 低通4000Hz, 陷波50Hz + Triple(4000.0, 50.0, 30.0) + } + type.SensorData.DataType.RESPIRATION -> { + // 呼吸: 低通1Hz, 不陷波 + Triple(1.0, 0.0, 0.0) + } + else -> { + // 默认参数 + Triple(40.0, 50.0, 30.0) + } + } + } + + /** + * 处理传感器数据包 + */ + fun processSensorData(sensorData: SensorData): SensorData { + val channelData = sensorData.getChannelData() + if (channelData == null || channelData.isEmpty()) { + return sensorData + } + + try { + // 根据数据类型设置采样率和滤波器参数 + val dataType = sensorData.getDataType() + val sampleRate = getSampleRateForDataType(dataType) + val (lowpassCutoff, notchFreq, notchQ) = getFilterParametersForDataType(dataType) + + Log.d("StreamingSignalProcessor", "处理数据类型: $dataType, 采样率: ${sampleRate}Hz, 低通: ${lowpassCutoff}Hz, 陷波: ${notchFreq}Hz") + + // 处理每个通道 + val processedChannels = channelData.map { channel -> + val channelData = channel.toFloatArray() + val processedData = processStreamingDataWithParameters(channelData.toList(), sampleRate, lowpassCutoff, notchFreq, notchQ) + processedData.toFloatArray() + } + + // 创建处理后的传感器数据 + val processedSensorData = SensorData() + processedSensorData.setDataType(sensorData.getDataType()) + processedSensorData.setTimestamp(sensorData.getTimestamp()) + processedSensorData.setPacketSn(sensorData.getPacketSn()) + processedSensorData.setChannelData(processedChannels.map { it.toList() }) + + return processedSensorData + + } catch (e: Exception) { + Log.e("StreamingSignalProcessor", "处理传感器数据异常: ${e.message}") + return sensorData + } + } + + /** + * 批量处理传感器数据包 + */ + fun processSensorDataList(sensorDataList: List): List { + return sensorDataList.map { processSensorData(it) } + } + + /** + * 获取缓冲区状态 + */ + fun getBufferStatus(): Map { + return mapOf( + "buffer_size" to dataBuffer.size, + "window_size" to windowSize, + "overlap_size" to overlapSize, + "step_size" to stepSize + ) + } + + /** + * 清空缓冲区 + */ + fun clearBuffer() { + dataBuffer.clear() + processedData.clear() + lastProcessedIndex = 0 + Log.d("StreamingSignalProcessor", "缓冲区已清空") + } + + /** + * 设置窗口参数 + */ + fun setWindowParameters(windowSize: Int, overlapSize: Int) { + this.windowSize = windowSize + this.overlapSize = overlapSize + this.stepSize = windowSize - overlapSize + Log.d("StreamingSignalProcessor", "窗口参数已更新: 窗口大小=$windowSize, 重叠大小=$overlapSize") + } + + /** + * 设置滤波器参数 + */ + fun setFilterParameters(lowpassCutoff: Double, notchFreq: Double, notchQ: Double) { + this.lowpassCutoff = lowpassCutoff + this.notchFreq = notchFreq + this.notchQ = notchQ + Log.d("StreamingSignalProcessor", "滤波器参数已更新") + } + + /** + * 清理资源 + */ + fun cleanup() { + try { + if (processorId != -1L) { + signalProcessor.destroyProcessor() + processorId = -1L + } + clearBuffer() + Log.d("StreamingSignalProcessor", "流式信号处理器资源已清理") + } catch (e: Exception) { + Log.e("StreamingSignalProcessor", "清理资源时发生错误: ${e.message}") + } + } + + // 委托给SignalProcessorJNI的方法 + fun normalizeAmplitude(signal: FloatArray): FloatArray? { + if (signalProcessorInitialized) { + signalProcessor.normalizeAmplitude(signal) + return signal // normalizeAmplitude修改原数组,返回原数组 + } else { + Log.w("StreamingSignalProcessor", "信号处理器未初始化,返回原始信号") + return signal + } + } + + fun bandpassFilter(signal: FloatArray, sampleRate: Float, lowFreq: Float, highFreq: Float): FloatArray? { + return if (signalProcessorInitialized) { + signalProcessor.bandpassFilter(signal, sampleRate.toDouble(), lowFreq.toDouble(), highFreq.toDouble()) + } else { + Log.w("StreamingSignalProcessor", "信号处理器未初始化,返回原始信号") + signal + } + } + + fun highpassFilter(signal: FloatArray, sampleRate: Float, cutoffFreq: Float): FloatArray? { + return if (signalProcessorInitialized) { + signalProcessor.highpassFilter(signal, sampleRate.toDouble(), cutoffFreq.toDouble()) + } else { + Log.w("StreamingSignalProcessor", "信号处理器未初始化,返回原始信号") + signal + } + } + + fun lowpassFilter(signal: FloatArray, sampleRate: Float, cutoffFreq: Float): FloatArray? { + return if (signalProcessorInitialized) { + signalProcessor.lowpassFilter(signal, sampleRate.toDouble(), cutoffFreq.toDouble()) + } else { + Log.w("StreamingSignalProcessor", "信号处理器未初始化,返回原始信号") + signal + } + } + + fun notchFilter(signal: FloatArray, sampleRate: Float, notchFreq: Float): FloatArray? { + return if (signalProcessorInitialized) { + signalProcessor.notchFilter(signal, sampleRate.toDouble(), notchFreq.toDouble()) + } else { + Log.w("StreamingSignalProcessor", "信号处理器未初始化,返回原始信号") + signal + } + } + + fun processRealtimeChunk(signal: FloatArray, sampleRate: Float): FloatArray? { + return if (signalProcessorInitialized) { + signalProcessor.processRealtimeChunk(signal, sampleRate.toDouble()) + } else { + Log.w("StreamingSignalProcessor", "信号处理器未初始化,返回原始信号") + signal + } + } + + fun calculateSignalQuality(signal: FloatArray): Float { + return if (signalProcessorInitialized) { + signalProcessor.calculateSignalQuality(signal) + } else { + Log.w("StreamingSignalProcessor", "信号处理器未初始化,返回默认质量值") + 0.0f + } + } +} diff --git a/app/src/main/java/com/example/cmake_project_test/UiManager.kt b/app/src/main/java/com/example/cmake_project_test/UiManager.kt new file mode 100644 index 0000000..8baebd9 --- /dev/null +++ b/app/src/main/java/com/example/cmake_project_test/UiManager.kt @@ -0,0 +1,232 @@ +package com.example.cmake_project_test + +import android.util.Log +import type.SensorData +import java.util.concurrent.atomic.AtomicBoolean + +/** + * UI管理类 + * 负责UI更新、统计信息构建和显示逻辑 + */ +class UiManager { + + private val uiUpdatePending = AtomicBoolean(false) + private var lastUpdateTime = 0L + + /** + * 计划UI更新,避免频繁刷新 + */ + fun scheduleUiUpdate( + dataManager: DataManager, + updateCallback: () -> Unit + ) { + val currentTime = System.currentTimeMillis() + if (currentTime - lastUpdateTime >= Constants.UPDATE_INTERVAL && + uiUpdatePending.compareAndSet(false, true)) { + lastUpdateTime = currentTime + updateCallback() + uiUpdatePending.set(false) + } + } + + /** + * 构建统计信息字符串 + */ + fun buildStatisticsString(dataManager: DataManager): String { + return try { + val packetBuffer = dataManager.getPacketBuffer() + + buildString { + append("=== 设备数据概览 ===\n") + append("当前缓冲区: ${dataManager.getPacketBufferSize()} 个数据包\n") + append("总共解析: ${dataManager.getTotalPacketsParsed()} 个数据包\n") + append("总数据量: ${dataManager.getRawStreamSize()} 字节\n") + + if (packetBuffer.isNotEmpty()) { + // 获取设备类型信息 + val deviceTypes = packetBuffer.mapNotNull { it.dataType }.distinct() + val deviceInfo = deviceTypes.joinToString(", ") { DeviceTypeHelper.getDeviceName(it) } + append("设备类型: $deviceInfo\n") + } else { + append("设备类型: 无\n") + } + + append("\n") + } + } catch (e: Exception) { + Log.e("UiManager", "构建统计信息失败: ${e.message}", e) + "=== 设备数据概览 ===\n构建统计信息失败: ${e.message}\n\n" + } + } + + /** + * 构建完整的显示内容 + */ + fun buildDisplayContent(dataManager: DataManager): String { + return try { + val packetBuffer = dataManager.getPacketBuffer() + val processedPackets = dataManager.getProcessedPackets() + val calculatedMetrics = dataManager.getCalculatedMetrics() + + // 即使没有数据,也显示基本状态信息 + val stats = buildStatisticsString(dataManager) + + if (packetBuffer.isEmpty()) { + return stats + "\n\n暂无数据包详情\n\n正在加载数据文件,请稍候..." + } + + // 构建信号处理信息 + val signalProcessingInfo = buildSignalProcessingInfo(dataManager, processedPackets) + + // 构建指标计算信息 + val metricsInfo = buildMetricsInfo(calculatedMetrics) + + // 只显示最新的一些数据包详情 + val recentPackets = packetBuffer.takeLast(Constants.MAX_DISPLAY_PACKETS) + val has12Lead = recentPackets.any { it.dataType == SensorData.DataType.ECG_12LEAD } + + val channelDetails = if (has12Lead) { + DeviceTypeHelper.buildChannelDetails( + recentPackets, + maxPackets = Constants.MAX_DETAIL_PACKETS, + maxChannels = Constants.MAX_12LEAD_CHANNELS, + maxSamples = Constants.MAX_DISPLAY_SAMPLES + ) + } else { + DeviceTypeHelper.buildChannelDetails( + recentPackets, + maxPackets = Constants.MAX_DETAIL_PACKETS, + maxChannels = Constants.MAX_DISPLAY_CHANNELS, + maxSamples = Constants.MAX_DISPLAY_SAMPLES + ) + } + + stats + signalProcessingInfo + metricsInfo + channelDetails + } catch (e: Exception) { + Log.e("UiManager", "构建显示内容时发生错误: ${e.message}", e) + "构建显示内容时发生错误: ${e.message}\n\n请检查数据格式是否正确。" + } + } + + /** + * 构建信号处理信息 + */ + private fun buildSignalProcessingInfo(dataManager: DataManager, processedPackets: List): String { + return buildString { + append("\n=== 信号处理信息 ===\n") + + if (processedPackets.isEmpty()) { + append("暂无信号处理数据\n") + } else { + append("已处理数据包: ${processedPackets.size} 个\n") + + // 显示流式信号处理器状态 + val bufferStatus = dataManager.getStreamingSignalProcessorStatus() + if (bufferStatus.isNotEmpty()) { + append("\n流式处理器状态:\n") + bufferStatus.forEach { (key, value) -> + append("- $key: $value\n") + } + } + + // 显示最新处理的数据包信息 + val latestPacket = processedPackets.lastOrNull() + if (latestPacket != null) { + append("\n最新处理数据包:\n") + append("- 数据类型: ${DeviceTypeHelper.getDeviceName(latestPacket.getDataType())}\n") + append("- 时间戳: ${latestPacket.getTimestamp()}\n") + append("- 包序号: ${latestPacket.getPacketSn()}\n") + + val channelData = latestPacket.getChannelData() + if (channelData != null && channelData.isNotEmpty()) { + append("- 通道数: ${channelData.size}\n") + channelData.forEachIndexed { index, channel -> + if (channel != null) { + append("- 通道${index + 1}样本数: ${channel.size}\n") + } else { + append("- 通道${index + 1}: 空数据\n") + } + } + } + } + } + + append("\n") + } + } + + /** + * 构建指标计算信息 + */ + private fun buildMetricsInfo(calculatedMetrics: List>): String { + return buildString { + append("\n=== 指标计算信息 ===\n") + + if (calculatedMetrics.isEmpty()) { + append("暂无指标数据\n") + append("可能的原因:\n") + append("- 指标计算器未初始化\n") + append("- 数据处理流程未执行\n") + append("- 指标计算返回空结果\n") + } else { + append("已计算 ${calculatedMetrics.size} 个数据包的指标\n") + append("非空指标数: ${calculatedMetrics.count { it.isNotEmpty() }}\n") + + // 显示最新一个数据包的指标 + val latestMetrics = calculatedMetrics.lastOrNull() + if (latestMetrics != null && latestMetrics.isNotEmpty()) { + append("\n最新指标:\n") + for ((key, value) in latestMetrics) { + val formattedValue = when { + key.contains("rate") || key.contains("hr") -> String.format("%.1f bpm", value) + key.contains("spo2") -> String.format("%.1f%%", value) + key.contains("quality") -> String.format("%.2f", value) + key.contains("amplitude") || key.contains("offset") -> String.format("%.3f mV", value) + key.contains("width") -> String.format("%.1f ms", value) + key.contains("timestamp") || key.contains("packet_sn") || key.contains("data_type") -> String.format("%.0f", value) + else -> String.format("%.3f", value) + } + append("- $key: $formattedValue\n") + } + } else { + append("最新指标: 无数据\n") + } + + // 显示统计信息 + if (calculatedMetrics.size > 1) { + val allHeartRates = calculatedMetrics.mapNotNull { it["heart_rate"] }.filter { it > 0 } + val allQualities = calculatedMetrics.mapNotNull { it["signal_quality"] }.filter { it >= 0 } + + if (allHeartRates.isNotEmpty()) { + val avgHeartRate = allHeartRates.average() + append("\n平均心率: ${String.format("%.1f", avgHeartRate)} bpm\n") + } + + if (allQualities.isNotEmpty()) { + val avgQuality = allQualities.average() + append("平均信号质量: ${String.format("%.2f", avgQuality)}\n") + } + } + } + + append("\n") + } + } + + /** + * 更新UI显示内容 + */ + fun updateDisplay( + dataManager: DataManager, + updateTextCallback: (String) -> Unit + ) { + val displayContent = buildDisplayContent(dataManager) + updateTextCallback(displayContent) + + // 智能清理缓冲区 + dataManager.cleanupBuffer() + + // 添加调试信息 + Log.d("UiManager", "当前缓冲区数据包数量: ${dataManager.getPacketBufferSize()}, 总共解析: ${dataManager.getTotalPacketsParsed()}") + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 97e6cd4..55347ba 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,28 +6,7 @@ android:orientation="vertical" tools:context=".MainActivity"> - -