KTNSPOnline.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. classdef KTNSPOnline
  2. % UEAMAPFILE -- Defines a class that contains information on a UEA
  3. % electrode array using CMP files provided by Blackrock Microsystems.
  4. %
  5. % To load a CMP file type 'MapName = UEAMapFile' where MapName is the name
  6. % of the variable that will be created and will contain the map class.
  7. %
  8. % Example: myArrayMap = UEAMapFile;
  9. %
  10. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  11. % METHODS:
  12. %
  13. % There are several available methods for mapfiles. Here's a list and a
  14. % description of what they perform.
  15. %
  16. % Electrode2Channel(Electrode):
  17. % Will return the Channel number corresponding to a passed Electrode.
  18. %
  19. % Channel2Electrode(Channel):
  20. % Will return the Electrode number corresponding to a passed Channel.
  21. %
  22. % GetChannelBankID(Channel):
  23. % Returns the BankID for a passed Channel.
  24. %
  25. % GetChannelPin(Channel):
  26. % Returns the Pin for a passed Channel.
  27. %
  28. % GetChannelLabel(Channel):
  29. % Returns the Label for a passed Channel.
  30. %
  31. % GenerateChannelSubplot(Channel):
  32. % Returns a subplot handle for the passed Channel to be used in plotting
  33. % UEA-like maps.
  34. %
  35. % GenerateChannelSubplotNames(Channel):
  36. % Plots the names of channels and electrodes on the subplot corresponding
  37. % to the passed Channel.
  38. %
  39. % GetChannelColumnRow(Channel):
  40. % Returns the Column and Row positions of the passed number that is used
  41. % to plot UEA-like maps.
  42. %
  43. % PlotCMP:
  44. % Will plot a map of the CMP Map with all the electrode and channel
  45. % names.
  46. %
  47. % Kian Torab
  48. % ktorab@blackrockmicro.com
  49. % Version 1.1.0.0
  50. %%
  51. properties (Hidden)
  52. NSPHandle
  53. dataCollectionFlag
  54. contCollection
  55. allSpikeData
  56. allLFPData
  57. end
  58. methods (Hidden)
  59. function obj = KTNSPOnline
  60. disp('Initializing NSP Connection...');
  61. try
  62. cbmex('open');
  63. catch
  64. fprintf(2,'Error connecting to NSP... Please make sure the NSP or nPlayServer is running and try again.\n');
  65. return;
  66. end
  67. obj.contCollection = 0;
  68. obj.dataCollectionFlag = 0;
  69. obj.dataCollectionStart;
  70. end
  71. end
  72. methods
  73. %% NSP Interface Mthods
  74. function closeConnection(obj)
  75. cbmex('close');
  76. end
  77. function openConnection(obj)
  78. cbmex('open');
  79. end
  80. function NSPTime = getNSPTimeSeconds(obj)
  81. NSPTime = cbmex('time');
  82. end
  83. function NSPTime = getNSPTimeSamples(obj)
  84. NSPTime = cbmex('time') * 30000;
  85. end
  86. %% Data Recording Methods
  87. function recordingStart(obj, fileName, userComment)
  88. if ~exist('userComment', 'var')
  89. userComment = 'KTNSPOnline triggered.';
  90. end
  91. if ~exist('fileName', 'var')
  92. fprintf(2,'Error...\n');
  93. disp('Filename is a required parameter.');
  94. else
  95. cbmex('fileconfig', fileName, userComment, 1);
  96. pause(0.1);
  97. cbmex('fileconfig', fileName, userComment, 1);
  98. end
  99. end
  100. function recordingStop(obj)
  101. cbmex('fileconfig', '', '', 0);
  102. end
  103. %% Output Methods
  104. function sendNSPComment(obj, comment)
  105. if ischar(comment)
  106. cbmex('comment', 0, comment);
  107. else
  108. fprintf(2,'Error...\n');
  109. disp('The comment should be an ASCII string.');
  110. end
  111. end
  112. function sendNSPMarker(obj, markerValue)
  113. if isnumeric(markerValue) && markerValue <= 255
  114. cbmex('comment', markerValue);
  115. else
  116. fprintf(2,'Error...\n');
  117. disp('The marker value should be a number less than 255.');
  118. end
  119. end
  120. function setDigOutTTLHigh(obj, digOutPortNum)
  121. digOutPortNum = digOutPortNum + 152;
  122. cbmex('digitalout', digOutPortNum, 1);
  123. end
  124. function setDigOutTTLLow(obj, digOutPortNum)
  125. digOutPortNum = digOutPortNum + 152;
  126. cbmex('digitalout', digOutPortNum, 0);
  127. end
  128. function setDigOutPulse(obj, digOutPortNum, pulseCount, frequency, pulseWidth)
  129. if ~exist('digOutPortNum', 'var') || ...
  130. ~exist('pulseCount', 'var') || ...
  131. ~exist('frequency', 'var') || ...
  132. ~exist('pulseWidth', 'var')
  133. fprintf(2,'The function requires the following inputs:\n');
  134. disp('1. Digital Output (digOutPortNum): The digital out port to be triggered.');
  135. disp('2. Pulse Count (pulseCount): The number of pulses to be sent out of digital out.');
  136. disp('3. Frequency (frequency): The frequency of the output signal.');
  137. disp('4. Pulse Width (pulseWidth): The width of the output TTL pulse.');
  138. disp('Example: object.setDigOutPulse(1, 10, 100, 0.01);');
  139. else
  140. digOutPortNum = digOutPortNum + 152;
  141. for idx = 1:pulseCount
  142. cbmex('digitalout', digOutPortNum, 1);
  143. pause(pulseWidth);
  144. cbmex('digitalout', digOutPortNum, 0);
  145. pause(1/frequency - pulseWidth);
  146. end
  147. end
  148. end
  149. function setAnaOutPulse(obj, anaOutPortNum, pulseCount, frequency, pulseWidth)
  150. if ~exist('anaOutPortNum', 'var') || ...
  151. ~exist('pulseCount', 'var') || ...
  152. ~exist('frequency', 'var') || ...
  153. ~exist('pulseWidth', 'var')
  154. fprintf(2,'The function requires the following inputs:\n');
  155. disp('1. Analog Output (anaOutPortNum): The analog out port to be triggered.');
  156. disp('2. Pulse Count (pulseCount): The number of pulses to be sent out of analog out.');
  157. disp('3. Frequency (frequency): The frequency of the output signal.');
  158. disp('4. Pulse Width (pulseWidth): The width of the output TTL pulse in milliseconds.');
  159. disp('Example: object.setAnaOutPulse(1, 10, 100, 0.01);');
  160. else
  161. anaOutPortNum = anaOutPortNum + 144;
  162. cbmex('analogout', anaOutPortNum, 'pulses', [pulseCount 0 pulseWidth pulseWidth 4000 0 0 0], 'ms', 'mv')
  163. end
  164. end
  165. %% Data Collection Methods
  166. % function status = dataCollectionIsActive(obj)
  167. % try
  168. % activeFlag = cbmex('trialdata', 1)
  169. % catch
  170. % status = 0;
  171. % disp('Data collection is not active.');
  172. % return;
  173. % end
  174. % disp('Data is currently being collected...');
  175. % status = 1;
  176. % end
  177. function status = dataCollectionStart(BA)
  178. % if ~BA.dataCollectionIsActive
  179. disp('Starting to collect data now...');
  180. cbmex('trialconfig', 1);
  181. % end
  182. % BA.dataCollectionFlag = 1;
  183. end
  184. function dataCollectionStop(obj)
  185. % if obj.dataCollectionIsActive
  186. disp('Data collection is now stopped.');
  187. cbmex('trialconfig', 0);
  188. % end
  189. % obj.dataCollectionFlag = 0;
  190. end
  191. % function dataCollectionSetContActive(obj)
  192. % disp('Continuous data collection is now active.');
  193. % fprintf(2, 'Do not change any channel sampling frequency while continuous data collection is active.\n');
  194. % obj.ContCollection = 1;
  195. % end
  196. function [spikeData, startTime, LFPData] = dataCollectionReadBuffer(obj)
  197. try
  198. [spikeData, startTime, LFPData] = cbmex('trialdata', 1);
  199. catch
  200. fprintf(2,'Error...\n');
  201. disp('Data collection is not active.');
  202. disp('Use dataCollectionStart method to start locating.');
  203. end
  204. % if obj.contCollection
  205. % numChannelsRead = size(LFPData,1);
  206. % if isempty(obj.allLFPData)
  207. % obj.allLFPData = LFPData;
  208. % else
  209. % for idx = 1:numChannelsRead
  210. % obj.allLFPData{idx, 3} = [obj.allLFPData{idx, 3}; LFPData{idx, 3}];
  211. % end
  212. % end
  213. % end
  214. end
  215. % function data = getDataLFPContinuous(obj)
  216. % data = obj.allLFPData;
  217. % end
  218. %% Detection Methods
  219. function spikedChannels = detectChannelsFired(obj, units)
  220. if ~exist('units', 'var')
  221. units = 1:5;
  222. end
  223. spikeData = dataCollectionReadBuffer(obj);
  224. spikeData(:,1) = [];
  225. spikedChannels = ~cellfun(@isempty, spikeData(:,units));
  226. end
  227. function fireFlag = detectChannelUnitFiredAny(obj, channels, units)
  228. if ~exist('units', 'var')
  229. units = 1:5;
  230. end
  231. spikedChannels = obj.detectChannelsFired(units);
  232. fireFlag = any(any(spikedChannels(channels, units)));
  233. end
  234. function fireFlag = detectChannelUnitFiredAll(obj, channels, units)
  235. if ~exist('units', 'var')
  236. units = 1:5;
  237. end
  238. spikedChannels = obj.detectChannelsFired(units);
  239. fireFlag = all(all(spikedChannels(channels, units)));
  240. end
  241. function fireFlag = detectWhenChannelUnitFiredAny(obj, channels, units, timeWindow)
  242. fireFlag = 0;
  243. if ~exist('units', 'var')
  244. units = 1:5;
  245. end
  246. if ~exist('timeWindow', 'var')
  247. timeWindow = 0.02;
  248. end
  249. while ~fireFlag
  250. pause(timeWindow);
  251. fireFlag = detectChannelUnitFiredAny(obj, channels, units);
  252. end
  253. end
  254. % function fireFlag = detectWhenChannelUnitFiredAll(obj, channels, units, timeWindow)
  255. % fireFlag = 0;
  256. % if ~exist('units', 'var')
  257. % units = 1:5;
  258. % end
  259. % if ~exist('timeWindow', 'var')
  260. % timeWindow = 0.02;
  261. % end
  262. % while ~fireFlag
  263. % pause(timeWindow);
  264. % fireFlag = detectChannelUnitFiredAll(obj, channels, units);
  265. % end
  266. % end
  267. function [bitValues, bitTimestamps] = detectDigInWord(obj)
  268. readData = obj.dataCollectionReadBuffer;
  269. bitValues = readData{151,3};
  270. bitTimestamps = readData{151,2};
  271. end
  272. function [bitValues, bitTimestamps] = detectDigInBinary(obj)
  273. readData = obj.dataCollectionReadBuffer;
  274. bitValues = dec2bin(readData{151,3});
  275. bitTimestamps = readData{151,2};
  276. end
  277. % % % % function fireFlag = detectDigInBit(obj, bitDetect)
  278. % % % % fireFlag = 0;
  279. % % % % if ~exist('bitDetect', 'var')
  280. % % % % disp('Please specify a bit that needs to be detected.');
  281. % % % % disp('Example: detectDigInBit(3)');
  282. % % % % return;
  283. % % % % end
  284. % % % % spikedChannels = obj.detectChannelsFired([1:5]);
  285. % % % % if (bitDetect < 0) || (bitDetect > 15)
  286. % % % % disp('The bit value can be between 0 and 15, inclusive.');
  287. % % % % else
  288. % % % % fireFlag = any(any(spikedChannels(136, 1:5)));
  289. % % % % end
  290. % % % % end
  291. function fireFlag = detectWhenDigInWord(obj, wordValue, timeWindow)
  292. fireFlag = 0;
  293. if ~exist('wordValue', 'var')
  294. fprintf(2,'Error...\n');
  295. disp('Please specify a word value that needs to be detected.');
  296. disp('Example: detectDigInBit(128)');
  297. return;
  298. end
  299. if ~exist('timeWindow', 'var')
  300. timeWindow = 0.02;
  301. end
  302. while ~fireFlag
  303. pause(timeWindow);
  304. readWordValue = detectDigInWord(obj);
  305. if wordValue == readWordValue
  306. fireFlag = 1;
  307. end
  308. end
  309. end
  310. function fireFlag = detectWhenDigInBit(obj, bitsValue, timeWindow)
  311. fireFlag = 0;
  312. if ~exist('bitValue', 'var')
  313. fprintf(2,'Error...\n');
  314. disp('Please specify a bit pattern that needs to be detected.');
  315. disp('Example: detectDigInBit(13)');
  316. return;
  317. end
  318. if ~exist('timeWindow', 'var')
  319. timeWindow = 0.02;
  320. end
  321. while ~fireFlag
  322. pause(timeWindow);
  323. bitsValue = detectDigInBit(obj, bitDetect);
  324. if strcmpi(bitsValue, readWordValue)
  325. fireFlag = 1;
  326. end
  327. end
  328. end
  329. %% Get Configuration Values
  330. function configValue = getAllChannelLabels(obj)
  331. configStream = cbmex('chanlabel');
  332. configValue = configStream(:,1);
  333. end
  334. function configValue = getChannelLabel(obj, channel)
  335. configStream = cbmex('chanlabel', channel);
  336. configValue = configStream{1};
  337. end
  338. function isFlag = isChannelSpikeExtractionEnabled(obj, channel)
  339. configStream = cbmex('chanlabel', channel);
  340. isFlag = configStream{2};
  341. end
  342. function isFlag = isChannelUnitEnabled(obj, channel, unit)
  343. configStream = cbmex('chanlabel', channel);
  344. isFlag = configStream{unit};
  345. end
  346. function ampRejRange = getAmplitudeRejectionRange(obj, channel)
  347. configStream = cbmex('config', channel);
  348. ampRejRange = [configStream{12,2}, configStream{13,2}];
  349. end
  350. function setAmplitudeRejectionRange(obj, channel, newValue)
  351. if ~exist('newValue', 'var')
  352. fprintf(2,'Error...\n');
  353. disp('newValue is a required argument.');
  354. return;
  355. end
  356. if length(newValue)<2 || length(newValue)>2
  357. fprintf(2,'Error...\n');
  358. disp('The passed argument to this function should have two values: lower and higher amplitude rejection values, respectively.');
  359. return;
  360. end
  361. if newValue(1)>=newValue(2)
  362. fprintf(2,'Error...\n');
  363. disp('The low amplitude rejection value should be smaller than the high value.');
  364. return;
  365. end
  366. configStream = cbmex('config', channel, 'amplrejneg', newValue(1));
  367. configStream = cbmex('config', channel, 'amplrejpos', newValue(2));
  368. end
  369. function spikeThresh = getSpikeThresholdValue(obj, channel)
  370. configStream = cbmex('config', channel);
  371. spikeThresh = ceil(configStream{10, 2}/4);
  372. end
  373. function setSpikeThresholdValue(obj, channel, newValue)
  374. if ~exist('newValue', 'var')
  375. fprintf(2,'Error...\n');
  376. disp('newValue is a required argument.');
  377. return;
  378. else
  379. configStream = cbmex('config', channel, 'spkthrlevel', newValue * 4);
  380. end
  381. end
  382. function refChan = getDigitalReferenceChannel(obj, channel)
  383. configStream = cbmex('config', channel);
  384. refChan = configStream{14,2};
  385. end
  386. function refChan = setDigitalReferenceChannel(obj, channel, newValue)
  387. if ~exist('newValue', 'var')
  388. fprintf(2,'Error...\n');
  389. disp('newValue is a required argument.');
  390. return;
  391. else
  392. configStream = cbmex('config', channel, 'refelecchan', newValue);
  393. end
  394. end
  395. %% Data Analysis Methods
  396. function onlineSpectogram(obj, frequencyRange)
  397. if ~exist('frequencyRange', 'var')
  398. fprintf(2,'Error...\n');
  399. disp('You need to define a frequency range for this function.');
  400. disp('Example: onlineSpectrogram([0.3, 7500]) to show the spectrogram in the range of 0.3 Hz to 7500 Hz.');
  401. return;
  402. end
  403. frequencyRange = linspace(frequencyRange(1), frequencyRange(end), 150);
  404. % All durations are in second
  405. sampleCollectionDuration = 0.02;
  406. refreshRate = 0.05;
  407. % Setting up a default frequency range, if none is specified
  408. if ~exist('frequencyRange', 'var')
  409. disp('Frequency range was not specified. A default range of 0.3:1:7500 will be used.');
  410. frequencyRange = 0.3:1:7500;
  411. end
  412. % Setting up the figure
  413. procFigure = figure;
  414. set(procFigure, 'Name', 'Close this figure to stop');
  415. % Setting up collection variable
  416. timeDisplay = tic;
  417. timeCollection = tic;
  418. flagCollection = 1;
  419. % Collection
  420. dataCollectionStart(obj);
  421. while ishandle(procFigure)
  422. pause(0.001);
  423. if flagCollection
  424. timeCollectionET = toc(timeCollection);
  425. if timeCollectionET > sampleCollectionDuration
  426. [spikeData, startTime, contData] = dataCollectionReadBuffer(obj);
  427. nGraphs = size(contData,1);
  428. if ishandle(procFigure)
  429. for idx = 1:nGraphs
  430. fs0 = contData{idx, 2};
  431. data = contData{idx, 3};
  432. collectSize = min(size(data), sampleCollectionDuration * fs0);
  433. x = data(1:collectSize);
  434. if isempty(frequencyRange)
  435. [psd, f] = periodogram(double(x),[],'onesided',512,fs0);
  436. else
  437. [psd, f] = periodogram(double(x),[],frequencyRange,fs0);
  438. end
  439. subplot(nGraphs,1,idx,'Parent',procFigure);
  440. psdLog = 10*log10(psd);
  441. plot(f, psdLog, 'b');
  442. title(sprintf('fs = %d t = %f',fs0, startTime));
  443. xlabel('Frequency (Hz)'); xlim([frequencyRange(1), frequencyRange(end)]);
  444. ylabel('Magnitude (dB)');
  445. end
  446. drawnow;
  447. end
  448. flagCollection = 0;
  449. end
  450. end
  451. timeCollectionET = toc(timeDisplay);
  452. if timeCollectionET >= refreshRate;
  453. timeDisplay = tic;
  454. timeCollection = tic;
  455. flagCollection = 1;
  456. end
  457. end
  458. dataCollectionStop(obj);
  459. end
  460. end
  461. end