You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

p300-tactile-accumulator.lua 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. function arrayMax(a)
  2. if #a == 0 then return nil, nil end
  3. local maxIdx, maxValue = 0, a[0]
  4. for i = 1, (#a -1 ) do
  5. if maxValue < a[i] then
  6. maxIdx, maxValue = i, a[i]
  7. end
  8. end
  9. return maxIdx, maxValue
  10. end
  11. -- For handling target fifo
  12. List = {}
  13. function List.new ()
  14. return {first = 0, last = -1}
  15. end
  16. function List.pushright (list, value)
  17. local last = list.last + 1
  18. list.last = last
  19. list[last] = value
  20. end
  21. function List.popleft (list)
  22. local first = list.first
  23. if first > list.last then
  24. error("list is empty")
  25. end
  26. local value = list[first]
  27. list[first] = nil -- to allow garbage collection
  28. list.first = first + 1
  29. return value
  30. end
  31. function List.isempty (list)
  32. if list.first > list.last then
  33. return true
  34. else
  35. return false
  36. end
  37. end
  38. -- this function is called when the box is initialized
  39. function initialize(box)
  40. dofile(box:get_config("${Path_Data}") .. "/plugins/stimulation/lua-stimulator-stim-codes.lua")
  41. row_base = _G[box:get_setting(2)]
  42. n_tactilos = box:get_setting(3)
  43. segment_start = _G[box:get_setting(4)]
  44. segment_stop = _G[box:get_setting(5)]
  45. col_base = row_base + n_tactilos
  46. -- 0 inactive, 1 segment started, 2 segment stopped (can vote)
  47. segment_status = 0
  48. -- the idea is to push the flash states to the fifo, and when predictions arrive (with some delay), they are matched in oldest-first fashion.
  49. target_fifo = List.new()
  50. -- box:log("Info", string.format("pop %d %d", id[1], id[2]))
  51. row_votes = {}
  52. col_votes = {}
  53. do_debug = false
  54. end
  55. -- this function is called when the box is uninitialized
  56. function uninitialize(box)
  57. end
  58. function process(box)
  59. -- loops until box is stopped
  60. while box:keep_processing() do
  61. -- first, parse the timeline stream
  62. for stimulation = 1, box:get_stimulation_count(2) do
  63. -- gets the received stimulation
  64. local identifier, date, duration = box:get_stimulation(2, 1)
  65. -- discards it
  66. box:remove_stimulation(2, 1)
  67. if identifier == segment_start then
  68. if do_debug then
  69. box:log("Info", string.format("Trial start"))
  70. box:log("Info", string.format("Clear votes"))
  71. end
  72. -- zero the votes
  73. col_votes = {}
  74. row_votes = {}
  75. target_fifo = List.new()
  76. -- fixme fixed 20
  77. for i = 0,20 do
  78. col_votes[i] = 0
  79. row_votes[i] = 0
  80. end
  81. segment_status = 1
  82. end
  83. -- Does the identifier code a flash? if so, put into fifo
  84. if segment_status == 1 and identifier >= row_base and identifier <= OVTK_StimulationId_LabelEnd then
  85. -- assume rows before cols
  86. if identifier < col_base then
  87. local t = {"row", identifier - row_base}
  88. List.pushright(target_fifo,t)
  89. if do_debug then
  90. box:log("Info", string.format("Push row target %d", identifier - row_base ))
  91. end
  92. else
  93. local t = {"col", identifier - col_base}
  94. List.pushright(target_fifo,t)
  95. if do_debug then
  96. box:log("Info", string.format("Push col target %d", identifier - col_base ))
  97. end
  98. end
  99. end
  100. if identifier == segment_stop then
  101. if do_debug then
  102. box:log("Info", string.format("Trial stop"))
  103. end
  104. segment_status = 2
  105. end
  106. end
  107. -- then parse the classifications
  108. for stimulation = 1, box:get_stimulation_count(1) do
  109. -- gets the received stimulation
  110. local identifier, date, duration = box:get_stimulation(1, 1)
  111. -- discards it
  112. box:remove_stimulation(1, 1)
  113. -- Is it an in-class prediction?
  114. if identifier == OVTK_StimulationId_Target then
  115. local t = List.popleft(target_fifo)
  116. if do_debug then
  117. box:log("Info", string.format("Pred fifo %s %d is target", t[1], t[2]))
  118. end
  119. if t[1]=="row" then
  120. row_votes[t[2]] = row_votes[t[2]] + 1
  121. else
  122. col_votes[t[2]] = col_votes[t[2]] + 1
  123. end
  124. end
  125. if identifier == OVTK_StimulationId_NonTarget then
  126. local t = List.popleft(target_fifo)
  127. if do_debug then
  128. box:log("Info", string.format("Pred fifo %s %d is nontarget", t[1], t[2]))
  129. end
  130. end
  131. end
  132. if segment_status == 2 and List.isempty(target_fifo) then
  133. -- output the vote after the segment end when we've matched all predictions
  134. local maxRowIdx, maxRowValue = arrayMax(row_votes)
  135. local maxColIdx, maxColValue = arrayMax(col_votes)
  136. if maxRowValue == 0 and maxColValue == 0 then
  137. box:log("Warning", string.format("Classifier predicted 'no p300' for all flashes of the trial"));
  138. end
  139. if do_debug then
  140. local rowVotes = 0
  141. local colVotes = 0
  142. for ir, val in pairs(row_votes) do
  143. rowVotes = rowVotes + val
  144. end
  145. for ir, val in pairs(col_votes) do
  146. colVotes = colVotes + val
  147. end
  148. box:log("Info", string.format("Vote [%d %d] wt [%d,%d]", maxRowIdx+row_base, maxColIdx+col_base, maxRowValue, maxColValue))
  149. box:log("Info", string.format(" Total [%d %d]", rowVotes, colVotes))
  150. end
  151. local now = box:get_current_time()
  152. box:send_stimulation(1, maxRowIdx + row_base, now, 0)
  153. -- box:send_stimulation(2, maxColIdx + col_base, now, 0)
  154. segment_status = 0
  155. end
  156. box:sleep()
  157. end
  158. end