From f6ca2d979c3757223ded10a47c77fb1687f4d3ed Mon Sep 17 00:00:00 2001 From: tiborauer <tibor.auer@gmail.com> Date: Fri, 8 Apr 2022 14:38:56 +0100 Subject: [PATCH] INITIAL - multiblock --- rMtS_multiblock.py | 318 ++++++++++++++++++++++++++------------------- 1 file changed, 183 insertions(+), 135 deletions(-) diff --git a/rMtS_multiblock.py b/rMtS_multiblock.py index 21459e0..fe66c4d 100644 --- a/rMtS_multiblock.py +++ b/rMtS_multiblock.py @@ -1,6 +1,19 @@ """ Repetitive match-to-sample test A combination of Alekseichuk et al., 2016 (https://doi.org/10.1016/j.cub.2016.04.035) and Berger et al., 2019 (https://doi.org/10.1038/s41467-019-12057-0) + +conditionBlocks * nConditionBlock + onsetConditionBlock + frequency + trialBlocks * nTrialBlock + onsetTrialBlock + sampleTrials * nSample + onsetSample + sample + onsetSample + matchTrials * nMatch + match + onsetResponse """ from psychopy import visual, data, logging, gui, core, clock, monitors import os @@ -77,7 +90,6 @@ if __name__ == '__main__': 'frequency': 10, 'phase': 0, 'duration': - stimRamp+stimRamp+ # do not include rampUp and rampDown in the stimulation -array(trialBlockJitterRange).mean()+ # do not stimulate during first ISI_trialBlock nTrialBlock*( array(trialBlockJitterRange).mean()-array(sampleJitterRange).mean()+ @@ -206,8 +218,8 @@ if __name__ == '__main__': ])] sampleTrials[-1]['matchTrials'] = matchTrials trialBlocks[-1]['sampleTrials'] = sampleTrials - conditionBlocks[-1]['trialBlocks'] = trialBlocks conditionBlocks[-1]['frequency'] = frequencies[cb] + conditionBlocks[-1]['trialBlocks'] = trialBlocks loopConditionBlock = data.TrialHandler(nReps=1, method='sequential', trialList=conditionBlocks, autoLog=False) thisExp.addLoop(loopConditionBlock) @@ -295,14 +307,14 @@ if __name__ == '__main__': ######## RUN ######## # Main loop - for thisBlock in loopBlock: + for thisConditionBlock in loopConditionBlock: # Rest - if thisBlock.thisTrialN == 0: trialClock.reset() + if thisConditionBlock.thisTrialN == 0: trialClock.reset() else: trialClock.reset(-interimClock.getTime()) restStim.status = NOT_STARTED - while trialClock.getTime() < restDuration: + while trialClock.getTime() < thisConditionBlock['onsetConditionBlock']: # get current time t = trialClock.getTime() frameN = frameN + 1 # number of completed frames (so 0 is the first frame) @@ -320,78 +332,70 @@ if __name__ == '__main__': restStim.draw() win.flip() - interimClock.reset(restDuration-trialClock.getTime()) + interimClock.reset(thisConditionBlock['onsetConditionBlock']-trialClock.getTime()) if restStim.status == STARTED: restStim.status = STOPPED win.logOnFlip(level=logging.EXP, msg='Rest - STOPPED') - loopSample = data.TrialHandler(nReps=1, method='sequential', trialList=thisBlock['sampleTrials'], autoLog=False) - thisExp.addLoop(loopSample) - - if doSTIMULATION: - if thisBlock['frequency']: - wave1.frequency = thisBlock['frequency'] - wave2.frequency = thisBlock['frequency'] - BSO.initialize() - BSO.loadWaveform([wave1, wave2]) - BSO.stimulate() - logging.log(level=logging.DATA, msg='Stimulation - {:.3f} - Frequency: {}'.format(SSO.clock,BSO.waves[0].frequency)) - else: - logging.log(level=logging.DATA, msg='Stimulation - {:.3f} - Frequency: {}'.format(SSO.clock,0)) + loopTrialBlock = data.TrialHandler(nReps=1, method='sequential', trialList=thisConditionBlock['trialBlocks'], autoLog=False) + thisExp.addLoop(loopTrialBlock) - for thisSample in loopSample: - # Sample - trialClock.reset(-interimClock.getTime()) - jitter = thisSample['onsetSample'] + for thisTrialBlock in loopTrialBlock: + # trial block - sampleCoordinates = cellCoordinates[thisSample['sample'],:] - sampleForm = visual.ElementArrayStim(win, nElements=sampleCoordinates.shape[0], sizes=sampleSize, xys = sampleCoordinates, units='pix', - elementTex=None, elementMask="circle", colors=colour, colorSpace='rgb', autoLog=False) - sampleForm.status = NOT_STARTED + # ISI_trialblock + trialClock.reset(-interimClock.getTime()) + restStim.status = NOT_STARTED - while trialClock.getTime() < (jitter + sampleDuration): + while trialClock.getTime() < thisTrialBlock['onsetTrialBlock']: # get current time t = trialClock.getTime() frameN = frameN + 1 # number of completed frames (so 0 is the first frame) - # update/draw components on each frame - gridForm.draw() - # *sampleForm* updates - if t >= jitter and sampleForm.status == NOT_STARTED: + if restStim.status == NOT_STARTED: # keep track of start time/frame for later - sampleForm.tStart = t - sampleForm.frameNStart = frameN # exact frame index - sampleForm.status = STARTED - win.logOnFlip(level=logging.EXP, msg='Sample - STARTED - ' + array2string(thisSample['sample'])) - if doTrigger: trigger.send(TRIGGER['sample']) + restStim.tStart = t + restStim.frameNStart = frameN # exact frame index + restStim.status = STARTED + win.logOnFlip(level=logging.EXP, msg='Rest - STARTED') + if doTrigger: trigger.send(TRIGGER['rest']) - if sampleForm.status == STARTED: - sampleForm.draw() + if restStim.status == STARTED: + restStim.draw() win.flip() - interimClock.reset((jitter + sampleDuration) - trialClock.getTime()) - - if sampleForm.status == STARTED: - sampleForm.status = STOPPED - win.logOnFlip(level=logging.EXP, msg='Sample - STOPPED') - - # Match - loopMatch = data.TrialHandler(nReps=1, method='sequential', trialList=thisSample['matchTrials'], autoLog=False) - thisExp.addLoop(loopMatch) - for thisMatch in loopMatch: - # Recall + interimClock.reset(thisTrialBlock['onsetTrialBlock']-trialClock.getTime()) + + if restStim.status == STARTED: + restStim.status = STOPPED + win.logOnFlip(level=logging.EXP, msg='Rest - STOPPED') + + # Stimulation + if doSTIMULATION and thisTrialBlock.thisTrialN == 0: + if thisConditionBlock['frequency']: + wave1.frequency = thisConditionBlock['frequency'] + wave2.frequency = thisConditionBlock['frequency'] + BSO.initialize() + BSO.loadWaveform([wave1, wave2]) + BSO.stimulate() + logging.log(level=logging.DATA, msg='Stimulation - {:.3f} - Frequency: {}'.format(SSO.clock,BSO.waves[0].frequency)) + else: + logging.log(level=logging.DATA, msg='Stimulation - {:.3f} - Frequency: {}'.format(SSO.clock,0)) + + loopSample = data.TrialHandler(nReps=1, method='sequential', trialList=thisTrialBlock['sampleTrials'], autoLog=False) + thisExp.addLoop(loopSample) + + for thisSample in loopSample: + # Sample trialClock.reset(-interimClock.getTime()) - if loopMatch.thisTrialN == 0: - jitter = thisSample['onsetMatch'] - fullForm.status = NOT_STARTED - else: jitter = 0 + jitter = thisSample['onsetSample'] - sampleCoordinates = cellCoordinates[thisMatch['match'],:] - recallForm = visual.ElementArrayStim(win, nElements=sampleCoordinates.shape[0], sizes=sampleSize, xys = sampleCoordinates, units='pix', + sampleCoordinates = cellCoordinates[thisSample['sample'],:] + sampleForm = visual.ElementArrayStim(win, nElements=sampleCoordinates.shape[0], sizes=sampleSize, xys = sampleCoordinates, units='pix', elementTex=None, elementMask="circle", colors=colour, colorSpace='rgb', autoLog=False) - recallForm.status == NOT_STARTED + sampleForm.status = NOT_STARTED while trialClock.getTime() < (jitter + sampleDuration): # get current time @@ -400,98 +404,142 @@ if __name__ == '__main__': # update/draw components on each frame gridForm.draw() - - # fullForm - if jitter: - if t < fillerDuration and fullForm.status == NOT_STARTED: - fullForm.tStart = t - fullForm.frameNStart = frameN # exact frame index - fullForm.status = STARTED - win.logOnFlip(level=logging.EXP, msg='Mask - STARTED - ' + array2string(thisMatch['match'])) - if doTrigger: trigger.send(TRIGGER['mask']) - - if t >= fillerDuration and fullForm.status == STARTED: - fullForm.status = STOPPED - win.logOnFlip(level=logging.EXP, msg='Mask - STOPPED') - - # *recallForm* updates - if t >= jitter and recallForm.status == NOT_STARTED: - recallForm.tStart = t - recallForm.frameNStart = frameN # exact frame index - recallForm.status = STARTED - win.logOnFlip(level=logging.EXP, msg='Match - STARTED - ' + array2string(thisMatch['match'])) - if doTrigger: trigger.send(TRIGGER['testBase']+loopMatch.thisTrialN+1) - if fullForm.status == STARTED: - fullForm.draw() - - if recallForm.status == STARTED: - recallForm.draw() + # *sampleForm* updates + if t >= jitter and sampleForm.status == NOT_STARTED: + # keep track of start time/frame for later + sampleForm.tStart = t + sampleForm.frameNStart = frameN # exact frame index + sampleForm.status = STARTED + win.logOnFlip(level=logging.EXP, msg='Sample - STARTED - ' + array2string(thisSample['sample'])) + if doTrigger: trigger.send(TRIGGER['sample']) + + if sampleForm.status == STARTED: + sampleForm.draw() win.flip() interimClock.reset((jitter + sampleDuration) - trialClock.getTime()) - if recallForm.status == STARTED: - recallForm.status = STOPPED - win.logOnFlip(level=logging.EXP, msg='Match - STOPPED') - - # Response - trialClock.reset(-interimClock.getTime()) - jitter = thisMatch['onsetResponse'] - - responseStim.status = NOT_STARTED - SSO.reset_buttons() + if sampleForm.status == STARTED: + sampleForm.status = STOPPED + win.logOnFlip(level=logging.EXP, msg='Sample - STOPPED') - while trialClock.getTime() < (jitter + responseDuration): - # get current time - t = trialClock.getTime() - frameN = frameN + 1 # number of completed frames (so 0 is the first frame) + # Match + loopMatch = data.TrialHandler(nReps=1, method='sequential', trialList=thisSample['matchTrials'], autoLog=False) + thisExp.addLoop(loopMatch) + for thisMatch in loopMatch: + # Recall + trialClock.reset(-interimClock.getTime()) + if loopMatch.thisTrialN == 0: + jitter = thisSample['onsetMatch'] + fullForm.status = NOT_STARTED + else: jitter = 0 + + sampleCoordinates = cellCoordinates[thisMatch['match'],:] + recallForm = visual.ElementArrayStim(win, nElements=sampleCoordinates.shape[0], sizes=sampleSize, xys = sampleCoordinates, units='pix', + elementTex=None, elementMask="circle", colors=colour, colorSpace='rgb', autoLog=False) + recallForm.status == NOT_STARTED + + while trialClock.getTime() < (jitter + sampleDuration): + # get current time + t = trialClock.getTime() + frameN = frameN + 1 # number of completed frames (so 0 is the first frame) + + # update/draw components on each frame + gridForm.draw() + + # fullForm + if jitter: + if t < fillerDuration and fullForm.status == NOT_STARTED: + fullForm.tStart = t + fullForm.frameNStart = frameN # exact frame index + fullForm.status = STARTED + win.logOnFlip(level=logging.EXP, msg='Mask - STARTED - ' + array2string(thisMatch['match'])) + if doTrigger: trigger.send(TRIGGER['mask']) + + if t >= fillerDuration and fullForm.status == STARTED: + fullForm.status = STOPPED + win.logOnFlip(level=logging.EXP, msg='Mask - STOPPED') + + # *recallForm* updates + if t >= jitter and recallForm.status == NOT_STARTED: + recallForm.tStart = t + recallForm.frameNStart = frameN # exact frame index + recallForm.status = STARTED + win.logOnFlip(level=logging.EXP, msg='Match - STARTED - ' + array2string(thisMatch['match'])) + if doTrigger: trigger.send(TRIGGER['testBase']+loopMatch.thisTrialN+1) + + if fullForm.status == STARTED: + fullForm.draw() - # update/draw components on each frame - gridForm.draw() - - # *sampleForm* updates - if t >= jitter and responseStim.status == NOT_STARTED: - # keep track of start time/frame for later - responseStim.tStart = t - responseStim.frameNStart = frameN # exact frame index - responseStim.setAutoDraw(True) - win.logOnFlip(level=logging.EXP, msg='Response - STARTED') - if doTrigger: trigger.send(TRIGGER['responseBase']+loopMatch.thisTrialN+1) - SSO.wait_for_button(no_block=True) + if recallForm.status == STARTED: + recallForm.draw() - win.flip() - interimClock.reset((jitter + responseDuration)-trialClock.getTime()) + win.flip() + interimClock.reset((jitter + sampleDuration) - trialClock.getTime()) - if responseStim.status == STARTED: - responseStim.status = STOPPED - responseStim.setAutoDraw(False) - win.logOnFlip(level=logging.EXP, msg='Response - STOPPED') - - if len(SSO.buttonpresses): # no - SSO.buttonpresses[-1][0] = bNo; yes - SSO.buttonpresses[-1][0] = bYes - logging.log(level=logging.EXP, msg='Button - {:.3f} - {}'.format(SSO.buttonpresses[-1][1],SSO.buttonpresses[-1][0])) - thisExp.addData('resp.key',SSO.buttonpresses[-1][0]) - thisExp.addData('resp.rt',SSO.buttonpresses[-1][1]-(SSO.clock-trialClock.getTime())-responseStim.tStart) + if recallForm.status == STARTED: + recallForm.status = STOPPED + win.logOnFlip(level=logging.EXP, msg='Match - STOPPED') - if expInfo['match type'] == 'single': - isMatch = isin(thisMatch['match'],thisSample['sample']) - elif expInfo['match type'] == 'multi': - isMatch = all(thisMatch['match'] == thisSample['sample']) + # Response + trialClock.reset(-interimClock.getTime()) + jitter = thisMatch['onsetResponse'] + + responseStim.status = NOT_STARTED + SSO.reset_buttons() - if isMatch and (SSO.buttonpresses[-1][0] == bYes): - thisExp.addData('resp.code','hit') - if doTrigger: trigger.send(TRIGGER['responseCorrect']) - elif not(isMatch) and (SSO.buttonpresses[-1][0] == bNo): - thisExp.addData('resp.code','cr') - if doTrigger: trigger.send(TRIGGER['responseCorrect']) - else: - thisExp.addData('resp.code','false') - if doTrigger: trigger.send(TRIGGER['responseIncorrect']) + while trialClock.getTime() < (jitter + responseDuration): + # get current time + t = trialClock.getTime() + frameN = frameN + 1 # number of completed frames (so 0 is the first frame) - else: thisExp.addData('resp.code','miss') - thisExp.nextEntry() + # update/draw components on each frame + gridForm.draw() + + # *sampleForm* updates + if t >= jitter and responseStim.status == NOT_STARTED: + # keep track of start time/frame for later + responseStim.tStart = t + responseStim.frameNStart = frameN # exact frame index + responseStim.setAutoDraw(True) + win.logOnFlip(level=logging.EXP, msg='Response - STARTED') + if doTrigger: trigger.send(TRIGGER['responseBase']+loopMatch.thisTrialN+1) + SSO.wait_for_button(no_block=True) + + win.flip() + interimClock.reset((jitter + responseDuration)-trialClock.getTime()) + + if responseStim.status == STARTED: + responseStim.status = STOPPED + responseStim.setAutoDraw(False) + win.logOnFlip(level=logging.EXP, msg='Response - STOPPED') + + if len(SSO.buttonpresses): # no - SSO.buttonpresses[-1][0] = bNo; yes - SSO.buttonpresses[-1][0] = bYes + logging.log(level=logging.EXP, msg='Button - {:.3f} - {}'.format(SSO.buttonpresses[-1][1],SSO.buttonpresses[-1][0])) + thisExp.addData('resp.key',SSO.buttonpresses[-1][0]) + thisExp.addData('resp.rt',SSO.buttonpresses[-1][1]-(SSO.clock-trialClock.getTime())-responseStim.tStart) + + if expInfo['match type'] == 'single': + isMatch = isin(thisMatch['match'],thisSample['sample']) + elif expInfo['match type'] == 'multi': + isMatch = all(thisMatch['match'] == thisSample['sample']) + + if isMatch and (SSO.buttonpresses[-1][0] == bYes): + thisExp.addData('resp.code','hit') + if doTrigger: trigger.send(TRIGGER['responseCorrect']) + elif not(isMatch) and (SSO.buttonpresses[-1][0] == bNo): + thisExp.addData('resp.code','cr') + if doTrigger: trigger.send(TRIGGER['responseCorrect']) + else: + thisExp.addData('resp.code','false') + if doTrigger: trigger.send(TRIGGER['responseIncorrect']) + + else: thisExp.addData('resp.code','miss') + thisExp.nextEntry() # Rest + restDuration = conditionBlockJitter[0] # final rest based on first trialClock.reset(-interimClock.getTime()) restStim.status = NOT_STARTED -- GitLab