-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathpsr_lfp_artifact_detection_psd.m
112 lines (87 loc) · 3.47 KB
/
psr_lfp_artifact_detection_psd.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
function artifacts = psr_lfp_artifact_detection_psd(data,parameters)
% PSR_LFP_ARTIFACT_DETECTION_PSD - Detects LFP power spectral density artifacts
%
% Syntax: artifacts = psr_lfp_artifact_detection_psd(data,parameters)
%
% Inputs:
% data - Same as input for PSR_LFP_CONVERSION
% parameters - See README and PSR_PARAMETERS_GENERAL
%
% Outputs:
% artifacts - Returns two-column array, where the first column are the
% onsets and the second column the offsets of the detected
% power spectral density artifacts. Timestamps given in secs.
%
% See also: PSR_WRAPPER, PSR_LFP_ARTIFACT_REMOVAL
% PASER: Processing and Analysis Schemes for Extracellular Recordings
% https://github.com/tbrouns/paser
% Author: Terence Brouns
% Radboud University, Neurophysiology Dept.
% E-mail address: [email protected]
% Date: 2018
%------------- BEGIN CODE --------------
[data,nBlocks] = psr_lfp_conversion(data);
% Artifact removal based on power spectral density
fRange = parameters.lfp.artifact.psd.frange;
Fr = parameters.Fr;
sWin = parameters.lfp.artifact.psd.win * Fr;
sOff = 0.5 * sWin; % Take half of window
sSec = sWin - sOff;
%% Calculate power spectral density for every data section
psdAll = cell(0,0);
secAll = cell(0,0);
for iBlock = nBlocks:-1:1
dataBlock = data{iBlock};
if (isfield(dataBlock,'trial')); dataBlock = dataBlock.trial{1}; end
nChans = size(dataBlock,1);
if (~isempty(dataBlock))
for iChan = 1:nChans
dataChan = dataBlock(iChan,:); % average across channels
dataSegment = buffer(dataChan,sWin,sOff); % extract the segments, with overlap
dataSegment = dataSegment(:,2:end-1); % ignore first and last segments
nSecs = size(dataSegment,2);
psdTemp = [];
for iSec = nSecs:-1:1
dataSec = dataSegment(:,iSec);
psd = pwelch(dataSec,[],[],fRange,Fr);
if (isempty(psdTemp)); psdTemp = zeros(length(psd),nSecs,'single'); end % Initialize
psdTemp(:,iSec) = single(psd);
end
offsets = sSec * (1:nSecs);
onsets = offsets - sSec + 1;
secTemp = [onsets;offsets;iBlock*ones(size(onsets))];
% Save
psdAll{iChan,iBlock} = psdTemp;
secAll{iChan,iBlock} = secTemp;
end
end
end
keepPsd = any(~cellfun(@isempty,psdAll),1);
keepSec = any(~cellfun(@isempty,secAll),1);
psdAll = psdAll(:,keepPsd);
secAll = secAll(:,keepSec);
psdAll = cell2mat(psdAll);
secAll = cell2mat(secAll);
psdMean = mean(psdAll,2); % Average across all segments
psdAll = bsxfun(@rdivide,psdAll,psdMean); % Normalize
% Total PSD threshold
psdSum = sum(psdAll); % Sum across all frequencies
thresh = parameters.lfp.artifact.psd.thresh * psr_mad(psdSum); % Artifact thresholding
artifacts = psdSum > thresh;
% Extract intervals
artifacts = secAll(:,artifacts);
artifactsAll = cell(nBlocks,1);
for iBlock = 1:nBlocks
artifactsBlock = artifacts(:,artifacts(3,:) == iBlock);
timings = artifactsBlock(1,:);
if (~isempty(timings))
d = diff(timings); % Combine adjacent intervals
offsets = find(d > sOff);
onsets = offsets + 1;
offsets = artifactsBlock(2,unique([offsets length(timings)]));
onsets = artifactsBlock(1,unique([1 onsets]));
artifactsAll{iBlock} = (([onsets;offsets] - 1) / Fr)';
end
end
artifacts = artifactsAll; % output
end