|
a |
|
b/preprocessOfApneaECG/BioSigKit/@BioSigKit/BioSigKit.m |
|
|
1 |
classdef BioSigKit < handle |
|
|
2 |
%% ============== BioSigKit ================== |
|
|
3 |
% BioSigKit is a set of useful signal processing tools that are either |
|
|
4 |
% developed by me personally or others in different fields of biosignal |
|
|
5 |
% processing. BioSigKit is a wrapper with a simple visual interface that |
|
|
6 |
% gathers this tools under a simple easy to use platform. This tool might |
|
|
7 |
% be only used for non-commercial, academic, research and learning |
|
|
8 |
% purposes. |
|
|
9 |
% |
|
|
10 |
%% ============== How to start =================== |
|
|
11 |
% example:: obj = BioSigKit(Sig, Fs, Gr) |
|
|
12 |
% Where::: |
|
|
13 |
% Sig: is the signal |
|
|
14 |
% Fs : Sample Rate |
|
|
15 |
% Gr : Flag for showing the interface or not |
|
|
16 |
% Then you can call any subroutine |
|
|
17 |
%% ====List Of Subroutines that you can call for QRS detection ========= |
|
|
18 |
% ------- Algorithm -------------------- How to Call --------------- |
|
|
19 |
% (1) Pan-Tompkins Algorithm : obj.PanTompkins |
|
|
20 |
% (2) Phase Space Reconstruction : obj.PhaseSpaceAlg |
|
|
21 |
% (3) RST State-Machine : obj.StateMachine |
|
|
22 |
% (4) Filter Bank: obj.FilterBankQRS |
|
|
23 |
% (5) MTEO qrstAlg: obj.MTEO_qrstAlg |
|
|
24 |
% (6) AMPD: obj.AMPD_PAlg |
|
|
25 |
%% ==== List of all subroutines for ACC, EMG and etc processing ======== |
|
|
26 |
% (7) Activity Detection Hilbert: obj.Env_hilbert |
|
|
27 |
% ---------------- Inputs ------------------ % |
|
|
28 |
% Smooth_window : Length of smoothing window in nr of sample |
|
|
29 |
% threshold_style : Set 0 for Automatic, Set 1 for Manual |
|
|
30 |
% DURATION : The number of samples for signal to be above |
|
|
31 |
% threshold to be considered active |
|
|
32 |
% ---------------- Output ------------------ % |
|
|
33 |
% alarm : Pattern of activities |
|
|
34 |
% ---------------- Demo -------------------- % |
|
|
35 |
% v = repmat([.1*ones(200,1);ones(100,1)],[10 1]); % generate true variance profile |
|
|
36 |
% obj.sig = sqrt(v).*randn(size(v)); |
|
|
37 |
% obj.Env_hilbert; |
|
|
38 |
%----------------- |
|
|
39 |
% (8) Comp Mobility and Complexity: obj.ComputeHjorthP |
|
|
40 |
%----------------- |
|
|
41 |
% (9) Posture detection 3 Chann ACC: obj.ACC_Act |
|
|
42 |
% ------------- Inputs ----------------------- % |
|
|
43 |
% obj.sig : 3 axis Accelerometer signal where, each row is an axis |
|
|
44 |
% and each column a sample (e.g. (3,:)) |
|
|
45 |
% obj.Fs : Sampling frequency of the Accelerometer |
|
|
46 |
% ------------- Output ----------------------- % |
|
|
47 |
% output : adaptively filtered ACC channels based on activity |
|
|
48 |
% state : activity level (0:steady,1:walking,2:joggin) |
|
|
49 |
% EE : Energy Expenditure over 1 min (or length sig) |
|
|
50 |
% F : Bandpass filter in Hz |
|
|
51 |
% SMA : Signal Magnitude area |
|
|
52 |
%------------------ |
|
|
53 |
% (10)PsuedoCorr template matching : obj.TemplateMatch |
|
|
54 |
% ------------------- Inputs --------------------- % |
|
|
55 |
% template : A template in the form of a vector, the length of |
|
|
56 |
% the template should be smaller than the signal. |
|
|
57 |
% lag : a lag in terms of nr of samples to move the template, |
|
|
58 |
% it should be smaller than the length of the template |
|
|
59 |
% ------------------- Outputs -------------------- % |
|
|
60 |
% PsC_s : template matching score in range [0,1]. |
|
|
61 |
% best_lag: the lag that gave the highest correlation score. |
|
|
62 |
%----------------- |
|
|
63 |
% (11)ECG derived respiration : obj.EDR_comp |
|
|
64 |
%----------------- |
|
|
65 |
% (12)ACC derived respiration : obj.ADR_comp |
|
|
66 |
%% ===== General Projective, linear and nonlinear filterings ============ %% |
|
|
67 |
% (13)Real-time neural PCA: obj.neural_pca |
|
|
68 |
% ----------------- Inputs ------------------- % |
|
|
69 |
% X : Multi-channel signal, each row represents a channel and |
|
|
70 |
% each column a sample. |
|
|
71 |
% nPCA: Number of PCAs to extract |
|
|
72 |
% nit: Number of iterations to go through the whole signal |
|
|
73 |
% T : Learning rate in range [0,1], default:0.9 |
|
|
74 |
% ---------------- Outputs ------------------- % |
|
|
75 |
% EigVec: Eigen vectors |
|
|
76 |
% PC : PCs |
|
|
77 |
% Eigval : Eigenvalues |
|
|
78 |
%----------------- |
|
|
79 |
% (14)Adaptive Filtering: obj.adaptive_filter |
|
|
80 |
% * RLS : Recursive Least Squares Filter |
|
|
81 |
% * ALE : Adaptive Line Enhancer (Delayed Filter) |
|
|
82 |
% * VLALE : Variable Leaky Adaptive Line Enhancer |
|
|
83 |
% * NLMS_ecg : Normalized Least Mean Squares filter for artficat |
|
|
84 |
% removal in ECG based on 3 channel Accelerometer recordings |
|
|
85 |
%-------------------- Inputs ---------------------------% |
|
|
86 |
% type: type of the filter (numeric): |
|
|
87 |
% (1) RLS : Recursive Least Squares Filter |
|
|
88 |
% (2) ALE : Adaptive Line Enhancer (Delayed Filter) |
|
|
89 |
% (3) VLALE : Variable Leaky Adaptive Line Enhancer |
|
|
90 |
% (4) NLMS_ecg : Normalized Least Mean Squares filter for |
|
|
91 |
% artficat removal in ECG based on 3 channel Accelerometer |
|
|
92 |
% recordings |
|
|
93 |
% ref: Reference signal: |
|
|
94 |
% * For RLS filter it is single channel (1*N) |
|
|
95 |
% * For NLMS_ECG filter it should be 3 Channel |
|
|
96 |
% Accelerometer (3*N) |
|
|
97 |
% obj.Sig: input signal to clean (single channel vector) |
|
|
98 |
% order : order of the filter (for VLALE and ALE also delay) |
|
|
99 |
% lambda : learning rate(0<= lambda <=1, usually close to 1) |
|
|
100 |
%-------------------- Outputs --------------------------% |
|
|
101 |
% output: cleaned signal |
|
|
102 |
% error_sig : error signal |
|
|
103 |
%----------------- |
|
|
104 |
% (15)Nonlinear phasespace filtering: obj.nonlinear_phase_filt |
|
|
105 |
%-------------------- Method ---------------------------% |
|
|
106 |
% Employs nonlinear phasespace filter to clean up the signal. This |
|
|
107 |
% method is very strong and even able to extract foetal ecg from |
|
|
108 |
% single channel maternal recordings. Please refer to examples of |
|
|
109 |
% BioSigKit for further details. |
|
|
110 |
%-------------------- Inputs ----------------------------% |
|
|
111 |
% sig : Signal to be analyzed (single channel) |
|
|
112 |
% t : Number of samples for computing delayed phase space (def: 1) |
|
|
113 |
% d : Embedding dimension to consider (def: 50) |
|
|
114 |
% m : dimension of null space (def: 49) |
|
|
115 |
% r : number of nearest neighbors to consider |
|
|
116 |
% (normally a large number def: length(sig)/4) |
|
|
117 |
% --------------------- Output ------------------------ % |
|
|
118 |
% output : Cleaned Signal |
|
|
119 |
% --------------------- example ----------------------- % |
|
|
120 |
% output = projective(foetal_ecg(:,2), 1, round(Fs/5), round(Fs/6.25), 1500); |
|
|
121 |
% (16)Teager-Keiser energy operator: obj.TK_comp |
|
|
122 |
|
|
|
123 |
%% ============== Licensce ========================================== %% |
|
|
124 |
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
|
125 |
% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
|
126 |
% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
|
127 |
% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
|
128 |
% OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
|
129 |
% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
|
|
130 |
% TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
|
131 |
% PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
|
132 |
% LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
|
133 |
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
|
134 |
% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
135 |
% Author : |
|
|
136 |
% Hooman Sedghamiz, Feb, 2018 |
|
|
137 |
% MSc. Biomedical Engineering, Linkoping University |
|
|
138 |
% Email : Hooman.sedghamiz@gmail.com |
|
|
139 |
%% ============== End Helper ================= %% |
|
|
140 |
%------------ Class Definition of BioSigKit Toolbox -------------- % |
|
|
141 |
properties(GetAccess = 'public', SetAccess = 'private') |
|
|
142 |
panel = []; % Main Visualization Panel |
|
|
143 |
SigView = []; % Signal View Axis |
|
|
144 |
FreqValHolder; % Holds SampFreq from interface |
|
|
145 |
Alg; % Alg type from interface |
|
|
146 |
Status; % Loader |
|
|
147 |
LoadedSig; % Loaded Sig |
|
|
148 |
path_alg = addpath(genpath(fileparts(fileparts(which(mfilename)))));% Path of Algorithm |
|
|
149 |
slashchar = char('/'*isunix + '\'*(~isunix)); % OS dependent / |
|
|
150 |
end |
|
|
151 |
%------------ Interface and private methods -------------% |
|
|
152 |
methods(Access = protected) |
|
|
153 |
BioSigKitPanel(obj); % Setup the interface |
|
|
154 |
end |
|
|
155 |
%------------ Properties for BioSigKit ------------------% |
|
|
156 |
properties(GetAccess = 'public', SetAccess = 'public') |
|
|
157 |
Fs; % Sample Freq |
|
|
158 |
Sig; % Signal |
|
|
159 |
Gr = 0; % Flag for showing the interface |
|
|
160 |
Results; % Struct holding results |
|
|
161 |
statsC; % Holding Stats |
|
|
162 |
PlotResult = 0; % Verbose results of each algorithm |
|
|
163 |
PhasePeriod = 0.020; % Phase Period Used for Phase Space |
|
|
164 |
ScalogramL; % Scalogram Length |
|
|
165 |
complexity; % Complexity based on Hjorth Param |
|
|
166 |
mobility; % mobility based on Hjorth |
|
|
167 |
ACC; % 3 channel ACC |
|
|
168 |
end |
|
|
169 |
%% ------------------ Methods ----------------------------- %% |
|
|
170 |
methods |
|
|
171 |
%% --------------------- Constructor -------------------------- %% |
|
|
172 |
function obj = BioSigKit(Sig, Fs, Gr) |
|
|
173 |
% --------- Check Inputs -------------% |
|
|
174 |
if nargin < 3 |
|
|
175 |
Gr = 0; |
|
|
176 |
if nargin < 2 |
|
|
177 |
Fs = 250; |
|
|
178 |
if nargin < 1 |
|
|
179 |
BioSigKitPanel(obj); |
|
|
180 |
return; |
|
|
181 |
end |
|
|
182 |
Gr = 1; |
|
|
183 |
end |
|
|
184 |
end |
|
|
185 |
%--------- Pack Necessary Parameters -------------% |
|
|
186 |
obj.Sig = Sig; |
|
|
187 |
obj.Gr = Gr; |
|
|
188 |
obj.Fs = Fs; |
|
|
189 |
obj.ScalogramL = ceil(length(Sig)/2) - 1; |
|
|
190 |
%--------- Create the interface of Flag is on ------------- % |
|
|
191 |
if obj.Gr |
|
|
192 |
%------ Initialize Figure------% |
|
|
193 |
BioSigKitPanel(obj); |
|
|
194 |
%------ Update Figure -----------% |
|
|
195 |
UpdateFig(obj); |
|
|
196 |
end |
|
|
197 |
end |
|
|
198 |
%% ----------------------Run Algorithms method-------------------- %% |
|
|
199 |
function RunAlg(obj) |
|
|
200 |
if isempty(obj.Sig) |
|
|
201 |
msgbox('First Load a Signal!'); |
|
|
202 |
return; |
|
|
203 |
end |
|
|
204 |
[~,ind] = min(size(obj.Sig)); |
|
|
205 |
if ind == 1 |
|
|
206 |
obj.Sig = obj.Sig(1,:); |
|
|
207 |
else |
|
|
208 |
obj.Sig = obj.Sig(:,1); |
|
|
209 |
end |
|
|
210 |
% ------------------ Set the Values --------------------------% |
|
|
211 |
obj.Fs = str2double(obj.FreqValHolder.String); |
|
|
212 |
% ------------------ Start Loader ----------------------------% |
|
|
213 |
obj.Status.start; |
|
|
214 |
obj.Status.setBusyText('Processing...'); |
|
|
215 |
pause(0.01); |
|
|
216 |
UpdateFig(obj); |
|
|
217 |
obj.Results = []; |
|
|
218 |
ResetStats(obj); |
|
|
219 |
% --------------- Run the appropriate method ------------------% |
|
|
220 |
try |
|
|
221 |
switch obj.Alg.Value |
|
|
222 |
% ----- Pan-Tompkins -------% |
|
|
223 |
case 1 |
|
|
224 |
PanTompkins(obj); |
|
|
225 |
% ----- Phase Space --------% |
|
|
226 |
case 2 |
|
|
227 |
PhaseSpaceAlg(obj); |
|
|
228 |
% ----- RST State-Machine ------% |
|
|
229 |
case 3 |
|
|
230 |
StateMachine(obj); |
|
|
231 |
% ----- QRS Filter-Bank -------- % |
|
|
232 |
case 4 |
|
|
233 |
FilterBankQRS(obj); |
|
|
234 |
% ---------- QRST MTEO ------------ % |
|
|
235 |
case 5 |
|
|
236 |
MTEO_qrstAlg(obj); |
|
|
237 |
%----------- AMPD Peak Detector --------% |
|
|
238 |
case 6 |
|
|
239 |
AMPD_PAlg(obj); |
|
|
240 |
otherwise |
|
|
241 |
return; |
|
|
242 |
end |
|
|
243 |
% -------------------- Update Figure --------------------- % |
|
|
244 |
visualizeResults(obj); |
|
|
245 |
catch ME |
|
|
246 |
msgbox(ME.message); |
|
|
247 |
end |
|
|
248 |
% ------------------- Update Loader ---------------------- % |
|
|
249 |
obj.Status.setBusyText('Done!'); |
|
|
250 |
pause(1); |
|
|
251 |
obj.Status.stop; |
|
|
252 |
end |
|
|
253 |
%% ---------------------- Import Sig ------------------------------ %% |
|
|
254 |
function ImportSig(obj) |
|
|
255 |
[FileName,Path] = uigetfile(fullfile(pwd,... |
|
|
256 |
'*.mat'),... |
|
|
257 |
'Select Your Signal'); |
|
|
258 |
if ~FileName |
|
|
259 |
return; |
|
|
260 |
end |
|
|
261 |
data = load([Path,obj.slashchar,FileName]); |
|
|
262 |
FN = fields(data); |
|
|
263 |
if ~isempty(FN) |
|
|
264 |
obj.Sig = data.(FN{1}); |
|
|
265 |
else |
|
|
266 |
msgbox('Bad Input Signal! Input should be a vector!'); |
|
|
267 |
return; |
|
|
268 |
end |
|
|
269 |
if ~isempty(obj.LoadedSig) |
|
|
270 |
obj.LoadedSig.String = [Path,FileName]; |
|
|
271 |
% ----------------- Update Figure ------------------ % |
|
|
272 |
UpdateFig(obj); |
|
|
273 |
obj.Results = []; |
|
|
274 |
end |
|
|
275 |
|
|
|
276 |
end |
|
|
277 |
%% ================= Phase Space ===================== %% |
|
|
278 |
%----- Employs Phase Space for QRS detection ------ % |
|
|
279 |
% Call : obj.PhaseSpaceAlg OR PhaseSpaceAlg(obj) |
|
|
280 |
% Outputs : |
|
|
281 |
% R : R Beats, |
|
|
282 |
% V : Processed area computed from Phase Space |
|
|
283 |
function [R,V] = PhaseSpaceAlg(obj) |
|
|
284 |
obj.Results = []; |
|
|
285 |
[R,V] = PhaseSpaceQRS(obj.Sig,obj.Fs,obj.PhasePeriod,... |
|
|
286 |
obj.PlotResult); |
|
|
287 |
obj.Results.R(1,:) = R(:,1); |
|
|
288 |
end |
|
|
289 |
%% ================= MTEO QRST Delineation ============== %% |
|
|
290 |
%------ Emlpoys MultiValued MTEO to delineate ECG---------- % |
|
|
291 |
% Call : obj.MTEO_qrstAlg OR MTEO_qrstAlg(obj) |
|
|
292 |
% Outputs: Delineated Waves (QRSTP) |
|
|
293 |
function [R,Q,S,T,P_w] = MTEO_qrstAlg(obj) |
|
|
294 |
obj.Results = []; |
|
|
295 |
[R,Q,S,T,P_w] = MTEO_qrst(obj.Sig,obj.Fs,obj.PlotResult); |
|
|
296 |
obj.Results.R(1,:) = R(:,1); |
|
|
297 |
obj.Results.Q(1,:) = Q(:,1); |
|
|
298 |
obj.Results.S(1,:) = S(:,1); |
|
|
299 |
obj.Results.T(1,:) = T(:,1); |
|
|
300 |
obj.Results.P(1,:) = P_w(:,1); |
|
|
301 |
end |
|
|
302 |
%% =================== AMPD Peak Detection ================== %% |
|
|
303 |
% ------------- Employs AMPD Algorithm for Peak detection -----------% |
|
|
304 |
% Inputs : |
|
|
305 |
% Sig |
|
|
306 |
% L : Max sample number for Scalogram computation(Leave free if have not idea) |
|
|
307 |
function R = AMPD_PAlg(obj) |
|
|
308 |
obj.Results = []; |
|
|
309 |
if isempty(obj.ScalogramL) |
|
|
310 |
obj.ScalogramL = ceil(length(obj.Sig)/2) - 1; |
|
|
311 |
end |
|
|
312 |
try |
|
|
313 |
R = AMPD_P(obj.Sig,obj.ScalogramL,obj.PlotResult); |
|
|
314 |
obj.Results.R(1,:) = R(:,1); |
|
|
315 |
catch ME |
|
|
316 |
msgbox(ME.message); |
|
|
317 |
end |
|
|
318 |
|
|
|
319 |
end |
|
|
320 |
%% ================== Filter Bank QRS Detector ================== %% |
|
|
321 |
function R = FilterBankQRS(obj) |
|
|
322 |
obj.Results = []; |
|
|
323 |
R = nqrsdetect(obj.Sig,obj.Fs); |
|
|
324 |
obj.Results.R(1,:) = R(:); |
|
|
325 |
end |
|
|
326 |
%% ==================== Pan-Tompkins QRS Detector ================ %% |
|
|
327 |
function [R_amp,R_ind] = PanTompkins(obj) |
|
|
328 |
obj.Results = []; |
|
|
329 |
[R_amp,R_ind]=pan_tompkin(obj.Sig,obj.Fs,obj.PlotResult); |
|
|
330 |
obj.Results.R(1,:) = R_ind(:); |
|
|
331 |
end |
|
|
332 |
%% ===================== Simple State-Machine ===================== %% |
|
|
333 |
% Outputs : R_i : Index of R peaks, R_a : Amplitude |
|
|
334 |
function [R_i,R_a,S_i,S_a,T_i,T_a,Q_i,Q_a] = StateMachine(obj) |
|
|
335 |
[R_i,R_a,S_i,S_a,T_i,T_a,Q_i,Q_a] = SimpleRST(obj.Sig,obj.Fs,... |
|
|
336 |
obj.PlotResult); |
|
|
337 |
obj.Results.R(1,:) = R_i; |
|
|
338 |
obj.Results.Q(1,:) = Q_i; |
|
|
339 |
obj.Results.S(1,:) = S_i; |
|
|
340 |
obj.Results.T(1,:) = T_i; |
|
|
341 |
end |
|
|
342 |
%% ===============Update the plot in interface ========== %% |
|
|
343 |
function visualizeResults(obj) |
|
|
344 |
% --------------- First the Figure ------------------% |
|
|
345 |
if ~isempty(obj.Results) |
|
|
346 |
PP = fields(obj.Results); |
|
|
347 |
Index = [5,10,15;3,8,13;2,7,12;1,6,11;4,9,14]; |
|
|
348 |
colors = distinguishable_colors(length(PP),{'k','y'}); |
|
|
349 |
for i= 1: length(PP) |
|
|
350 |
% ----------- Update Signal ------------------ % |
|
|
351 |
line(repmat(obj.Results.(PP{i})(1,:),[2 1]),... |
|
|
352 |
repmat([min(obj.Sig-mean(obj.Sig))/2; max(obj.Sig-mean(obj.Sig))/2],... |
|
|
353 |
size(obj.Results.(PP{i})(1,:))),'LineWidth',1.5,... |
|
|
354 |
'LineStyle','-.','color',colors(i,:),'Parent',obj.SigView); |
|
|
355 |
% ----------- Update Stats -------------------- % |
|
|
356 |
intervals = round((diff(obj.Results.(PP{i})(1,:)))./obj.Fs,3); |
|
|
357 |
obj.statsC.Children(Index(i,1)).String = mat2str(max(intervals)); |
|
|
358 |
obj.statsC.Children(Index(i,2)).String = mat2str(round(mean(intervals),3)); |
|
|
359 |
obj.statsC.Children(Index(i,3)).String = mat2str(length(obj.Results.(PP{i})(1,:))); |
|
|
360 |
end |
|
|
361 |
end |
|
|
362 |
end |
|
|
363 |
%% ================= Update VisualInterface Axis ================= %% |
|
|
364 |
function UpdateFig(obj) |
|
|
365 |
cla(obj.SigView); |
|
|
366 |
plot(obj.Sig - mean(obj.Sig),'LineWidth',2,'Parent',obj.SigView,'color','yellow'); |
|
|
367 |
set(obj.SigView,'XGrid','on',... |
|
|
368 |
'YGrid','on','XMinorGrid','on','YMinorGrid','on',... |
|
|
369 |
'Color',[0,0,0],'YColor',[1,1,1],'XColor',[1,1,1]); |
|
|
370 |
axis(obj.SigView,'tight'); |
|
|
371 |
end |
|
|
372 |
%% ================ Update Stats ================================== %% |
|
|
373 |
function ResetStats(obj) |
|
|
374 |
for j = 1: 15 |
|
|
375 |
obj.statsC.Children(j).String = '\bf --'; |
|
|
376 |
end |
|
|
377 |
end |
|
|
378 |
end |
|
|
379 |
%% ============ Experimental Methods not in GUI Yet ================== %% |
|
|
380 |
methods |
|
|
381 |
%% ------ Use Hilber Tr to detect Activity in signals --------- %% |
|
|
382 |
function alarm = Env_hilbert(obj,Smooth_window,threshold_style,... |
|
|
383 |
DURATION) |
|
|
384 |
% ---------------- Inputs ------------------ % |
|
|
385 |
% Smooth_window : Length of smoothing window in nr of sample |
|
|
386 |
% threshold_style : Set 0 for Automatic, Set 1 for Manual |
|
|
387 |
% DURATION : The number of samples for signal to be above |
|
|
388 |
% threshold to be considered active |
|
|
389 |
% ---------------- Output ------------------ % |
|
|
390 |
% alarm : Pattern of activities |
|
|
391 |
% ---------------- Demo -------------------- % |
|
|
392 |
% v = repmat([.1*ones(200,1);ones(100,1)],[10 1]); % generate true variance profile |
|
|
393 |
% obj.sig = sqrt(v).*randn(size(v)); |
|
|
394 |
% obj.Env_hilbert; |
|
|
395 |
% ---------------- Input Handling ------------------% |
|
|
396 |
if nargin < 4 |
|
|
397 |
DURATION = 20; % default |
|
|
398 |
if nargin < 3 |
|
|
399 |
threshold_style = 1; % default 1 , means it is done automatic |
|
|
400 |
if nargin < 2 |
|
|
401 |
Smooth_window = 20; % default for smoothing length |
|
|
402 |
end |
|
|
403 |
end |
|
|
404 |
end |
|
|
405 |
alarm = envelop_hilbert(obj.Sig,... |
|
|
406 |
Smooth_window,... |
|
|
407 |
threshold_style,DURATION,obj.PlotResult); |
|
|
408 |
end |
|
|
409 |
%% ------ Employ Hjorth Parameters to analyze a signal --------- %% |
|
|
410 |
function [mobility,complexity] = ComputeHjorthP(obj) |
|
|
411 |
% ---------------- Hjorth Parameters ------------------- % |
|
|
412 |
% ---------------- Output ----------------------% |
|
|
413 |
% mobility : Central frequency |
|
|
414 |
% complexity : bandwith of the signal |
|
|
415 |
[mobility,complexity] = simpleHjorth(obj.Sig,obj.Fs); |
|
|
416 |
obj.mobility = mobility; |
|
|
417 |
obj.complexity = complexity; |
|
|
418 |
end |
|
|
419 |
%% ------ Activity Detection in ACC signals ---------------- %% |
|
|
420 |
function [output,state,EE,F,SMA] = ACC_Act(obj) |
|
|
421 |
% ------------- Inputs ----------------------- % |
|
|
422 |
% obj.sig : 3 axis Accelerometer signal where, each row is an axis |
|
|
423 |
% and each column a sample (e.g. (3,:)) |
|
|
424 |
% obj.Fs : Sampling frequency of the Accelerometer |
|
|
425 |
% ------------- Output ----------------------- % |
|
|
426 |
% output : adaptively filtered ACC channels based on activity |
|
|
427 |
% state : activity level (0:steady,1:walking,2:joggin) |
|
|
428 |
% EE : Energy Expenditure over 1 min (or length sig) |
|
|
429 |
% F : Bandpass filter in Hz |
|
|
430 |
% SMA : Signal Magnitude area |
|
|
431 |
if isempty(obj.Sig) |
|
|
432 |
error('Please first load your ACC to obj.ACC!'); |
|
|
433 |
end |
|
|
434 |
[output,state,EE,F,SMA] = ACC_Activity(obj.Sig,obj.Fs); |
|
|
435 |
end |
|
|
436 |
%% ------------- Template Matching with Psuedo Corr--------------------- %% |
|
|
437 |
function [PsC_s,best_lag] = TemplateMatch(obj,template,lag) |
|
|
438 |
% ------------------- Inputs --------------------- % |
|
|
439 |
% template : A template in the form of a vector, the length of |
|
|
440 |
% the template should be smaller than the signal. |
|
|
441 |
% lag : a lag in terms of nr of samples to move the template, |
|
|
442 |
% it should be smaller than the length of the template |
|
|
443 |
% ------------------- Outputs -------------------- % |
|
|
444 |
% PsC_s : template matching score in range [0,1]. |
|
|
445 |
% best_lag: the lag that gave the highest correlation score. |
|
|
446 |
if nargin < 3 |
|
|
447 |
[PsC_s,best_lag] = PsC(template,obj.Sig); |
|
|
448 |
else |
|
|
449 |
[PsC_s,best_lag] = PsC(template,obj.Sig,lag); |
|
|
450 |
end |
|
|
451 |
|
|
|
452 |
end |
|
|
453 |
%% ---------------- Real time Neural PCA Filter ----------------- %% |
|
|
454 |
function [EigVec,PC,Eigval] = neural_pca(obj,nPCA,nit) |
|
|
455 |
% ----------------- Inputs ------------------- % |
|
|
456 |
% X : Multi-channel signal, each row represents a channel and |
|
|
457 |
% each column a sample. |
|
|
458 |
% nPCA: Number of PCAs to extract |
|
|
459 |
% nit: Number of iterations to go through the whole signal |
|
|
460 |
% T : Learning rate in range [0,1], default:0.9 |
|
|
461 |
% ---------------- Outputs ------------------- % |
|
|
462 |
% EigVec: Eigen vectors |
|
|
463 |
% PC : PCs |
|
|
464 |
% Eigval : Eigenvalues |
|
|
465 |
% ---------------- Check Inputs -------------- % |
|
|
466 |
if size(obj.Sig,1) < 2 |
|
|
467 |
fprintf('|#| BioSigKit> Input signals should be channles*N.\n'); |
|
|
468 |
end |
|
|
469 |
[EigVec,PC,Eigval] = RTpca(obj.Sig,nPCA,nit); |
|
|
470 |
end |
|
|
471 |
%% ----------------- Adaptive Filters --------------------- %% |
|
|
472 |
function [output,error_sig] = adaptive_filter(obj,type,ref,... |
|
|
473 |
order,lamda) |
|
|
474 |
%-------------------- Inputs ---------------------------% |
|
|
475 |
% type: type of the filter (numeric): |
|
|
476 |
% (1) RLS : Recursive Least Squares Filter |
|
|
477 |
% (2) ALE : Adaptive Line Enhancer (Delayed Filter) |
|
|
478 |
% (3) VLALE : Variable Leaky Adaptive Line Enhancer |
|
|
479 |
% (4) NLMS_ecg : Normalized Least Mean Squares filter for |
|
|
480 |
% artficat removal in ECG based on 3 channel Accelerometer |
|
|
481 |
% recordings |
|
|
482 |
% ref: Reference signal: |
|
|
483 |
% * For RLS filter it is single channel (1*N) |
|
|
484 |
% * For NLMS_ECG filter it should be 3 Channel |
|
|
485 |
% Accelerometer (3*N) |
|
|
486 |
% obj.Sig: input signal to clean (single channel vector) |
|
|
487 |
% order : order of the filter (for VLALE and ALE also delay) |
|
|
488 |
% lambda : learning rate(0<= lambda <=1, usually close to 1) |
|
|
489 |
%-------------------- Outputs --------------------------% |
|
|
490 |
% output: cleaned signal |
|
|
491 |
% error_sig : error signal |
|
|
492 |
% ------------------- Process Inputs -------------------% |
|
|
493 |
output= []; |
|
|
494 |
error_sig = []; |
|
|
495 |
if nargin < 2 |
|
|
496 |
fprintf('|#| BioSigKit> Please select a filter type! (1-4).\n'); |
|
|
497 |
fprintf('|#| BioSigKit> Exiting.'); |
|
|
498 |
return; |
|
|
499 |
end |
|
|
500 |
if nargin < 5 |
|
|
501 |
lamda = 1; |
|
|
502 |
if nargin < 4 |
|
|
503 |
order = 1*obj.Fs; |
|
|
504 |
if nargin < 3 |
|
|
505 |
ref= []; |
|
|
506 |
end |
|
|
507 |
end |
|
|
508 |
end |
|
|
509 |
% ------------------- Run the Filters-------------------% |
|
|
510 |
switch type |
|
|
511 |
% ------ Recursive Least Squars (RLS) ------ % |
|
|
512 |
case 1 |
|
|
513 |
if isempty(ref) || size(ref,1) > 1 |
|
|
514 |
fprintf('|#| BioSigKit> Ref signal should be 1*N!\n'); |
|
|
515 |
return; |
|
|
516 |
end |
|
|
517 |
fprintf('|#| BioSigKit> Adaptive RLS Filter...\n'); |
|
|
518 |
[output,error_sig] = RLS(ref,obj.Sig,order,lamda); |
|
|
519 |
% ------ Adaptive Line Enhancer (ADLE) ------ % |
|
|
520 |
case 2 |
|
|
521 |
fprintf('|#| BioSigKit> Adaptive Delayed Line Enhancer...\n'); |
|
|
522 |
[output,error_sig] = ALE_imp(obj.Sig,obj.Fs,order); |
|
|
523 |
% ------ Variable Leaky ALE (VLALE) ------ % |
|
|
524 |
case 3 |
|
|
525 |
fprintf('|#| BioSigKit> Variable Leaky ALE Running...\n'); |
|
|
526 |
[output,error_sig] = VLALE_imp(obj.Sig,obj.Fs,order); |
|
|
527 |
% ------ ECG artifact removal with 3 channel ACC ------ % |
|
|
528 |
case 4 |
|
|
529 |
if isempty(ref) || size(ref,1) < 3 || size(ref,1) > 3 |
|
|
530 |
fprintf('|#| BioSigKit> Accelerometer signals should be 3*N!\n'); |
|
|
531 |
return; |
|
|
532 |
end |
|
|
533 |
fprintf('|#| BioSigKit> ECG Artifact removal with NLMS...\n'); |
|
|
534 |
output = NLMS_ecg(ref,obj.Sig,order,obj.PlotResult); |
|
|
535 |
otherwise |
|
|
536 |
fprintf('|#| BioSigKit> Filter type not recognized!\n'); |
|
|
537 |
end |
|
|
538 |
fprintf('|#| Done!\n'); |
|
|
539 |
end |
|
|
540 |
%% ----------------- Non-Linear PhaseSpace Filter -------------- %% |
|
|
541 |
function output = nonlinear_phase_filt(obj,t,d,m,r) |
|
|
542 |
%-------------------- Method ---------------------------% |
|
|
543 |
% Employs nonlinear phasespace filter to clean up the signal. This |
|
|
544 |
% method is very strong and even able to extract foetal ecg from |
|
|
545 |
% single channel maternal recordings. Please refer to examples of |
|
|
546 |
% BioSigKit for further details. |
|
|
547 |
%-------------------- Inputs ----------------------------% |
|
|
548 |
% sig : Signal to be analyzed (single channel) |
|
|
549 |
% t : Number of samples for computing delayed phase space (def: 1) |
|
|
550 |
% d : Embedding dimension to consider (def: 50) |
|
|
551 |
% m : dimension of null space (def: 49) |
|
|
552 |
% r : number of nearest neighbors to consider |
|
|
553 |
% (normally a large number def: length(sig)/4) |
|
|
554 |
% --------------------- Output ------------------------ % |
|
|
555 |
% output : Cleaned Signal |
|
|
556 |
% --------------------- example ----------------------- % |
|
|
557 |
% output = projective(foetal_ecg(:,2), 1, round(Fs/5), round(Fs/6.25), 1500); |
|
|
558 |
%---------------------- Input Handling ----------------------% |
|
|
559 |
if nargin < 5 |
|
|
560 |
r = round(length(obj.Sig)/4); |
|
|
561 |
if nargin < 4 |
|
|
562 |
m = 49; |
|
|
563 |
if nargin < 3 |
|
|
564 |
d = 50; |
|
|
565 |
if nargin < 2 |
|
|
566 |
t = 1; |
|
|
567 |
end |
|
|
568 |
end |
|
|
569 |
end |
|
|
570 |
end |
|
|
571 |
% ------------------- Run filter ------------------- % |
|
|
572 |
fprintf('|#| BioSigKit> Running Non-linear Filter...\n'); |
|
|
573 |
output = projective (obj.Sig, t, d, m, r); |
|
|
574 |
fprintf('|#| Done!\n'); |
|
|
575 |
end |
|
|
576 |
%% =================== ECG derived Respiration =============== %% |
|
|
577 |
function EDR = EDR_comp(obj) |
|
|
578 |
% --------------------- Method ------------------------ % |
|
|
579 |
% 1) Use Pan-tompkins for R peak detection |
|
|
580 |
% 2) Extract templates |
|
|
581 |
% 3) Use neural PCA to extract the eigenvectors |
|
|
582 |
% 4) construct EDR signal. |
|
|
583 |
% ---------------- Compute R peaks --------------------- % |
|
|
584 |
obj.PanTompkins(); |
|
|
585 |
% --------------- Segment window size ------------------ % |
|
|
586 |
Win = round(0.356*obj.Fs); |
|
|
587 |
if ~mod(Win,2) |
|
|
588 |
L = Win/2; |
|
|
589 |
Win = Win + 1; |
|
|
590 |
else |
|
|
591 |
L = (Win-1)/2; |
|
|
592 |
end |
|
|
593 |
% --------------- Segment the signal --------------------- % |
|
|
594 |
R = zeros(Win,length(obj.Results.R)); |
|
|
595 |
for i = 1 : length(obj.Results.R) |
|
|
596 |
if obj.Results.R(i)-L < 1 |
|
|
597 |
dummy = Win - length(obj.Sig(1:obj.Results.R(i)+L)); |
|
|
598 |
R(:,i) = [ones(dummy,1)*mean(obj.Sig(1:obj.Results.R(i)+L));... |
|
|
599 |
obj.Sig(1:obj.Results.R(i)+L)]; |
|
|
600 |
elseif obj.Results.R(i)+L > length(obj.Sig) |
|
|
601 |
dummy = Win - length(obj.Sig(obj.Results.R(i)-L:end)); |
|
|
602 |
R(:,i) = [obj.Sig(obj.Results.R(i)-L:end);... |
|
|
603 |
ones(dummy,1)*mean(obj.Sig(obj.Results.R(i)-L:end))]; |
|
|
604 |
else |
|
|
605 |
R(:,i) = obj.Sig(obj.Results.R(i)-L:obj.Results.R(i) + L); |
|
|
606 |
end |
|
|
607 |
end |
|
|
608 |
% ------------------- Real Time PCA ------------------------ % |
|
|
609 |
temp_ecg = obj.Sig; |
|
|
610 |
obj.Sig = R'; |
|
|
611 |
PC = neural_pca(obj,size(R,2),2); |
|
|
612 |
obj.Sig = temp_ecg; |
|
|
613 |
PC = PC(:,1); |
|
|
614 |
% ------------------- Build EDR Signal --------------------- % |
|
|
615 |
xx = 1 : length(temp_ecg); |
|
|
616 |
interp1 = [0 obj.Results.R length(temp_ecg)]; |
|
|
617 |
PC = [0 PC' 0]; |
|
|
618 |
EDR = spline(interp1,PC,xx); |
|
|
619 |
EDR = detrend(EDR); |
|
|
620 |
end |
|
|
621 |
%% ================ 3 Channel ACC derived Respiration ========== %% |
|
|
622 |
function ADR = ADR_comp(obj) |
|
|
623 |
%------------ Method -----------------------------% |
|
|
624 |
% (1) Adaptively filter the ACC signals |
|
|
625 |
% (2) Real Time PCA |
|
|
626 |
% (3) Mix PCs and reconstruct the signal |
|
|
627 |
%------------------- Input handling ----------------% |
|
|
628 |
if size(obj.Sig,1) < 3 |
|
|
629 |
fprintf('|#| BioSigKit> ACC signal should be 3*n!'); |
|
|
630 |
return; |
|
|
631 |
end |
|
|
632 |
|
|
|
633 |
output = ACC_Activity(obj.Sig,obj.Fs); |
|
|
634 |
% ------------------- Real Time PCA ------------------------ % |
|
|
635 |
PC = neural_pca(obj,3,2); |
|
|
636 |
PC = PC(:,1); |
|
|
637 |
ADR = output'*PC; |
|
|
638 |
ADR = detrend(ADR); |
|
|
639 |
end |
|
|
640 |
%% =================== Teager-Keiser Energy Op =========== %% |
|
|
641 |
function [ey,ex] = TK_comp(obj) |
|
|
642 |
% --------------- Outputs ----------------% |
|
|
643 |
% ey: energy operator |
|
|
644 |
% ex: Teager operator |
|
|
645 |
[ey,ex]=energyop(obj.Sig,obj.PlotResult); |
|
|
646 |
end |
|
|
647 |
end |
|
|
648 |
end |