saveNEVSpikes.m 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. function saveNEVSpikes(spikeStruct, newFileName)
  2. % saveNEVSpikes
  3. %
  4. % Allows the user to save a modified version of the spike waveforms into a
  5. % NEV file. This can be very useful for those who want to save MATLAB
  6. % sorted or rethresholded NEV data back into the NEV file. It also re-saves
  7. % the other data in the original NEV file into the new NEV. A
  8. %
  9. % Use saveNEVSpikes(spikeStruct, newFileName)
  10. %
  11. % spikeStruct: The structure containing the spike data needed to be
  12. % saved. The structure format must match the one that
  13. % openNEV outputs.
  14. % spikeStruct.TimeStamp: contains all the timestamps
  15. % spikeStruct.Electrode: contains all the electrode #s
  16. % spikeStruct.Unit: contains all the sorted unit #s
  17. % spikeStruct.Waveform: Spike waveforms, containing 48 data
  18. % points
  19. %
  20. % newFileName: The file name of the new NEV file.
  21. % DEFAULT: User will be prmpted for a file name.
  22. %
  23. % Example 1:
  24. % saveNEVSpikes(spikeStruct, 'sortedNEV';
  25. %
  26. % In the example above, the user will be prompted to select a NEV file.
  27. % The data stored in spikeStruct will be saved into sortedNEV alongside
  28. % the data in the user-selected NEV file. The new NEV will be saved as
  29. % sortedNEV.nev
  30. %
  31. % Kian Torab
  32. % ktorab@blackrockmicro.com
  33. % Blackrock Microsystems
  34. % Version 1.2.1.0
  35. %
  36. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  37. % Version History
  38. %
  39. % 1.0.0.0: Initial Release.
  40. %
  41. % 1.2.0.0:
  42. % - Fixed a bug where the data saved incorrectly under the Windows OS.
  43. % - Sped up the processing significantly.
  44. %
  45. % 1.2.1.0
  46. % - Fixed a bug introduced in 1.2.0.0.
  47. %
  48. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  49. %% Validating the input argument
  50. if ~exist('spikeStruct', 'var')
  51. disp('spikeStruct is a required input argument.');
  52. return;
  53. end
  54. % if isfield(spikeStruct, 'Data') && isfield(spikeStruct, 'MetaTags')
  55. % newFileName = fullfile(NEV.MetaTags.FilePath, [NEV.MetaTags.Filename, '.nev']);
  56. % spikeStruct = NEV.Data.Spikes;
  57. % end
  58. if ~exist('newFileName', 'var')
  59. newFileName = input('What file name would you like to use to save the new NEV? ', 's');
  60. if isempty(newFileName)
  61. disp('A filename is required.');
  62. return;
  63. end
  64. end
  65. %% Opening the file and reading header
  66. [dataFilename dataFolder] = getFile('*.nev', 'Choose a NEV that you would like to modify.');
  67. fileFullPath = [dataFolder dataFilename];
  68. FID = fopen(fileFullPath, 'r', 'ieee-le');
  69. %% Calculating the header bytes
  70. BasicHeader = fread(FID, 20, '*uint8');
  71. headerBytes = double(typecast(BasicHeader(13:16), 'uint32'));
  72. dataPacketByteLength = double(typecast(BasicHeader(17:20), 'uint32'));
  73. %% Calculating the data file length and eeking to the beginning of the file
  74. fseek(FID, 0, 'eof');
  75. endOfDataByte = ftell(FID);
  76. dataByteLength = endOfDataByte - headerBytes;
  77. numberOfPackets = double(dataByteLength) / double(dataPacketByteLength);
  78. %% Reading the header binaries and saving it for future
  79. fseek(FID, 0, 'bof');
  80. headerBinaries = fread(FID, headerBytes, '*uint8');
  81. %% Reading the data binaries
  82. dataBinaries = fread(FID, [dataPacketByteLength numberOfPackets], '*uint8', 0);
  83. %% Finding what PacketIDs have the desired channels
  84. PacketIDs = zeros(1,size(dataBinaries,2));
  85. for IDX = 1:size(dataBinaries,2)
  86. PacketIDs(IDX) = typecast(dataBinaries(5:6, IDX), 'uint16');
  87. end
  88. %% Only capturing all the binary lines that contain non-spike events, such
  89. % as comments, tracking, patient trigger, etc.
  90. newDataBinaries = dataBinaries(:, PacketIDs > 256); clear dataBinaries;
  91. %% Extracting the timestamps of all the above non-spike events
  92. dataTimestamps = zeros(1,size(newDataBinaries,2));
  93. for IDX = 1:size(newDataBinaries,2)
  94. dataTimestamps(IDX) = typecast(newDataBinaries(1:4, IDX), 'uint32');
  95. end
  96. %% Converting all the user supplied data into binaries for saving into the
  97. % new structure
  98. newSpikesBinary = zeros(size([typecast(uint32(spikeStruct.TimeStamp(1)), 'uint8'),...
  99. typecast(uint16(spikeStruct.Electrode(1)), 'uint8'),...
  100. typecast(uint8(spikeStruct.Unit(1)), 'uint8'),...
  101. 1,...
  102. typecast(int16(spikeStruct.Waveform(:,1))', 'uint8')]',1), size(spikeStruct.Electrode,2));
  103. for idx = 1:size(spikeStruct.Electrode,2)
  104. newSpikesBinary(:,idx) = [typecast(uint32(spikeStruct.TimeStamp(idx)), 'uint8'),...
  105. typecast(uint16(spikeStruct.Electrode(idx)), 'uint8'),...
  106. typecast(uint8(spikeStruct.Unit(idx)), 'uint8'),...
  107. 1,...
  108. typecast(int16(spikeStruct.Waveform(:,idx))', 'uint8')]';
  109. end
  110. %% Processing data
  111. % Concatinating the user supplied data (non-spike) and the user supplied data
  112. newDataBinaries = [newDataBinaries, newSpikesBinary];
  113. % Ranking the spike and re-ranking the data for saving (timestamp descending)
  114. allTimestamps = [dataTimestamps, spikeStruct.TimeStamp];
  115. [~, ranking] = sort(allTimestamps);
  116. newDataBinaries = newDataBinaries(:,ranking);
  117. %% Saving the new NEV containig the desired channels
  118. FIDw = fopen([dataFolder newFileName '.nev'], 'w+', 'ieee-le');
  119. fwrite(FIDw, headerBinaries, 'uint8');
  120. fwrite(FIDw, newDataBinaries, 'uint8');
  121. fclose(FID);
  122. fclose(FIDw);