ISP_MoBi_sequence_MEP.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. function [forces]=ISP_MoBi_sequence(contraction_task, data_subject, volume, forces, bl)
  2. cd 'C:\Users\neuro\Desktop\script_mobi_da_commentare'
  3. sca;
  4. close all;
  5. %%
  6. % consider left hand state
  7. switch contraction_task{1,2} % force as a proportion of the outer circle...
  8. case 'Contratta'
  9. LEFT = [0.2 0.4];
  10. istruzione_sx = 'Mantieni\nil cerchietto\nverde';
  11. case 'Rilassata'
  12. LEFT = [0 0.01];
  13. istruzione_sx = 'Rilassa'
  14. end
  15. % consider right hand state (leftover from previous usage of the script)
  16. switch contraction_task{1,3}
  17. case 'Contratta'
  18. RIGHT = [0.2 0.4];
  19. istruzione_dx = 'Mantieni\nil cerchietto\nverde';
  20. case 'Rilassata'
  21. RIGHT = [0.0 0.01];
  22. istruzione_dx = 'Rilassa'
  23. end
  24. %% specify trigger number acccording to the parameters used
  25. switch contraction_task{1,1}
  26. case 'Mono_AP'
  27. TMS_trigger = 3;
  28. case 'Mono_PA'
  29. TMS_trigger = 5;
  30. case 'Mono_LM'
  31. TMS_trigger = 7;
  32. case 'Bi_PA_contracted'
  33. TMS_trigger = 160;
  34. case 'Bi_AP'
  35. TMS_trigger = 130;
  36. case 'Bi_PA'
  37. TMS_trigger = 150;
  38. case 'Bi_LM'
  39. TMS_trigger = 170;
  40. end
  41. %%
  42. % as a proportion to y axis...
  43. outer_dimension = 0.4;
  44. % inner and middle dimension of the left circle
  45. inner_dimensionL = LEFT(1);
  46. middle_dimensionL = LEFT(2);
  47. % leftover from previous usage of the script
  48. inner_dimensionR = RIGHT(1);
  49. middle_dimensionR = RIGHT(2);
  50. inner_force_limit_propL = inner_dimensionL;
  51. outer_force_limit_propL = middle_dimensionL;
  52. % leftover from previous usage of the script
  53. inner_force_limit_propR = inner_dimensionR;
  54. outer_force_limit_propR = middle_dimensionR;
  55. outer_circle_color = [1 1 1]; % white
  56. middle_circle_color = [0.5 0.5 0.5]; % grey
  57. inner_circle_color = [0 0 0]; % black
  58. variable_circle_colorL = [0 1 0]; % green
  59. % leftover from previous usage of the script
  60. variable_circle_colorR = [0 1 0]; %green
  61. variable_circle_thickness = 5;
  62. force_steps = 500;
  63. num_trials = 20;
  64. estimation_time = 10; %secs
  65. time_x_avg = 0.5; % secs
  66. %% DO you want to tinker with small screen?
  67. tinker = 0;
  68. HideCursor
  69. %% SCREEN SETTING
  70. [window, misure_schermo, screenNum, CenterX, CenterY, ifi] = MoBi_Screen_Settings_Brescia(tinker);
  71. %%
  72. delete(instrfindall);
  73. clear s
  74. %Set up the serial port
  75. s = serial('COM5'); % this depends on the COM where arduino is connected
  76. set(s, 'BaudRate', 115200); % set BaudRate to 115200 (as in Arduino!!!!!)
  77. % open it
  78. fopen(s);
  79. WaitSecs(2);
  80. for d = 1:2
  81. MoBi_scrittura(s);
  82. end
  83. %% % set the delays properly for Psychtoolbox
  84. [ifi_af, ifi_as, ifi_nf, ifi_ns] = MoBI_frames_Brescia(ifi, ifi);
  85. [onesec_af, onesec_as, onesec_nf, onesec_ns] = MoBI_frames_Brescia(1, ifi);
  86. %% parallel port initialization
  87. ioObj = io64;
  88. status = io64(ioObj);
  89. address = hex2dec('DFB8'); %select LTP1 output port address
  90. io64(ioObj, address ,0); %set Trigger to zero where it allready should be!!
  91. %%
  92. % no TMS delivery within 0.4s from former one
  93. delay_after_TMS = 0.4;
  94. % stimulation is delivered randomly between 4 and 6 seconds
  95. TMS_extremes = [4.4 6.4]-delay_after_TMS;
  96. % set the random intervals for TMS delivery
  97. TMS_interval_tmp = (TMS_extremes(2) - TMS_extremes(1))*rand(num_trials,1)+TMS_extremes(1);
  98. % set the TMS timings properly for Psychtoolbox
  99. [TMS_int_af, TMS_int_as, TMS_int_nf, TMS_int_ns] = MoBI_frames_Brescia(TMS_interval_tmp, ifi);
  100. %% get max force if block 1
  101. %
  102. if bl == 1
  103. [max_force_left, max_force_right] = ISP_MoBi_get_maximal_force(window, misure_schermo, ifi, s, time_x_avg,estimation_time);
  104. % set the estimated force
  105. forces = struct;
  106. forces.max_force_left= max_force_left;
  107. % leftover from previous usage of the script
  108. forces.max_force_right = max_force_right;
  109. else
  110. % if bl>1 then, this number is estimated on the first block of each session
  111. max_force_left = forces.max_force_left;
  112. % leftover from previous usage of the script
  113. max_force_right = forces.max_force_right;
  114. end
  115. %% the systems outputs a "-1/force" function
  116. % --> set the max of the function to max_force and the min to 0
  117. % in steps of 500 values of forces (this can be changed)
  118. % from 1 to 10 in steps of force_steps (500)
  119. x = linspace(1,10,force_steps);
  120. % produce the shape
  121. funz_tmp = (-1./x)+1;
  122. % the shape is morphed so that it goes from 0 to 1 and multuplied by
  123. % max_force
  124. funzL = (funz_tmp./(funz_tmp(end)))*max_force_left;
  125. % leftover from previous usage of the script
  126. funzR = (funz_tmp./(funz_tmp(end)))*max_force_right;
  127. % this is the linear function with range [0 max_force_left] which will be
  128. % helpful afterwards to linearize the voltage readings
  129. linearizeL = linspace(0, max_force_left, force_steps);
  130. % leftover from previous usage of the script
  131. linearizeR = linspace(0, max_force_right, force_steps);
  132. %% SET ALL THE VISUALS on the LEFT
  133. % proportion with respect to the entire screen
  134. set_proportion_of_diameter_outer_circleL = outer_dimension;
  135. prop_diameter_outer_circleL = set_proportion_of_diameter_outer_circleL;
  136. % transform proportions to actual pixels --> we get diameter of outer
  137. % circle in pixels
  138. dimensions_outer_circleL = MoBi_prop2dim(misure_schermo, 0, prop_diameter_outer_circleL);
  139. % same procedure for inner and middle circle calculated on the basis of the outer
  140. % circle
  141. prop_diameter_inner_circleL = inner_dimensionL*prop_diameter_outer_circleL;
  142. dimensions_inner_circleL = MoBi_prop2dim(misure_schermo, 0, prop_diameter_inner_circleL);
  143. prop_diameter_middle_circleL = middle_dimensionL*prop_diameter_outer_circleL;
  144. dimensions_middle_circleL = MoBi_prop2dim(misure_schermo, 0, prop_diameter_middle_circleL);
  145. %% generate a structure for the LEFT circle with the features of the circle as fields
  146. % we have two objsect now...place the left one on the first quarter of the
  147. % screen length, the other on the last quarter
  148. first_quarter = 3*misure_schermo(3)/8;
  149. last_quarter = 5*misure_schermo(3)/8;
  150. % ...color
  151. ObL.outer_circle.color = outer_circle_color;
  152. % ...diameter
  153. ObL.outer_circle.dimension = [0 0 dimensions_outer_circleL(2) dimensions_outer_circleL(2)];
  154. % center position on the screen
  155. baseRect_outer = [ObL.outer_circle.dimension];
  156. % place in the first quarter of the x axis
  157. ObL.outer_circle.dimension = CenterRectOnPoint(baseRect_outer, first_quarter, misure_schermo(4)/2);% da che cosa dipendono questi numeri???
  158. % the same goes for the other circles
  159. ObL.inner_circle.color = inner_circle_color;
  160. ObL.inner_circle.dimension = [0 0 dimensions_inner_circleL(2) dimensions_inner_circleL(2)]
  161. baseRect_inner = [ObL.inner_circle.dimension];
  162. % place in the first quarter of the x axis
  163. ObL.inner_circle.dimension = CenterRectOnPoint(baseRect_inner, first_quarter, misure_schermo(4)/2);
  164. ObL.middle_circle.color = middle_circle_color;
  165. ObL.middle_circle.dimension = [0 0 dimensions_middle_circleL(2) dimensions_middle_circleL(2)];
  166. baseRect_middle = [ObL.middle_circle.dimension];
  167. % place in the first quarter
  168. ObL.middle_circle.dimension = CenterRectOnPoint(baseRect_middle, first_quarter, misure_schermo(4)/2);
  169. %% SET ALL THE VISUALS on the RIGHT (LEFTOVER!)
  170. % proportion with respect to the entire screen
  171. set_proportion_of_diameter_outer_circleR = outer_dimension;
  172. prop_diameter_outer_circleR = [set_proportion_of_diameter_outer_circleR];
  173. % transform proportions to actual pixels --> we get dimaeter of outer
  174. % circle in pixels
  175. dimensions_outer_circleR = MoBi_prop2dim(misure_schermo, 0, prop_diameter_outer_circleR);
  176. % same procedure for inner and middle circle calculated on the basis of the outer
  177. % circle
  178. prop_diameter_inner_circleR = inner_dimensionR*prop_diameter_outer_circleR;
  179. dimensions_inner_circleR = MoBi_prop2dim(misure_schermo, 0, prop_diameter_inner_circleR);
  180. prop_diameter_middle_circleR = middle_dimensionR*prop_diameter_outer_circleR;
  181. dimensions_middle_circleR = MoBi_prop2dim(misure_schermo, 0, prop_diameter_middle_circleR);
  182. %% generate a structure for the RIGHT circle with the features of the circle as fields (LEFTOVER!)
  183. % ...color
  184. ObR.outer_circle.color = outer_circle_color;
  185. % ...diameter
  186. ObR.outer_circle.dimension = [0 0 dimensions_outer_circleR(2) dimensions_outer_circleR(2)];
  187. % center position on the screen;
  188. baseRect_outer = [ObR.outer_circle.dimension];
  189. % place in the first quarter
  190. ObR.outer_circle.dimension = CenterRectOnPoint(baseRect_outer, last_quarter, misure_schermo(4)/2);% da che cosa dipendono questi numeri???
  191. % same goes for the other circles
  192. ObR.inner_circle.color = inner_circle_color;
  193. ObR.inner_circle.dimension = [0 0 dimensions_inner_circleR(2) dimensions_inner_circleR(2)]
  194. baseRect_inner = [ObR.inner_circle.dimension];
  195. % place in the last quarter
  196. ObR.inner_circle.dimension = CenterRectOnPoint(baseRect_inner, last_quarter, misure_schermo(4)/2);
  197. ObR.middle_circle.color = middle_circle_color;
  198. ObR.middle_circle.dimension = [0 0 dimensions_middle_circleR(2) dimensions_middle_circleR(2)]
  199. baseRect_middle = [ObR.middle_circle.dimension];
  200. % place in the last quarter
  201. ObR.middle_circle.dimension = CenterRectOnPoint(baseRect_middle, last_quarter, misure_schermo(4)/2);
  202. %% we also have a black background.
  203. Bk.color = [0 0 0];
  204. Bk.dimension = misure_schermo;
  205. %% resolution setting:
  206. % every time participant presses, read values change. here we set the boundary values to which a variable circle will change its diameter based on the pressure produced
  207. % create a vector from 0 to diameter of outer circle in steps of
  208. % length(x)
  209. diameter_values_nonscaled_tmp = linspace(0, dimensions_outer_circleL(2), length(x));
  210. % in case participants press more than what they did in the max_force
  211. % evaluation, values bigger than outer_circle diameter are added, in order
  212. % not to get errors.
  213. % It should not happen though because it would mean the max_force
  214. % estimatimation was performed in an unproper way.
  215. diameter_values_nonscaled = [diameter_values_nonscaled_tmp ...
  216. diameter_values_nonscaled_tmp(end)+[1:100].*diff(diameter_values_nonscaled_tmp(1:2))];
  217. %% INSTRUCTIONS FOR the researcher
  218. Screen('FillRect', window, Bk.color, Bk.dimension);
  219. Screen('TextSize', window, 25);
  220. hor_alignement = round(misure_schermo(3)*0.5,0);
  221. ver_alignement = round(misure_schermo(4)*0.25,0);
  222. TMS_cond = contraction_task{1,1};
  223. DrawFormattedText(window,TMS_cond, hor_alignement,...
  224. ver_alignement, [1 1 1]);
  225. clear hor_alignement ver_alignement
  226. testo_sx = contraction_task{1,2};
  227. hor_alignement = round(misure_schermo(3)*0.25,0);
  228. ver_alignement = round(misure_schermo(4)*0.75,0);
  229. DrawFormattedText(window,testo_sx, hor_alignement,...
  230. ver_alignement, [1 1 1]);
  231. clear hor_alignement ver_alignement
  232. testo_dx = contraction_task{1,3};
  233. hor_alignement = round(misure_schermo(3)*0.75,0);
  234. ver_alignement = round(misure_schermo(4)*0.75,0);
  235. DrawFormattedText(window,testo_dx, hor_alignement,...
  236. ver_alignement, [1 1 1]);
  237. clear hor_alignement ver_alignement
  238. [VBL]=Screen(window, 'Flip', ifi, 1);
  239. % wait for keyboard press
  240. WaitSecs(2)
  241. %%
  242. while ~KbCheck
  243. end
  244. %% start noise (the volume has been adjusted to zero and the wav file played through an ipod)...so...leftover
  245. [audiodata, infreq] = psychwavread('AirCool_Magstim_48000.wav');
  246. noise_seconds = 60*5; % ten minutes
  247. noise_short = audiodata(1:infreq*noise_seconds)';
  248. pasound2 = PsychPortAudio('Open', []);
  249. sound2 = [noise_short; noise_short];
  250. PsychPortAudio('Volume', pasound2, volume);
  251. PsychPortAudio('FillBuffer', pasound2, sound2);
  252. noise_ts = PsychPortAudio('Start',pasound2, 1, 1);
  253. %% linearization of the force:
  254. % the response of the pressure sensor is not linear
  255. % the decrease of resistance is not proportional to the force
  256. % applied to it
  257. % so we want to map the non-linear (-1/force) output across the fR to a
  258. % linear one, this way a fixed amount of force increase will correspond to a linear increase in the detected value
  259. % the linearization mapping will be applied while acquiring thanks to the
  260. % following mapping
  261. % funz is the non linear output from the reading
  262. % linearize = linspace(0, max_force, force_steps);
  263. %
  264. % idx = find(force<=funz, 1, 'first');
  265. %
  266. % force_lin = linearize(idx);
  267. %% RUN TIME ACTUAL EXPERIMENT
  268. Screen('FillRect', window, [0 0 0], misure_schermo);
  269. [VBL]=Screen(window, 'Flip', ifi, 1);
  270. tic
  271. on_spot = 0;
  272. for tr = 1:num_trials
  273. i = 0;
  274. while 1
  275. % read from serial port what arduino has written
  276. force_tmp = MoBi_scrittura(s);
  277. % split the 3 values that are read from the three pins in arduino
  278. force_tmp2 = (strsplit(force_tmp, '_'));
  279. % GET INFO ONLY FROM THE A0 PIN, which is the first read
  280. force_tmpL = str2double(force_tmp2{1});
  281. % leftover from previous usage of the script
  282. force_tmpR = str2double(force_tmp2{2});
  283. % force linearization.
  284. [linear_forceL idxL] = MoBi_linearize_force(force_tmpL, linearizeL, funzL);
  285. % leftover from previous usage of the script
  286. [linear_forceR idxR] = MoBi_linearize_force(force_tmpR, linearizeR, funzR);
  287. %%
  288. diameter_valuesL = diameter_values_nonscaled(idxL);
  289. % leftover from previous usage of the script
  290. diameter_valuesR = diameter_values_nonscaled(idxR);
  291. if on_spot == 0
  292. % draw left side circles in background
  293. ObL.variable_circle.color = variable_circle_colorL;
  294. ObL.variable_circle.dimension = [0 0 diameter_valuesL diameter_valuesL];
  295. baseRect_variable = [ObL.variable_circle.dimension];
  296. ObL.variable_circle.dimension = CenterRectOnPoint(baseRect_variable, first_quarter, misure_schermo(4)/2);
  297. Screen('FrameOval', window, ObL.outer_circle.color, ObL.outer_circle.dimension);
  298. Screen('FillOval', window, ObL.middle_circle.color, ObL.middle_circle.dimension);
  299. Screen('FillOval', window, ObL.inner_circle.color, ObL.inner_circle.dimension);
  300. Screen('FrameOval', window, ObL.variable_circle.color, ObL.variable_circle.dimension, variable_circle_thickness);
  301. % draw right side circles in background
  302. ObR.variable_circle.color = variable_circle_colorR;
  303. ObR.variable_circle.dimension = [0 0 diameter_valuesR diameter_valuesR];
  304. baseRect_variable = [ObR.variable_circle.dimension];
  305. ObR.variable_circle.dimension = CenterRectOnPoint(baseRect_variable, last_quarter, misure_schermo(4)/2);
  306. Screen('FrameOval', window, ObR.outer_circle.color, ObR.outer_circle.dimension);
  307. Screen('FillOval', window, ObR.middle_circle.color, ObR.middle_circle.dimension);
  308. Screen('FillOval', window, ObR.inner_circle.color, ObR.inner_circle.dimension);
  309. Screen('FrameOval', window, ObR.variable_circle.color, ObR.variable_circle.dimension, variable_circle_thickness);
  310. Screen('TextSize', window, 80);
  311. DrawFormattedText(window, '+', 'center','center', [1 1 1]);
  312. else
  313. Screen('FillRect', window, [0 0 0], misure_schermo);
  314. Screen('TextSize', window, 80);
  315. DrawFormattedText(window, '+', 'center','center', [1 1 1]);
  316. end
  317. % show circles on the screen
  318. [VBL] = Screen(window, 'Flip', VBL+ifi_as);
  319. % when tinkering show the diameter and forces values for each circle
  320. sprintf( '%f_%f_%f____%f_%f_%f', inner_force_limit_propL*max_force_left, linear_forceL, outer_force_limit_propL*max_force_left,...
  321. inner_force_limit_propR*max_force_right, linear_forceR, outer_force_limit_propR*max_force_right)
  322. %% check force range for TMS: if force is within the range for TMS delivery
  323. if (linear_forceL >= inner_force_limit_propL*max_force_left) && (linear_forceL <= outer_force_limit_propL*max_force_left)...
  324. && (linear_forceR >= inner_force_limit_propR*max_force_right) && (linear_forceR <= outer_force_limit_propR*max_force_right)
  325. i= i+1;
  326. % then do not show the circles, only the fixation cross
  327. on_spot = 1;
  328. Screen('FillRect', window, [0 0 0], misure_schermo);
  329. Screen('TextSize', window, 80);
  330. DrawFormattedText(window, '+', 'center','center', [1 1 1]);
  331. else % ... if force is out of range
  332. i = 0;
  333. on_spot = 0;
  334. % change colors of the circle that is out
  335. if (linear_forceL <= inner_force_limit_propL*max_force_left) || (linear_forceL >= outer_force_limit_propL*max_force_left)...
  336. variable_circle_colorL = [1 0 0];
  337. else
  338. variable_circle_colorL = [0 1 0];
  339. end
  340. if (linear_forceR <= inner_force_limit_propR*max_force_right) || (linear_forceR >= outer_force_limit_propR*max_force_right)
  341. variable_circle_colorR = [1 0 0];
  342. else
  343. variable_circle_colorR = [0 1 0];
  344. end
  345. end
  346. % in case i is equal or greater than the frames to be waited
  347. % for TMS to be delivered, then deliver TMS
  348. if i >= TMS_int_nf(tr)
  349. can_i_trigger = 1;
  350. % function implementing also trigger value
  351. MoBi_Trigger(ioObj,address,TMS_trigger, can_i_trigger);
  352. can_i_trigger = 0;
  353. TR.TMS_timing(tr) = VBL;
  354. TR.i(tr) = i;
  355. TR.ns(tr) = TMS_int_ns(tr);
  356. TR.nf(tr) = TMS_int_nf(tr);
  357. TR.tictoc(tr) = toc;
  358. disp(strcat('TMS', '_', num2str(tr)))
  359. WaitSecs(delay_after_TMS)
  360. % break the while loop and start another trial
  361. break
  362. end
  363. end
  364. end
  365. sca
  366. % leftover (see comments on volume)
  367. PsychPortAudio('Stop', pasound2,0);
  368. PsychPortAudio('Close');
  369. clear audiodata sound2 noise_short pasound2 sound2
  370. save(data_subject)
  371. end