findSpikes.m 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. function Spikes = findSpikes(NSx, varargin)
  2. % findSpikes
  3. %
  4. % Searches NSx data structure for spikes by thresholding. The output is
  5. % compatible with NEV.Data.Spikes data structure.
  6. %
  7. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  8. % Use OUTPUT = findSpikes(NSx, 'threshold', 'preThreshold', 'spikeLength', 'channels', 'duration', 'filter')
  9. %
  10. % NSx: The data structure holding the NSx structure
  11. %
  12. % NOTE: All following input arguments are optional. Input arguments may
  13. % be in any order.
  14. %
  15. % 'threshold': The threshold value to apply to the file.
  16. % DEFAULT: -65 uV
  17. %
  18. % 'preThreshold': The number of pre-threshold crossing samples to save.
  19. % DEFAULT: 10
  20. %
  21. % 'spikeLength': Length of each extracted spike in number of samples.
  22. % DEFAULT: 48
  23. %
  24. % 'channels': The channels to perform spike extraction on.
  25. % DEFAULT: will perform spike extraction on all channels
  26. %
  27. % 'duration': The duration of file to perform spike extraction on.
  28. % DEFAULT: will perform extraction on the entire file
  29. %
  30. % 'filter': The filter cut-off frequency applied before spike
  31. % extraction.
  32. % DEFAULT: no filter will be applied
  33. %
  34. % OUTPUT: Contains the NEV.Spikes structure.
  35. %
  36. % USAGE EXAMPLE:
  37. %
  38. % Spikes = findSpikes(NSx, 'channels', 1:3, 'duration', 1:10000, 'threshold', -65);
  39. %
  40. % In the above example the NSx analog data is searched for spikes on
  41. % channels 1 through 3 for samples between 1 and 10000. Threshold is
  42. % set to -65 uV for all channels.
  43. %
  44. % Spikes = findSpikes(NSx, 'channels', 1:3, 'threshold', [-65 -100 -85]);
  45. % In the above example the NSx analog data is searched for spikes among
  46. % channels 1 to 3. Thresholds for channel 1 is set to -65 uV, for channel
  47. % 2 is set to -100 uV, and for channel 3 is set to -85 uV.
  48. %
  49. % Original Author: Ehsan Azar
  50. %
  51. % Contributors:
  52. % Kian Torab, Blackrock Microsystems, ktorab@blackrockmicro.com
  53. %
  54. % Version 1.0.2.0
  55. %
  56. Spikes = struct('TimeStamp', [],'Electrode', [], 'Unit', [],'Waveform', [], 'findSpikesVer', []);
  57. %% Validating the input arguments. Exit with error message if error occurs.
  58. spikelen = 48;
  59. threshold = -65;
  60. preThreshold = 10;
  61. filterCorner = 250;
  62. next = '';
  63. for i=1:length(varargin)
  64. inputArgument = varargin{i};
  65. if (strcmpi(next, 'threshold'))
  66. next = '';
  67. threshold = inputArgument;
  68. elseif (strcmpi(next, 'preThreshold'))
  69. next = '';
  70. preThreshold = inputArgument;
  71. elseif (strcmpi(next, 'channels'))
  72. next = '';
  73. channels = inputArgument;
  74. elseif (strcmpi(next, 'duration'))
  75. next = '';
  76. duration = inputArgument;
  77. elseif (strcmpi(next, 'spikeLength'))
  78. next = '';
  79. spikelen = inputArgument;
  80. elseif (strcmpi(next, 'filter'))
  81. next = '';
  82. filterCorner = inputArgument;
  83. elseif strcmpi(inputArgument, 'threshold')
  84. next = 'threshold';
  85. elseif strcmpi(inputArgument, 'preThreshold')
  86. next = 'preThreshold';
  87. elseif strcmpi(inputArgument, 'channels')
  88. next = 'channels';
  89. elseif strcmpi(inputArgument, 'duration')
  90. next = 'duration';
  91. elseif strcmpi(inputArgument, 'spikeLength')
  92. next = 'spikeLength';
  93. elseif strcmpi(inputArgument, 'filter')
  94. next = 'filter';
  95. end
  96. end
  97. clear next;
  98. %% Give all input arguments a default value. All input argumens are
  99. % optional.
  100. if ~exist('channels', 'var'); channels = (1:size(NSx.Data, 1))'; end
  101. if ~exist('duration', 'var'); duration = (1:size(NSx.Data, 2))'; end
  102. if ~exist('filter', 'var'); filter = []; end
  103. %% Apply filter
  104. Data = NSx.Data(channels, duration).';
  105. if ~isempty(filter)
  106. [b, a] = butter(4, filterCorner/15000, 'high');
  107. Data = filter(b, a, Data);
  108. end
  109. %% Threshold
  110. if isfield(NSx, 'ElectrodesInfo');
  111. if (isscalar(threshold) && all([NSx.ElectrodesInfo(channels).MaxAnalogValue] == NSx.ElectrodesInfo(1).MaxAnalogValue) && ...
  112. all([NSx.ElectrodesInfo(channels).MaxDigiValue] == NSx.ElectrodesInfo(1).MaxDigiValue))
  113. threshold = (threshold * double(NSx.ElectrodesInfo(1).MaxDigiValue) / ...
  114. double(NSx.ElectrodesInfo(1).MaxAnalogValue)); % scalar threshold
  115. else
  116. threshold = (threshold .* double([NSx.ElectrodesInfo(channels).MaxDigiValue]) ./ ...
  117. double([NSx.ElectrodesInfo(channels).MaxAnalogValue])); % vector threshold
  118. end
  119. threshold = cast(threshold, class(Data));
  120. end
  121. [row, col] = find((diff(bsxfun(@minus, Data, threshold) > 0) < 0).');
  122. clear threshold;
  123. Spikes.TimeStamp = col' - preThreshold;
  124. clear col;
  125. Spikes.Electrode = row';
  126. clear row;
  127. Spikes.Electrode(Spikes.TimeStamp < 1) = [];
  128. Spikes.TimeStamp(Spikes.TimeStamp < 1) = [];
  129. Spikes.Electrode(Spikes.TimeStamp + spikelen > size(NSx.Data, 2)) = [];
  130. Spikes.TimeStamp(Spikes.TimeStamp + spikelen > size(NSx.Data, 2)) = [];
  131. %% Lockout violation removal
  132. % this makes sure all spikes are at least one spike length apart
  133. ii = 1;
  134. while (ii < length(Spikes.TimeStamp))
  135. idx = find((Spikes.Electrode((ii+1):end) == Spikes.Electrode(ii)) & ...
  136. (Spikes.TimeStamp((ii+1):end) - Spikes.TimeStamp(ii) >= spikelen), 1);
  137. if (isempty(idx))
  138. idx = find(Spikes.Electrode((ii+1):end) == Spikes.Electrode(ii));
  139. else
  140. idx = find(Spikes.Electrode((ii+1):(ii+idx-1)) == Spikes.Electrode(ii));
  141. end
  142. if (~isempty(idx))
  143. Spikes.Electrode(ii + idx) = [];
  144. Spikes.TimeStamp(ii + idx) = [];
  145. end
  146. ii = ii + 1;
  147. end
  148. clear idx;
  149. %% Spike extraction
  150. Spikes.Waveform = zeros(spikelen, length(Spikes.TimeStamp));
  151. for ii = 1:length(Spikes.TimeStamp)
  152. ts = Spikes.TimeStamp(ii):(Spikes.TimeStamp(ii) + spikelen - 1);
  153. Spikes.Waveform(:, ii) = Data(ts, Spikes.Electrode(ii));
  154. end
  155. % convert index to actual channel number
  156. Spikes.Electrode = channels(Spikes.Electrode);