BlockVectorData.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. classdef BlockVectorData < Entry
  2. %BlockVectorData contains instructions for loading the Data types
  3. % (See BlockVectorDataType.m) from the data portions of the file
  4. % listed in the header.
  5. properties(GetAccess = protected, SetAccess = private)
  6. FileID % File Handle to be used for file reading operations.
  7. end
  8. methods
  9. function this = BlockVectorData(aEntryRecord, aFileID)
  10. %BlockVectorData: Constructs a new BlockVectorData corresponding
  11. % to an Entry Record and the file handle it came from
  12. this = this@Entry(aEntryRecord, int64(ftell(aFileID)));
  13. this.FileID = aFileID;
  14. fseek(this.FileID, double(this.EntryRecord.Length), 'cof');
  15. if ~(ftell(this.FileID) == (this.Start + this.EntryRecord.Length) || isinf(this.EntryRecord.Length))
  16. error('Unexpected BlockVectorHeader length')
  17. end
  18. end
  19. function Waveforms = GetRawV1ContinuousWaveforms(...
  20. this, ...
  21. aSourceSet, ...
  22. aChannelsToLoad, ...
  23. aTimeRange, ...
  24. aDimensions)
  25. if aSourceSet.Header.NumSamplesPerBlock ~= 1
  26. error('load_AxIS_raw:argNumSamplesPerBlock', ...
  27. 'Invalid header for RAW file: incorrect samples per block');
  28. end
  29. if aSourceSet.Header.BlockHeaderSize ~= 0
  30. error('load_AxIS_raw:argBlockHeaderSize', ...
  31. 'Invalid header for RAW file: incorrect block header size');
  32. end
  33. fseek(this.FileID, int64(this.Start), 'bof');
  34. fStart = 0;
  35. % Read data
  36. fSampleFreq = int64(aSourceSet.Header.SamplingFrequency);
  37. fChannelCount = int64(aSourceSet.Header.NumChannelsPerBlock);
  38. fBytesPerSecond = fSampleFreq * fChannelCount * int64(2);
  39. fMaxTime = int64(this.EntryRecord.Length / fBytesPerSecond);
  40. if ~strcmp(aTimeRange, 'all')
  41. fStart = aTimeRange(1);
  42. fEnd =aTimeRange(2);
  43. if(fStart >= fEnd)
  44. warning('Invalid timespan argument: end time < start time. No valid waveform can be returned.');
  45. Waveforms = Waveform.empty;
  46. return;
  47. end
  48. fSkipInitialSamples = fStart * fSampleFreq;
  49. fSkipInitialBytes = fSkipInitialSamples * fChannelCount * int64(2);
  50. if(fStart > fMaxTime)
  51. warning( 'DataSet only contains %d Seconds of data (%d Seconds was the requested start time). No waveform will be returned.', fMaxTime, fStart);
  52. Waveforms = Waveform.empty;
  53. return;
  54. end
  55. if(fEnd > fMaxTime)
  56. fEnd = fMaxTime;
  57. warning('DataSet only contains %d Seconds of data. Returned waveforms will be shorter than requested (%d Seconds).', fMaxTime, fEnd - fStart);
  58. end
  59. fNumSamples = int64((fEnd - fStart) * aSourceSet.Header.SamplingFrequency);
  60. % skip past samples that are before the current time range
  61. fseek(this.FileID, fSkipInitialBytes, 'cof');
  62. else
  63. fNumSamples = int64((fMaxTime) * aSourceSet.Header.SamplingFrequency);
  64. end
  65. % Read the data for the given channel, skipping from one to the next
  66. fNumChannels = length(aSourceSet.ChannelArray.Channels);
  67. fMaxExtents = [max([aSourceSet.ChannelArray.Channels(:).WellRow]), ...
  68. max([aSourceSet.ChannelArray.Channels(:).WellColumn]), ...
  69. max([aSourceSet.ChannelArray.Channels(:).ElectrodeColumn]), ...
  70. max([aSourceSet.ChannelArray.Channels(:).ElectrodeRow])];
  71. switch aDimensions
  72. case {LoadArgs.ByPlateDimensions}
  73. Waveforms = [];
  74. case {LoadArgs.ByWellDimensions}
  75. Waveforms = cell(double(fMaxExtents(1:2)));
  76. case {LoadArgs.ByElectrodeDimensions}
  77. Waveforms = cell(double(fMaxExtents));
  78. end
  79. if length(aChannelsToLoad) == 1
  80. % We're only reading one channel. For efficiency, take advantage of fread's
  81. % 'skip' argument.
  82. fread(this.FileID, aChannelsToLoad - 1, 'int16=>int16');
  83. fWaveform = fread(this.FileID, fNumSamples, 'int16=>int16', 2*(fNumChannels - 1));
  84. fChannelMapping = aSourceSet.ChannelArray.Channels(aChannelsToLoad);
  85. fWaveform = Waveform(fChannelMapping, fStart, fWaveform, aSourceSet);
  86. fOuputIndex = double([...
  87. fChannelMapping.WellRow, ...
  88. fChannelMapping.WellColumn, ...
  89. fChannelMapping.ElectrodeColumn, ...
  90. fChannelMapping.ElectrodeRow]);
  91. switch aDimensions
  92. case {LoadArgs.ByPlateDimensions}
  93. Waveforms = fWaveform;
  94. case {LoadArgs.ByWellDimensions}
  95. Waveforms{fOuputIndex(1),fOuputIndex(2)} = fWaveform;
  96. case {LoadArgs.ByElectrodeDimensions}
  97. Waveforms{fOuputIndex(1),fOuputIndex(2), fOuputIndex(3),fOuputIndex(4)} = fWaveform;
  98. end
  99. else
  100. fNumSamples = fNumSamples * fNumChannels;
  101. fTempChannelData = fread(this.FileID, fNumSamples, 'int16=>int16');
  102. % This test (which can fail only when we read the the end of the file)
  103. % makes sure that we didn't get a number of samples that's not divisible
  104. % by the number of channels.
  105. fRemainderCount = mod(length(fTempChannelData), fNumChannels);
  106. if fRemainderCount ~= 0
  107. warning('load_AXiS_raw:remainderCheck', ...
  108. 'File %s has wrong number of samples for %u channels', ...
  109. this, fNumChannels);
  110. end
  111. % Convert the 1D array to a 2D array, with channel as the second dimension
  112. % (starts as first dimension and then is transposed)
  113. fTempChannelData = reshape(fTempChannelData(:), fNumChannels, []);
  114. for fChannelIndex = aChannelsToLoad
  115. fChannelMapping = aSourceSet.ChannelArray.Channels(fChannelIndex);
  116. fWaveform = fTempChannelData(fChannelIndex,:)';
  117. fWaveform = Waveform(fChannelMapping, fStart, fWaveform, aSourceSet);
  118. fOuputIndex = double([...
  119. fChannelMapping.WellRow, ...
  120. fChannelMapping.WellColumn, ...
  121. fChannelMapping.ElectrodeColumn, ...
  122. fChannelMapping.ElectrodeRow]);
  123. switch aDimensions
  124. case {LoadArgs.ByPlateDimensions}
  125. if(isempty(Waveforms))
  126. Waveforms = fWaveform;
  127. else
  128. Waveforms(length(Waveforms) + 1) = fWaveform;
  129. end
  130. case {LoadArgs.ByWellDimensions}
  131. Waveforms{fOuputIndex(1),fOuputIndex(2)}(...
  132. length(Waveforms{fOuputIndex(1),fOuputIndex(2)}) + 1) = fWaveform;
  133. case {LoadArgs.ByElectrodeDimensions}
  134. Waveforms{fOuputIndex(1),fOuputIndex(2), fOuputIndex(3),fOuputIndex(4)} = fWaveform; %We only expect one Waveform per channel
  135. end
  136. end
  137. end
  138. end
  139. function Waveforms = GetSpikeV1Waveforms(...
  140. this, ...
  141. aSourceSet, ...
  142. aChannelsToLoad, ...
  143. aTimeRange, ...
  144. aDimensions)
  145. if aSourceSet.Header.NumChannelsPerBlock ~= 1
  146. error('Load_spike_v1:argNumChannelsPerBlock', ...
  147. 'Invalid header for SPIKE file: incorrect channels per block');
  148. end
  149. if aSourceSet.Header.BlockHeaderSize < Spike_v1.LOADED_HEADER_SIZE
  150. error('Load_spike_v1:argBlockHeaderSize', ...
  151. 'Invalid header for SPIKE file: block header size too small');
  152. end
  153. if aSourceSet.Header.NumSamplesPerBlock < 1
  154. error('load_AxIS_spike:argNumSamplesPerBlock', ...
  155. 'Invalid header for SPIKE file: number of samples per block < 1');
  156. end
  157. fHeader = aSourceSet.Header;
  158. if ischar(aTimeRange) && strcmp(aTimeRange, 'all')
  159. fFirstSample = 0;
  160. fLastSample = Inf;
  161. else
  162. fFirstSample = aTimeRange(1) * fHeader.SamplingFrequency;
  163. fLastSample = aTimeRange(2) * fHeader.SamplingFrequency;
  164. end
  165. fMaxExtents = [max([aSourceSet.ChannelArray.Channels(:).WellRow]), ...
  166. max([aSourceSet.ChannelArray.Channels(:).WellColumn]), ...
  167. max([aSourceSet.ChannelArray.Channels(:).ElectrodeColumn]), ...
  168. max([aSourceSet.ChannelArray.Channels(:).ElectrodeRow])];
  169. switch aDimensions
  170. case {LoadArgs.ByPlateDimensions}
  171. Waveforms = [];
  172. case {LoadArgs.ByWellDimensions}
  173. Waveforms = cell(double(fMaxExtents(1:2)));
  174. case {LoadArgs.ByElectrodeDimensions}
  175. Waveforms = cell(double(fMaxExtents));
  176. end
  177. fWaveformBytesSize = (fHeader.NumChannelsPerBlock * fHeader.NumSamplesPerBlock * 2) + fHeader.BlockHeaderSize;
  178. fseek(this.FileID, int64(this.Start), 'bof');
  179. fData = fread(this.FileID, this.EntryRecord.Length, 'int8=>int8');
  180. fData = reshape(fData, fWaveformBytesSize, []);
  181. fNumWaves = size(fData,2);
  182. fNumWaves = fNumWaves(1);
  183. fChannelArray = aSourceSet.ChannelArray;
  184. for iWave = 1 : fNumWaves
  185. fStartingSample = typecast(fData(1:8,iWave), 'int64');
  186. if fStartingSample < fFirstSample
  187. % Too early
  188. continue;
  189. elseif fStartingSample >= fLastSample
  190. % Too late. Since spikes are in chronological order, we
  191. % can stop reading now.
  192. break;
  193. end
  194. fHardwareChannelIndex = typecast(fData(9,iWave), 'uint8');
  195. fHardwareChannelAchk = typecast(fData(10,iWave), 'uint8');
  196. fChannelIndex = fChannelArray.LookupChannel(fHardwareChannelAchk, fHardwareChannelIndex);
  197. fChannelMapping = fChannelArray.Channels(fChannelIndex);
  198. fOuputIndex = double([...
  199. fChannelMapping.WellRow, ...
  200. fChannelMapping.WellColumn, ...
  201. fChannelMapping.ElectrodeColumn, ...
  202. fChannelMapping.ElectrodeRow]);
  203. if sum(eq(aChannelsToLoad, fChannelIndex))
  204. fWaveform = Spike_v1( ...
  205. fChannelMapping, ...
  206. double(fStartingSample) / fHeader.SamplingFrequency, ...
  207. typecast(fData(fHeader.BlockHeaderSize+1:fWaveformBytesSize,iWave)','int16')', ...
  208. aSourceSet, ...
  209. typecast(fData(11:14,iWave), 'uint32'), ...
  210. typecast(fData(15:22,iWave), 'double'), ...
  211. typecast(fData(23:30,iWave), 'double'));
  212. switch aDimensions
  213. case {LoadArgs.ByPlateDimensions}
  214. if(isempty(Waveforms))
  215. Waveforms = fWaveform;
  216. else
  217. Waveforms(length(Waveforms) + 1) = fWaveform;
  218. end
  219. case {LoadArgs.ByWellDimensions}
  220. Waveforms{fOuputIndex(1),fOuputIndex(2)}(...
  221. length(Waveforms{fOuputIndex(1),fOuputIndex(2)}) + 1) = fWaveform;
  222. case {LoadArgs.ByElectrodeDimensions}
  223. Waveforms{fOuputIndex(1),fOuputIndex(2), fOuputIndex(3),fOuputIndex(4)}(...
  224. length(Waveforms{fOuputIndex(1),fOuputIndex(2), fOuputIndex(3),fOuputIndex(4)}) + 1) = fWaveform;
  225. end
  226. end
  227. end
  228. end
  229. end
  230. end