-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathpspm_emg_pp.m
137 lines (119 loc) · 4.51 KB
/
pspm_emg_pp.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
function [sts, output] = pspm_emg_pp(fn, options)
% ● Description
% pspm_emg_pp reduces noise in emg data in 3 steps. Following from the
% literature[1] it does the following steps:
% 1) Initial filtering:
% 4th order Butterworth with 50 Hz and 470 Hz cutoff frequencies
% 2) Remove mains noise:
% 50 Hz (variable) notch filter
% 3) Smoothing and rectifying:
% 4th order Butterworth low-pass filter with a time constant of 3 ms
% (=> cutoff of 53.05Hz)
% Once the data is preprocessed, according to the option 'channel_action',
% it will either replace the existing channel or add it as new channel to
% the provided file.
% ● Format
% [sts, output] = pspm_emg_pp(fn, options)
% ● Arguments
% fn: [string]
% Path to the PsPM file which contains the EMG data.
% options:
% .mains_freq: [integer] Frequency of mains noise to remove
% with notch filter (default: 50Hz).
% .channel: [numeric/string] Channel to be preprocessed.
% Can be a channel ID or a channel name.
% Default is 'emg' (i.e. first EMG channel)
% .channel_action: ['add'/'replace'] Defines whether the new channel should
% be added or the previous outputs of this function should
% be replaced. (Default: 'replace')
% ● References
% [1] Khemka S, Tzovara A, Gerster S, Quednow BB, Bach DR (2016).
% Modeling Startle Eyeblink Electromyogram to Assess Fear Learning.
% Psychophysiology
% ● History
% Introduced in PsPM 3.1
% Written in 2009-2016 by Tobias Moser (University of Zurich)
%% Initialise
global settings
if isempty(settings)
pspm_init;
end
sts = -1;
output = struct();
% set default values
% -------------------------------------------------------------------------
if nargin < 2
options = struct();
end
if ~isfield(options, 'mains_freq')
options.mains_freq = 50;
end
options = pspm_options(options, 'emg_pp');
if options.invalid
return
end
% check values
% -------------------------------------------------------------------------
if ~isnumeric(options.mains_freq)
warning('ID:invalid_input', 'Option mains_freq must be numeric.');
return;
end
% load data
% -------------------------------------------------------------------------
[lsts, infos, data] = pspm_load_data(fn, options.channel);
if lsts ~= 1, return, end
% do the job
% -------------------------------------------------------------------------
% (1) 4th order Butterworth band-pass filter with cutoff frequency of 50 Hz and 470 Hz
filt.sr = data{1}.header.sr;
filt.lpfreq = 470;
filt.lporder = 4;
filt.hpfreq = 50;
filt.hporder = 4;
filt.down = 'none';
filt.direction = 'uni';
[lsts, data{1}.data, data{1}.header.sr] = pspm_prepdata(data{1}.data, filt);
if lsts == -1, return; end
% (2) remove mains noise with notch filter
% design from
% http://dsp.stackexchange.com/questions/1088/filtering-50hz-using-a-
% notch-filter-in-matlab
nfr = filt.sr/2; % Nyquist frequency
freqRatio = options.mains_freq/nfr; % ratio of notch freq. to Nyquist freq.
nWidth = 0.1; % width of the notch filter
% Compute zeros
nZeros = [exp( sqrt(-1)*pi*freqRatio ), exp( -sqrt(-1)*pi*freqRatio )];
% Compute poles
nPoles = (1-nWidth) * nZeros;
b = poly( nZeros ); % Get moving average filter coefficients
a = poly( nPoles ); % Get autoregressive filter coefficients
% filter signal x
data{1}.data = filter(b,a,data{1}.data);
% (3) smoothed using 4th order Butterworth low-pass filter with
% a time constant of 3 ms corresponding to a cutoff frequency of 53.05 Hz
filt.sr = data{1}.header.sr;
filt.lpfreq = 1/(2*pi*0.003);
filt.lporder = 4;
filt.hpfreq = 'none';
filt.hporder = 0;
filt.down = 'none';
filt.direction = 'uni';
% rectify before with abs()
[lsts, data{1}.data, data{1}.header.sr] = pspm_prepdata(abs(data{1}.data), filt);
if lsts == -1, return; end
% change channel type to emg_pp to match sebr modality
old_channeltype = data{1}.header.chantype;
data{1}.header.chantype = 'emg_pp';
% save data
% -------------------------------------------------------------------------
channel_str = num2str(options.channel);
o.msg.prefix = sprintf(...
'EMG preprocessing :: Input channel: %s -- Input channeltype: %s -- Output channeltype: %s --', ...
channel_str, ...
old_channeltype, ...
data{1}.header.chantype);
[lsts, outinfos] = pspm_write_channel(fn, data{1}, options.channel_action, o);
if lsts ~= 1, return; end
output.channel = outinfos.channel;
sts = 1;
return