%% ICG特征点检测算法
% 样本周期分割
% === 周期分割优化(同步ECG周期边界)===
% === 信号质量评估 (兼容性修复) ===
try
% 尝试使用原生range函数
signalRange = range(app.icgData_diff);
catch
% 原生函数不可用时的手动计算
signalRange = max(app.icgData_diff) - min(app.icgData_diff);
end
% 改进幅度阈值逻辑
% baselineNoise = std(app.icgData_diff(app.icgData_diff < prctile(app.icgData_diff, 10)));
% if signalRange < 5 * baselineNoise % 动态阈值
% errordlg(sprintf('信号质量过低 (动态范围: %.4f, 基线噪声: %.4f)', ...
% signalRange, baselineNoise), '信号质量警告');
% return;
% end
% === 动态参数计算 ===
% 1. 自适应显著度阈值
noiseLevel = std(app.icgData_diff(1:min(100, end))); % 前100点噪声估计
prominenceThresh = max(noiseLevel * 3, 0.1 * signalRange);
switch app.DropDown_sele.Value
case 'ARM'
% 2. 智能最小峰值距离
defaultDistance = 0.5; % 默认值取最小高度
case 'FPGA'
defaultDistance = 0.7; % 默认值取最小高度
end
adaptiveDistance = 50; % 最小距离
% === 分级峰值检测策略 ===
[pks, locs] = findpeaks1(app, app.icgData_diff, defaultDistance, adaptiveDistance);
% 初级检测失败时启用备用方案
if numel(locs) < 2
% 方案1:降低显著度要求
[pks2, locs2] = findpeaks(...
app.icgData_diff, ...
'MinPeakDistance', adaptiveDistance * 0.7, ...
'MinPeakProminence', prominenceThresh * 0.5 ...
);
% 方案2:基于局部极大值检测
if numel(locs2) < 2
[~, locs3] = findpeaks(...
movmax(app.icgData_diff, 5) == app.icgData_diff, ...
'MinPeakDistance', adaptiveDistance ...
);
locs = locs3;
else
locs = locs2;
end
end
app.icg_C_points = locs; % 新增:保存检测到的ICG C点全局位置
fprintf('成功检测到%d个C峰\n', numel(locs));
% 使用ECG划分的周期数
numCycles = numel(app.ecgsegments);
% 计算整体平均心率(用于全局参数)
% if numCycles >= 1
% RR_intervals = diff(app.R_points(1:numCycles+1));
% meanRR = mean(RR_intervals);
% currentHR = 60 / (meanRR / app.fs);
% else
% currentHR = 60; % 默认值
% end
% 计算周期心率(使用ECG的R峰间隔)
% RR_interval = app.R_points(i+1) - app.R_points(i);
% app.segments(i).HR = 60 / (RR_interval / app.fs);
% app.MinBCDistance = round(0.08 * app.fs); % 最小BC间距80ms (固定值)
% app.MaxBCDistance = round(0.16 * currentHR/60 * app.fs); % 基于平均心率计算
% 周期处理
if numCycles < 1
app.segments = [];
else
app.segments = struct(...
'HR', 0, 'C_point', 0, 'C_value', 0, ...
'C2_point', 0, 'C2_value', 0,...
'B_point', 0, 'B_value', 0, 'X_point', 0, 'X_value', 0, ...
'BX_time', 0, 'startIdx', 0, 'endIdx', 0, ...
'valid', false, 'SV', 0, 'CO', 0, 'CI', 0, 'SVI', 0);
app.segments = repmat(app.segments, 1, numCycles);
for i = 2:numCycles
app.segments(i).valid = false;
% 关键修改:直接使用ECG周期边界
% 获取当前周期边界(注意偏移量可能导致越界)
segStart = max(1, app.ecgsegments(i).start_idx - 30); % 边界保护
segEnd = min(length(app.icgData_diff), app.ecgsegments(i).end_idx + 30); % 边界保护
% 验证信号段有效性
if segStart >= segEnd || (segEnd - segStart) < 50
fprintf('周期%d: 无效的信号段长度\n', i);
continue; % 跳过当前周期
end
% 存储周期信息
app.segments(i).startIdx = segStart ;
app.segments(i).endIdx = segEnd ;
segmentData = app.icgData_diff(segStart:segEnd);
segmentData_dz2dt = app.icgData_diff_dz2dt(segStart:segEnd); %% 二阶微分
% 特征点检测
% 特征点检测
try
%% 1. 检测C点(一阶微分最大值)并打印
% [c_local, c_value] = detectCpoint(app, segmentData);
% app.segments(i).C_point = segStart + c_local - 1;
% app.segments(i).C_value = c_value;
% fprintf('周期%d: C点检测结果 -> 局部位置: %d, 全局位置: %d, 幅值: %.2fV\n', ...
% i, c_local, app.segments(i).C_point, c_value);
% 在当前ECG周期内查找预存的ICG C点
idx_in_segment = find(app.icg_C_points >= segStart & app.icg_C_points <= segEnd);
if isempty(idx_in_segment)
fprintf('周期%d: 未找到C点,跳过特征点检测\n', i);
app.segments(i).valid = false;
continue; % 无C点时跳过该周期
else
% 处理多个C点:取最接近周期中心的点
if numel(idx_in_segment) > 1
segmentCenter = round((segStart + segEnd) / 2);
[~, minIdx] = min(abs(app.icg_C_points(idx_in_segment) - segmentCenter));
c_global = app.icg_C_points(idx_in_segment(minIdx));
else
c_global = app.icg_C_points(idx_in_segment(1));
end
c_local = c_global - segStart + 1;
c_value = app.icgData_diff(c_global);
app.segments(i).C_point = c_global;
app.segments(i).C_value = c_value;
fprintf('周期%d: C点使用预检测结果 -> 全局位置: %d, 幅值: %.2fV\n', i, c_global, c_value);
end
%% 2. 二阶微分处理(C2点检测)并打印
minPeakHeight = 0.1;
minPeakDistance = 20;
[pks2, locs2] = findpeaks(segmentData_dz2dt, ...
'MinPeakHeight', minPeakHeight, ...
'MinPeakDistance', minPeakDistance);
if isempty(locs2)
[c2_value, c2_local] = max(segmentData_dz2dt);
locs2 = c2_local;
pks2 = c2_value;
end
[~, idx] = min(abs(locs2 - c_local));
app.segments(i).C2_point = segStart + locs2(idx) - 1;
app.segments(i).C2_value = pks2(idx);
fprintf('周期%d: C2点检测结果 -> 局部位置: %d, 全局位置: %d, 幅值: %.2fV\n', ...
i, locs2(idx), app.segments(i).C2_point, pks2(idx));
%% 3. 检测B点并打印
% 检查C点有效性
if isempty(app.segments(i).C_point) || isnan(app.segments(i).C_value)
fprintf('周期%d: C点无效,跳过B点检测\n', i);
app.segments(i).B_point = NaN;
app.segments(i).B_value = NaN;
continue;
end
% 检查C2点有效性
if ~isfield(app.segments(i), 'C2_point') || isempty(locs2) || idx > numel(locs2)
fprintf('周期%d: C2点无效,使用单参数模式\n', i);
[b_local, b_value] = detectBpoint_simple(app, segmentData, app.segments(i).C_point - segStart + 1, app.segments(i).C_value);
else
% 标准调用(带C2点)
[b_local, b_value] = detectBpoint(app, segmentData, c_local, c_value, locs2(idx) ); % 确保局部位置
end
app.segments(i).B_point = segStart + b_local - 1;
app.segments(i).B_value = b_value;
fprintf('周期%d: B点检测结果 -> 局部位置: %d, 全局位置: %d, 幅值: %.2fV\n', i, b_local, app.segments(i).B_point, b_value);
%% 4. 检测X点并打印
[x_local, x_value] = detectXpoint(app, segmentData, c_local, app.segments(i).B_point, segStart, app.fs);
app.segments(i).X_point = segStart + x_local - 1;
app.segments(i).X_value = x_value;
fprintf('周期%d: X点检测结果 -> 局部位置: %d, 全局位置: %d, 幅值: %.2fV\n', i, x_local, app.segments(i).X_point, x_value);
%% 计算并打印BX时间
if app.segments(i).B_point < app.segments(i).X_point
app.segments(i).BX_time = (app.segments(i).X_point - app.segments(i).B_point) / app.fs;
fprintf('周期%d: BX时间 = %.3fs\n', i, app.segments(i).BX_time);
else
app.segments(i).BX_time = NaN;
fprintf('周期%d: B点位置超过X点,BX时间无效\n', i);
end
app.segments(i).valid = true;
fprintf('周期%d特征点检测成功!\n', i);
catch ME
fprintf('周期%d特征点检测失败: %s\n', i, ME.message);
app.segments(i).valid = false;
end
end
end
% === 结果输出 ===
validCount = sum([app.segments.valid]);
fprintf('成功处理%d/%d个周期:\n', validCount, numel(app.segments));
app.segments = app.segments; % 在计算segments的回调函数中执行
% 调用一次show_plotButtonPushed来绘制所有特征点
app.show_plotButtonPushed();
% 初始化累加变量
total_C_value = 0;
total_BX_time = 0;
total_C2_value = 0;
valid_count = 0;
for i = 1:numel(app.segments)
if app.segments(i).valid
% 打印当前周期信息
fprintf('周期%d: B@%d(%.2fV), C@%d(%.2fV), C2@%d(%.2fV), X@%d(%.2fV), BX=%.3fs\n',...
i, app.segments(i).B_point, app.segments(i).B_value,...
app.segments(i).C_point, app.segments(i).C_value,...
app.segments(i).C2_point, app.segments(i).C2_value,...
app.segments(i).X_point, app.segments(i).X_value,...
app.segments(i).BX_time);
% 累加有效周期的值
total_C_value = total_C_value + app.segments(i).C_value;
total_BX_time = total_BX_time + app.segments(i).BX_time;
total_C2_value = total_C2_value + app.segments(i).C2_value;
valid_count = valid_count + 1;
end
end
% 计算平均值(避免除零错误)
if valid_count > 0
app.avg_C_value = total_C_value / valid_count;
app.avg_BX_time = total_BX_time / valid_count;
app.avg_C2_value = total_C2_value / valid_count;
fprintf('\n==== 统计结果 ====\n');
fprintf('有效周期数: %d\n', valid_count);
fprintf('平均C值: %.2fV\n', app.avg_C_value);
fprintf('平均C2值: %.2fV\n', app.avg_C2_value);
fprintf('平均BX时间: %.3fs\n', app.avg_BX_time);
else
fprintf('\n警告: 未发现有效周期数据\n');
end
根据代码生成特征点识别与特征参数提取的处理流程