This commit is contained in:
ZhangJinLong 2025-09-02 15:51:47 +08:00
parent c02885dd51
commit a368ce9048
34 changed files with 8147 additions and 311 deletions

View File

@ -4,6 +4,14 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-09-02T03:30:46.971762700Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=ba2e16dd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

141
JNI_FIX_SUMMARY.md Normal file
View File

@ -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<SensorData>?
}
```
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<SensorData>?
```
#### 优势
- 保持原有的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. 享受重构后的代码结构优势

136
README_REFACTOR.md Normal file
View File

@ -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文档和示例代码

271
SIGNAL_PROCESSOR_README.md Normal file
View File

@ -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<float>` 处理数据
- 字节序使用小端序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的易用性。
建议在使用前先运行示例代码,熟悉各种功能的使用方法,然后根据具体需求进行定制化开发。

View File

@ -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 {

Binary file not shown.

View File

@ -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)

View File

@ -0,0 +1,58 @@
#ifndef _DATA_MAPPER_H
#define _DATA_MAPPER_H
#include "cpp/data_praser.h"
#include <jni.h>
#include <android/log.h>
#include <vector>
#include <variant>
#include <stdexcept>
#include <map>
// 数据映射器类
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<SensorData> convertJavaSensorDataListToVector(JNIEnv* env, jobject sensorDataList);
jobject convertSensorDataVectorToJavaList(JNIEnv* env, const std::vector<SensorData>& sensorDataVector);
SensorData convertJavaSensorDataToCpp(JNIEnv* env, jobject javaSensorData);
std::vector<float> convertJavaFloatListToVector(JNIEnv* env, jobject floatList);
#endif

View File

@ -87,6 +87,9 @@ SensorData parse_respiration(const uint8_t* data);
// 统一解析入口函数 - 支持多个包头和数据包格式
std::vector<SensorData> parse_device_data(const std::vector<uint8_t>& file_data);
// CRC16校验函数
uint16_t calculate_crc16(const uint8_t* data, size_t length);
// 工具函数:将数值转换为十六进制字符串
std::string to_hex_string(uint16_t value);

View File

@ -0,0 +1,74 @@
#ifndef _INDICATOR_CAL_H
#define _INDICATOR_CAL_H
#include "cpp/data_praser.h"
#include <jni.h>
#include <android/log.h>
#include <vector>
#include <map>
#include <string>
#include <memory>
// 指标计算器类
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<float>& ecg_signal);
float calculate_qrs_width(const std::vector<float>& ecg_signal, float sample_rate);
float calculate_st_offset(const std::vector<float>& ecg_signal, float sample_rate);
// PPG相关指标
float calculate_heart_rate_ppg(const std::vector<float>& 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<float>& ppg_signal);
float calculate_amplitude_ratio(const SensorData& ppg_data);
// HRV相关指标
float calculate_sdnn(const std::vector<float>& rr_intervals);
float calculate_rmssd(const std::vector<float>& rr_intervals);
float calculate_pnn50(const std::vector<float>& rr_intervals);
float calculate_triangular_index(const std::vector<float>& rr_intervals);
// 信号处理
std::vector<float> detect_r_peaks(const std::vector<float>& ecg_signal, float sample_rate);
std::vector<float> detect_pulse_peaks(const std::vector<float>& ppg_signal, float sample_rate);
float calculate_signal_quality(const std::vector<float>& signal);
// 综合指标计算
std::map<std::string, float> calculate_all_ecg_metrics(const SensorData& ecg_data, float sample_rate);
std::map<std::string, float> calculate_all_ppg_metrics(const SensorData& ppg_data, float sample_rate);
std::map<std::string, float> calculate_all_hrv_metrics(const std::vector<float>& rr_intervals);
// 完整数据处理流程
std::map<std::string, float> 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<std::string, float>& metrics);
std::vector<float> convertJavaFloatListToVector(JNIEnv* env, jobject floatList);
#endif

View File

@ -0,0 +1,179 @@
#ifndef _SIGNAL_PROCESSOR_H_
#define _SIGNAL_PROCESSOR_H_
#include "data_praser.h"
#include <variant>
#include <map>
#include <vector>
#include <string>
// 滤波类型
enum class filtertype
{
lowpass,highpass,notchpass,bandpass,bandstop
};
// 特征数据结构
struct ECGChannelFeatures {
std::vector<int> r_peaks; // R波位置
float sdnn = 0.0f; // RR间期标准差
float rmssd = 0.0f; // RR间期差值的均方根
};
struct FeatureSet {
std::vector<ECGChannelFeatures> 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<ECGIndicators> ecg_indicators;
std::vector<PPGIndicators> ppg_indicators;
std::vector<EEGIndicators> 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<SensorData> process_channel_based_filtering_simple(
const std::vector<SensorData>& data_packets);
// 特征提取
FeatureSet extract_signal_features(const SensorData& processed_data,
const std::vector<std::string>& features = {});
// 计算生理指标
IndicatorSet compute_physiological_indicators(const FeatureSet& features,
const std::vector<std::string>& 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<std::string, FilterConfig> filter_configs_;
// 滤波器状态
struct FilterState {
std::vector<double> x_history;
std::vector<double> y_history;
};
std::map<std::string, FilterState> filter_states_;
// 在SignalProcessor类中添加
public:
// 带通滤波器实现
std::vector<float> bandpass_filter(const std::vector<float>& input,
double sample_rate,
double low_cutoff,
double high_cutoff);
std::vector<float> Lowpass_filter(const std::vector<float>& input,
double sample_rate,
double low_cutoff) ;
std::vector<float> filter(const std::vector<float>& input,
double sample_rate,
double low_cutoff,
double high_cutoff,
filtertype type);
std::vector<float> Highpass_filter(const std::vector<float>& input,
double sample_rate,
double high_cutoff);
std::vector<float> compensate_motion_artifact(const std::vector<float>& ppg,
const std::vector<float>& motion);
std::vector<float> compensate_eog_artifact(const std::vector<float>& eeg,
const std::vector<float>& eog1,
const std::vector<float>& eog2) ;
// 添加自适应陷波滤波器声明
std::vector<float> adaptive_notch_filter(const std::vector<float>& input,
double sample_rate,
double target_freq,
double bandwidth) ;
std::vector<float> bandstop_filter(const std::vector<float>& input,
double sample_rate,
double low_cutoff,
double high_cutoff);
void normalize_amplitude(std::vector<float>& signal) ;
// 添加通用预处理辅助函数
std::vector<float> remove_dc_offset(const std::vector<float>& signal);
std::vector<float> apply_gain(const std::vector<float>& signal, float gain);
float calculate_correlation(const std::vector<float>& x, const std::vector<float>& y);
float calculate_snr(const std::vector<float>& signal);
float calculate_PPG_sqi(const std::vector<float>& red_channel,
const std::vector<float>& ir_channel);
float calculate_ecg_sqi(const std::vector<float>& signal, double sample_rate);
std::vector<float> remove_motion_artifacts(const std::vector<float>& signal, double sample_rate);
// 新增:通道级滤波辅助方法
private:
std::vector<std::vector<float>> apply_channel_filters(
const std::vector<std::vector<float>>& channels, DataType data_type);
std::vector<std::vector<float>> apply_eeg_filters(
const std::vector<std::vector<float>>& channels);
std::vector<std::vector<float>> apply_ecg_filters(
const std::vector<std::vector<float>>& channels);
std::vector<std::vector<float>> apply_ppg_filters(
const std::vector<std::vector<float>>& channels);
std::vector<std::vector<float>> apply_respiration_filters(
const std::vector<std::vector<float>>& channels);
std::vector<std::vector<float>> apply_snore_filters(
const std::vector<std::vector<float>>& channels);
std::vector<std::vector<float>> apply_stethoscope_filters(
const std::vector<std::vector<float>>& channels);
};
#endif

View File

@ -1,10 +1,15 @@
#include <jni.h>
#include <jni.h>
#include <string>
#include <android/log.h>
#include <memory>
#include <vector>
#include <map>
#include <cstring>
#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<StreamParser*>(handle);
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<StreamParser*>(handle);
if (!parser || !chunk) return;
if (!parser) return;
jsize len = env->GetArrayLength(chunk);
if (len <= 0) return;
@ -115,10 +126,314 @@ Java_com_example_cmake_1project_1test_MainActivity_streamParserDrainPackets(
JNIEnv* env,
jobject /* this */,
jlong handle) {
if (handle == 0) return nullptr; // 检查句柄
auto* parser = reinterpret_cast<StreamParser*>(handle);
if (!parser) return nullptr;
try {
std::vector<SensorData> 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<jlong, std::unique_ptr<SignalProcessor>> processor_instances;
static jlong next_processor_id = 1;
// 辅助函数将Java字节数组转换为C++向量
std::vector<float> 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<float> 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<float>& 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<SignalProcessor>();
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<float> signal_vec = java_byte_array_to_vector(env, signal);
std::vector<float> 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<float> signal_vec = java_byte_array_to_vector(env, signal);
std::vector<float> 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<float> signal_vec = java_byte_array_to_vector(env, signal);
std::vector<float> 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<float> signal_vec = java_byte_array_to_vector(env, signal);
// 使用自适应陷波滤波器
std::vector<float> 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<float> 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<float> 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<float> x_vec = java_byte_array_to_vector(env, x);
std::vector<float> 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<float> 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<float> 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<std::vector<float>>{signal_vec};
FeatureSet features = it->second->extract_signal_features(temp_data);
// 将特征转换为float数组简化处理
std::vector<float> 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<float> chunk_vec = java_byte_array_to_vector(env, chunk);
// 应用基本的预处理
std::vector<float> 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;
}
}
// 便捷方法:从整块数据按固定块大小模拟流式解析,返回解析到的所有包
@ -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<jlong>(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<Mapper*>(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<Mapper*>(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<Mapper*>(mapperHandle);
__android_log_print(ANDROID_LOG_INFO, "DataMapper", "Mapper指针获取成功");
// 将Java的List<SensorData>转换为C++的vector<SensorData>
std::vector<SensorData> cppSensorDataList = convertJavaSensorDataListToVector(env, sensorDataList);
__android_log_print(ANDROID_LOG_INFO, "DataMapper", "转换后的数据包数量: %zu", cppSensorDataList.size());
// 批量映射
std::vector<SensorData> 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<SensorData>转换回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<List<Float>>)
std::vector<std::vector<float>> channels;
channels.reserve(listSize);
for (int i = 0; i < listSize; i++) {
jobject channelList = env->CallObjectMethod(channelDataList, getMethod, i);
if (channelList != nullptr) {
std::vector<float> channel = convertJavaFloatListToVector(env, channelList);
channels.push_back(channel);
}
}
cppSensorData.channel_data = channels;
} else {
// 单通道数据 (List<Float>)
std::vector<float> channel = convertJavaFloatListToVector(env, channelDataList);
cppSensorData.channel_data = channel;
}
} else {
// 第一个元素为空,假设是单通道数据
std::vector<float> channel = convertJavaFloatListToVector(env, channelDataList);
cppSensorData.channel_data = channel;
}
} catch (...) {
// 如果转换失败,创建一个空的通道数据
std::vector<float> emptyChannel;
cppSensorData.channel_data = emptyChannel;
}
} else {
// 空列表,创建空的通道数据
std::vector<float> emptyChannel;
cppSensorData.channel_data = emptyChannel;
}
} else {
// 通道数据为空,创建空的通道数据
std::vector<float> 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<float>
std::vector<float> convertJavaFloatListToVector(JNIEnv* env, jobject floatList) {
std::vector<float> 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<SensorData>转换为C++的vector<SensorData>
std::vector<SensorData> convertJavaSensorDataListToVector(JNIEnv* env, jobject sensorDataList) {
std::vector<SensorData> 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<jlong>(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<MetricsCalculator*>(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<MetricsCalculator*>(calculatorHandle);
// 将Java的SensorData转换为C++的SensorData
SensorData cppSensorData = convertJavaSensorDataToCpp(env, sensorData);
// 执行完整的数据处理流程
std::map<std::string, float> 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<MetricsCalculator*>(calculatorHandle);
// 将Java的SensorData转换为C++的SensorData
SensorData cppSensorData = convertJavaSensorDataToCpp(env, sensorData);
// 计算ECG指标
std::map<std::string, float> 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<MetricsCalculator*>(calculatorHandle);
// 将Java的SensorData转换为C++的SensorData
SensorData cppSensorData = convertJavaSensorDataToCpp(env, sensorData);
// 计算PPG指标
std::map<std::string, float> 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<MetricsCalculator*>(calculatorHandle);
// 将Java的List<Float>转换为C++的vector<float>
std::vector<float> cppRrIntervals = convertJavaFloatListToVector(env, rrIntervals);
// 计算HRV指标
std::map<std::string, float> 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<string, float>转换为Java的Map<String, Float>
jobject convertMetricsMapToJava(JNIEnv* env, const std::map<std::string, float>& metrics) {
// 创建HashMap
jclass hashMapClass = env->FindClass("java/util/HashMap");
jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()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, "<init>", "(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;
}

View File

@ -0,0 +1,531 @@
#include "cpp/data_mapper.h"
#include <iostream>
#include <cstring>
#include <map>
#include <android/log.h>
// 构造函数
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<int>(data_mapped.data_type));
__android_log_print(ANDROID_LOG_INFO, "DataMapper", "DEBUG: 数据类型值 = %d", static_cast<int>(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<std::vector<std::vector<float>>>(data_mapped.channel_data)) {
auto& output_channels = std::get<std::vector<std::vector<float>>>(data_mapped.channel_data);
__android_log_print(ANDROID_LOG_INFO, "DataMapper", "映射后通道数量: %zu", output_channels.size());
} else if (std::holds_alternative<std::vector<float>>(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<std::vector<std::vector<float>>>(data.channel_data)) {
// 多通道格式
auto& input_channels = std::get<std::vector<std::vector<float>>>(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<std::vector<float>> output_channels;
output_channels.reserve(input_channels.size());
// 复制并应用校准系数 (0.318 μV/unit)
for (const auto& input_channel : input_channels) {
std::vector<float> 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<std::vector<float>>(data.channel_data)) {
// 单通道格式
auto& input_channel = std::get<std::vector<float>>(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<float> 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<std::vector<std::vector<float>>>(data.channel_data)) {
// 多通道格式
auto& input_channels = std::get<std::vector<std::vector<float>>>(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<std::vector<float>> output_channels;
output_channels.reserve(input_channels.size());
// 复制并应用校准系数 (0.5 mV/unit)
for (const auto& input_channel : input_channels) {
std::vector<float> 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<std::vector<float>>(data.channel_data)) {
// 单通道格式
auto& input_channel = std::get<std::vector<float>>(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<float> 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<int>(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<std::vector<std::vector<float>>>(data.channel_data)) {
__android_log_print(ANDROID_LOG_ERROR, "DataMapper", "错误通道数据格式不正确期望std::vector<std::vector<float>>");
__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<std::vector<std::vector<float>>>(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<std::vector<float>> 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<std::vector<std::vector<float>>>(processed.channel_data)) {
auto& final_channels = std::get<std::vector<std::vector<float>>>(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<std::vector<std::vector<float>>>(data.channel_data)) {
throw std::runtime_error("Invalid channel data format for PPG");
}
// 获取已解析的通道数据
auto& input_channels = std::get<std::vector<std::vector<float>>>(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<std::vector<float>> 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<std::vector<std::vector<float>>>(data.channel_data)) {
throw std::runtime_error("Invalid channel data format for Respiration");
}
// 获取已解析的通道数据
auto& input_channels = std::get<std::vector<std::vector<float>>>(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<std::vector<float>> 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<std::vector<float>>(data.channel_data)) {
throw std::runtime_error("Invalid channel data format for SNORE");
}
// 获取已解析的通道数据
auto& raw_samples = std::get<std::vector<float>>(data.channel_data);
// 检查输入数据是否为空
if (raw_samples.empty()) {
throw std::runtime_error("Input channel data for SNORE is empty");
}
// 应用校准系数 (0.146 mV/unit)
std::vector<float> 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<std::vector<std::vector<float>>>(data.channel_data)) {
throw std::runtime_error("Invalid channel data format for Stethoscope");
}
// 获取已解析的通道数据
auto& input_channels = std::get<std::vector<std::vector<float>>>(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<std::vector<float>> output(2);
output[0] = input_channels[0];
output[1] = input_channels[1];
// 更新处理后的通道数据
processed.channel_data = output;
return processed;
}
// End of data_mapper.cpp

View File

@ -2,6 +2,7 @@
#include <cstring> // 添加 memcpy 头文件
#include <sstream> // 添加 stringstream 头文件
#include <iomanip> // 添加 iomanip 头文件
#include <iostream> // 添加 iostream 头文件
#include <jni.h>
#include <android/log.h>
#include <map>
@ -316,7 +317,7 @@ SensorData parse_respiration(const uint8_t* data) {
// 统一解析入口函数 - 支持多个包头和数据包格式
std::vector<SensorData> parse_device_data(const std::vector<uint8_t>& 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<SensorData> parse_device_data(const std::vector<uint8_t>& file_data)
const uint8_t* end_ptr = ptr + file_size;
std::map<DataType, std::vector<SensorData>> grouped_data;
std::vector<SensorData> results;
while (ptr < end_ptr) {
// 检查是否有响应包头
bool has_response_header = false;
@ -339,9 +341,23 @@ std::vector<SensorData> parse_device_data(const std::vector<uint8_t>& 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<uint32_t>(ptr + 4);
std::cout << "实际数据点数: " << actual_points << std::endl;
// 读取CRC校验 (小端)
uint16_t crc = read_le<uint16_t>(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<SensorData> parse_device_data(const std::vector<uint8_t>& 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<SensorData> parse_device_data(const std::vector<uint8_t>& 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<float>> 和 vector<float>
if (auto* multiChannels = std::get_if<std::vector<std::vector<float>>>(&sensorData.channel_data)) {
// 处理多通道数据 (vector<vector<float>>)
__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<int>(sensorData.data_type));
jclass arrayListClass = env->FindClass("java/util/ArrayList");
jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "<init>", "()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<std::vector<float>>(&sensorData.channel_data)) {
// 处理单通道数据 (vector<float>) - 将其包装为单通道的多通道格式

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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<String, Float>? {
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<SensorData>, sampleRate: Float = 250.0f): List<Map<String, Float>> {
if (!isInitialized()) {
Log.w("CompletePipeline", "管道未初始化")
return emptyList()
}
Log.d("CompletePipeline", "开始批量处理 ${rawDataList.size} 个数据包...")
val results = mutableListOf<Map<String, Float>>()
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}")
}
}
}

View File

@ -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 // 模拟传输延迟(毫秒)- 减少延迟
}

View File

@ -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<SensorData>()
private var totalPacketsParsed = 0L
// 信号处理相关
private var streamingSignalProcessor: StreamingSignalProcessor? = null
private var streamingSignalProcessorInitialized = false
private val processedPackets = mutableListOf<SensorData>()
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<Map<String, Float>>()
private var latestMetrics: Map<String, Float> = emptyMap()
// 流式数据处理相关
private val channelBuffers = mutableMapOf<Int, MutableList<Float>>() // 通道号 -> 数据缓冲区
private val processedChannelBuffers = mutableMapOf<Int, MutableList<Float>>() // 处理后的通道数据
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<SensorData>) {
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<List<Float>>()
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<SensorData> = packetBuffer
fun getProcessedPackets(): List<SensorData> = processedPackets
fun getCalculatedMetrics(): List<Map<String, Float>> = calculatedMetrics
fun getLatestMetrics(): Map<String, Float> = latestMetrics
fun getChannelBuffersStatus(): Map<Int, Int> = channelBuffers.mapValues { it.value.size }
// 流式处理相关getter方法
fun getProcessedChannelBuffersStatus(): Map<Int, Int> = processedChannelBuffers.mapValues { it.value.size }
fun getTotalProcessedSamples(): Long = totalProcessedSamples
fun getCurrentDataType(): type.SensorData.DataType? = currentDataType
fun getProcessingStatus(): Map<String, Any> {
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<String, Int> {
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<SensorData>): List<SensorData> {
Log.d("DataManager", "开始应用信号处理,处理 ${packets.size} 个数据包")
ensureStreamingSignalProcessor()
if (!streamingSignalProcessorInitialized || streamingSignalProcessor == null) {
Log.w("DataManager", "流式信号处理器未初始化,跳过信号处理")
return packets
}
val processedPackets = mutableListOf<SensorData>()
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<List<Float>>()
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<Float>): List<Float> {
val signal = channel.toFloatArray()
try {
if (streamingSignalProcessor != null) {
// 1. 带通滤波 (1-40HzEEG主要频率范围)
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<Float>): List<Float> {
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<Float>): List<Float> {
val signal = channel.toFloatArray()
try {
if (streamingSignalProcessor != null) {
// 1. 低通滤波 (8HzPPG主要频率范围)
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}")
}
}
}

View File

@ -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<SensorData>): List<SensorData> {
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<SensorData>): List<SensorData>?
}

View File

@ -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<SensorData>,
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<String>()
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")
}
}

View File

@ -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<Byte>()
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
}
}
}

View File

@ -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)
}
}

View File

@ -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<String, Float>? {
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<String, Float>? {
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<String, Float>? {
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<Float>): Map<String, Float>? {
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<String, Float>?
/**
* 计算ECG指标
* @param calculatorHandle 计算器句柄
* @param sensorData 传感器数据
* @param sampleRate 采样率
* @return 计算的ECG指标映射null表示失败
*/
private external fun calculateECGMetrics(calculatorHandle: Long, sensorData: SensorData, sampleRate: Float): Map<String, Float>?
/**
* 计算PPG指标
* @param calculatorHandle 计算器句柄
* @param sensorData 传感器数据
* @param sampleRate 采样率
* @return 计算的PPG指标映射null表示失败
*/
private external fun calculatePPGMetrics(calculatorHandle: Long, sensorData: SensorData, sampleRate: Float): Map<String, Float>?
/**
* 计算HRV指标
* @param calculatorHandle 计算器句柄
* @param rrIntervals RR间期列表
* @return 计算的HRV指标映射null表示失败
*/
private external fun calculateHRVMetrics(calculatorHandle: Long, rrIntervals: List<Float>): Map<String, Float>?
}

View File

@ -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<SensorData>()
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)
// 初始化管理器
dataManager = DataManager(this) // 传入this作为NativeMethodCallback
uiManager = UiManager()
// 初始化UI
binding.sampleText.text = "正在初始化..."
binding.sampleText.text = "正在初始化...\n\n请稍候,正在加载数据文件..."
// 设置按钮点击事件
binding.btnReset.setOnClickListener {
resetData()
}
binding.btnReload.setOnClickListener {
reloadData()
}
// 移除按钮点击事件,只保留流式读取功能
// 在后台线程处理数据加载和解析
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)
fun onBleNotify(chunk: ByteArray) {
dataManager.onBleNotify(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更新
uiManager.scheduleUiUpdate(dataManager) {
uiManager.updateDisplay(dataManager) { text ->
binding.sampleText.text = text
}
}
}
// 使用缓冲的数据包更新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 个数据包")
}
// 添加调试信息:显示实际的数据包数量变化
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)
}
}
} catch (e: IOException) {
Log.e("MainActivity", "Error reading asset file", e)
null
}
}
/**
* 构建通道数据详情字符串
*/
private fun buildChannelDetails(data: List<SensorData>, maxPackets: Int = 3, maxChannels: Int = 2, maxSamples: Int = 10): String {
if (data.isEmpty()) {
return "无通道数据"
}
val details = mutableListOf<String>()
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<SensorData>?
external fun createStreamParser(): Long
external fun destroyStreamParser(handle: Long)
external fun streamParserAppend(handle: Long, chunk: ByteArray)
external fun streamParserDrainPackets(handle: Long): List<SensorData>?
external fun parseStreamFromBytes(data: ByteArray, chunkSize: Int): List<SensorData>?
/**
* 重置所有数据
*/
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<SensorData>?
}

View File

@ -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<SensorData>?
}

View File

@ -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<Float>()
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)
}
}
}

View File

@ -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", "信号处理器已销毁")
}
}

View File

@ -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?
}

View File

@ -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<Float>()
private val processedData = mutableListOf<Float>()
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<Float>): List<Float> {
return processStreamingDataWithParameters(newData, sampleRate, lowpassCutoff, notchFreq, notchQ)
}
/**
* 处理新的数据流使用指定参数
* @param newData 新的数据样本
* @param sampleRate 采样率
* @param lowpassCutoff 低通滤波截止频率
* @param notchFreq 陷波滤波频率
* @param notchQ 陷波滤波品质因数
* @return 处理后的数据样本
*/
fun processStreamingDataWithParameters(
newData: List<Float>,
sampleRate: Double,
lowpassCutoff: Double,
notchFreq: Double,
notchQ: Double
): List<Float> {
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<Float>()
// 当缓冲区有足够数据时进行窗口处理
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<Float> {
return processWindowWithParameters(windowData, sampleRate, lowpassCutoff, notchFreq, notchQ)
}
/**
* 处理单个窗口的数据使用指定参数
*/
private fun processWindowWithParameters(
windowData: FloatArray,
sampleRate: Double,
lowpassCutoff: Double,
notchFreq: Double,
notchQ: Double
): List<Float> {
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<Double, Double, Double> {
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<SensorData>): List<SensorData> {
return sensorDataList.map { processSensorData(it) }
}
/**
* 获取缓冲区状态
*/
fun getBufferStatus(): Map<String, Int> {
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
}
}
}

View File

@ -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<SensorData>): 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<Map<String, Float>>): 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()}")
}
}

View File

@ -6,28 +6,7 @@
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:gravity="center">
<Button
android:id="@+id/btn_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重置数据"
android:layout_marginEnd="8dp" />
<Button
android:id="@+id/btn_reload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重新加载"
android:layout_marginStart="8dp" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"

View File

@ -7,6 +7,8 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# Use Java 17 for Android Gradle Plugin
org.gradle.java.home=C:\\Program Files\\Android\\Android Studio\\jbr
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects