TEAP (Toolbox for Emotion Analysis using Physiological Signals) doc
ECG_feat_extr.m
Go to the documentation of this file.
1 %This file is part of TEAP.
2 %
3 %TEAP is free software: you can redistribute it and/or modify
4 %it under the terms of the GNU General Public License as published by
5 %the Free Software Foundation, either version 3 of the License, or
6 %(at your option) any later version.
7 %
8 %TEAP is distributed in the hope that it will be useful,
9 %but WITHOUT ANY WARRANTY; without even the implied warranty of
10 %MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 %GNU General Public License for more details.
12 %
13 %You should have received a copy of the GNU General Public License
14 %along with TEAP. If not, see <http://www.gnu.org/licenses/>.
15 %
16 %> @file ECG_feat_extr.m
17 %> @brief Computes ECG features
18 %TODO: clarifiy the help
19 %TODO call on other simple function instead of having all the computation
20 %TODO: changes all any statement to all or ismember because I think it can
21 %lead to problems (variable computation while it should not compute it)
22 %in one file
23 %
24 % Inputs:
25 %> @param ECGSignal: the ECG signal (already subtracted from one lead)
26 %> @param varargin: you can choose which features to extract
27 %> default or no input will result in extracting all the features
28 %> (see feature Extractor)
29 %> featues names include:
30 %> - meanIBI: mean interbeat interval
31 %> - HRV: heart rate variability (deviation of IBI)
32 %> - MSE: Multi-Scale Entropy at 5 levels 1-5 (5 features)
33 %> - sp0001: Spectral power 0-0.1Hz,
34 %> - sp0102: Spectral power 0.1-0.2Hz,
35 %> - sp0203: Spectral power 0.2-0.3Hz,
36 %> - sp0304: Spectral power 0.3-0.4Hz,
37 %> - energyRatio: Spectral energy ratio between f<0.08Hz/f>0.15Hz and f<5.0Hz
38 %> - tachogram_LF: Low frequency spectral power in tachogram (HRV) [0.01-0.08Hz]
39 %> - tachogram_MF: Medium frequency spectral power in tachogram (HRV) [0.08-0.15Hz]
40 %> - tachogram_HF: High frequency spectral power in tachogram (HRV) [0.15-0.5Hz]
41 %> - tachogram_energy_ratio: Energy ratio for tachogram spectral content (MF/(LF+HF))
42 %
43 %> @retval ECG_features: vector of features among the following features
44 %> @retval ECG_feats_names: the names of the features is the same order than in
45 %> 'ECG_features'
46 %> @retval Bulk: if the input to the function is a Bulk than the Bulk is returned
47 %> with the updated ECG signal, including IBI. Otherwise NaN is
48 %> returned
49 %>
50 %
51 %> @author Copyright Guillaume Chanel 2013, 2015
52 %> @author Copyright Frank Villaro-Dixon, 2014
53 function [ECG_features, ECG_feats_names, Bulk] = ECG_feat_extr(ECGSignal,varargin)
54 
55 %Make sure we have an ECG signal and get the bulk for saving IBI (if needed)
56 [ECGSignal, Bulk] = ECG__assert_type(ECGSignal);
57 if(nargout < 3) %No bulk requested -> do not need to keep it
58  Bulk = [];
59 end
60 
61 % Define full feature list and get features selected by user
62 %TODO: confirm with Mohammad that the changes are ok (suppression of 'sp0103'
63 featuresNames = {'meanIBI', 'HRV','MSE','sp0001','sp0102','sp0203','sp0304','energyRatio','tachogram_LF','tachogram_MF','tachogram_HF',...
64  'tachogram_energy_ratio'};
65 featuresNamesIBI = {'meanIBI', 'HRV','MSE','energyRatio','tachogram_LF','tachogram_MF','tachogram_HF','tachogram_energy_ratio'};
66 ECG_feats_names = featuresSelector(featuresNames,varargin{:});
67 
68 %Compute the results
69 if(~isempty(ECG_feats_names))
70 
71  %Compute IBI if a features needing it is requested
72  if(any(ismember(featuresNamesIBI,ECG_feats_names)))
73  %Compute IBI
74  ECGSignal = ECG__compute_IBI( ECGSignal );
75  IBI = Signal__get_raw(ECGSignal.IBI);
76  IBI_sp = Signal__get_samprate(ECGSignal.IBI);
77 
78  %Update the Bulk with the new ECG signal
79  if(~isempty(Bulk))
80  Bulk = Bulk_update_signal(Bulk, Signal__get_signame(ECGSignal), ECGSignal);
81  end
82  end
83 
84  %Get information of ECG signal
85  ECG = Signal__get_raw(ECGSignal);
86  ECG_sp = Signal__get_samprate(ECGSignal);
87  %set the welch window size
88  welch_window_size_ECG = ECG_sp* 20;
89  welch_window_size_IBI= IBI_sp* 20;
90 
91  %meanIBI is computed
92  if any(strcmp('meanIBI',ECG_feats_names)) || any(strcmp('HRV',ECG_feats_names))
93  HRV = std(IBI);
94  meanIBI = mean(IBI);
95  end
96  if any(strcmp('MSE',ECG_feats_names))
97  %multi-scale entropy for 5 scales on hrv
98  [MSE] = multiScaleEntropy(IBI,5);
99  end
100  if length(ECG)< welch_window_size_ECG +ECG_sp
101  warning('singal to short for the welch size');
102  end
103  if length(ECG)< welch_window_size_ECG +1
104  warning('singal to short for the welch size - PSD features cannot be calcualted');
105  sp0001 = NaN;sp0102 = NaN;sp0203 = NaN; sp0304 = NaN;energyRatio = NaN;
106  else
107  if any(strcmp('sp0001',ECG_feats_names)) || any(strcmp('sp0102',ECG_feats_names)) ...
108  || any(strcmp('sp0203',ECG_feats_names)) || any(strcmp('sp0304',ECG_feats_names)) ...
109  || any(strcmp('energyRatio',ECG_feats_names))
110 
111  [P, f] = pwelch(ECG, welch_window_size_ECG, [], [], ECG_sp);
112  P=P/sum(P);
113  %power spectral featyres
114  %WARN: check that this resolution is obrainable with the ECG sampling rate
115  sp0001 = log(sum(P(f>0.0 & f<=0.1))+eps);
116  sp0102 = log(sum(P(f>0.1 & f<=0.2))+eps);
117  sp0203 = log(sum(P(f>0.2 & f<=0.3))+eps);
118  sp0304 = log(sum(P(f>0.3 & f<=0.4))+eps);
119  energyRatio = log(sum(P(f<0.08))/sum(P(f>0.15 & f<0.5))+eps);
120  end
121  if length(IBI)< welch_window_size_IBI +IBI_sp
122  warning('singal to short for the welch size');
123  end
124  if length(IBI)< welch_window_size_IBI +1
125  warning('singal to short for the welch size - PSD features cannot be calcualted');
126  tachogram_LF = NaN;tachogram_MF = NaN;tachogram_HF = NaN;
127  tachogram_energy_ratio = NaN;
128  else
129  %tachogram features; psd features on inter beat intervals
130  %R. McCraty, M. Atkinson, W. Tiller, G. Rein, and A. Watkins, �The
131  %effects of emotions on short-term power spectrum analysis of
132  %heart rate variability,� The American Journal of Cardiology, vol. 76,
133  %no. 14, pp. 1089 � 1093, 1995
134  if any(strcmp('tachogram_LF',ECG_feats_names)) ...
135  || any(strcmp('tachogram_MF',ECG_feats_names)) || any(strcmp('tachogram_HF',ECG_feats_names)) ...
136  || any(strcmp('tachogram_energy_ratio',ECG_feats_names))
137 
138  %Handle the case were IBI could not be computed
139  if(~isnan(IBI))
140  [Pt, ft] = pwelch(IBI, welch_window_size_IBI, [], [], IBI_sp);
141  %WARN: check that this is possible with the IBI sampling rate
142  %WARN: these values are sometimes negative because of the log, doesn't it appear as strange for a user ?
143  tachogram_LF = log(sum(Pt(ft>0.01 & ft<=0.08))+eps);
144  tachogram_MF = log(sum(Pt(ft>0.08 & ft<=0.15))+eps);
145  tachogram_HF = log(sum(Pt(ft>0.15 & ft<=0.5))+eps);
146  tachogram_energy_ratio = tachogram_MF/(tachogram_LF+tachogram_HF);
147  else
148  tachogram_LF = NaN;
149  tachogram_MF = NaN;
150  tachogram_HF = NaN;
151  tachogram_energy_ratio = NaN;
152  end
153  end
154  end
155  end
156  %Setup feature vector
157  ECG_features = [];
158  for i = 1:length(ECG_feats_names)
159  eval(['ECG_features = cat(2, ECG_features, ' ECG_feats_names{i} ');']);
160  end
161 
162  %MSE actually contains five features so rename features MSE as MSE1, MSE2... if it exists
163  %TODO: just a temporary hack ?
164  idx = find(ismember(ECG_feats_names, 'MSE'));
165  if ~isempty(idx)
166  ECG_feats_names = {ECG_feats_names{1:idx-1} 'MSE1' 'MSE2' 'MSE3' 'MSE4' 'MSE5' ECG_feats_names{idx+1:end}};
167  end
168 
169 else
170  ECG_features = [];
171 end
172 
173 end
174 
Signal__get_samprate
function Signal__get_samprate(in Signal)
Signal__get_raw
function Signal__get_raw(in Signal)
ECG__assert_type
function ECG__assert_type(in Signal)
ECG__compute_IBI
function ECG__compute_IBI(in ECGSignal)
multiScaleEntropy
function multiScaleEntropy(in data, in depth)
Bulk_update_signal
function Bulk_update_signal(in BulkSig, in signame, in sig)
ECG_feat_extr
function ECG_feat_extr(in ECGSignal, in varargin)
featuresSelector
function featuresSelector(in featuresNames, in varargin)
If no Include/exclude statement is specifed all features are returned.
Signal__get_signame
function Signal__get_signame(in Signal)