-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMagstimExtension.py
151 lines (131 loc) · 7.68 KB
/
MagstimExtension.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#===============================================================================
# Sets the stimulus parameters during the intertrial phase.
# Application cannot advance to response from task if MSReqStimReady and it isn't ready.
# Triggers the stimulus as soon as the response phase is entered.
#===============================================================================
import numpy as np
import random
import time
import pygame, pygame.locals
from AppTools.StateMonitors import addstatemonitor
class MagstimApp(object):
params = [
"PythonApp:Magstim int MSEnable= 0 0 0 1 // Enable: 0 no, 1 yes (boolean)",
"PythonApp:Magstim string MSSerialPort= COM4 % % % // Serial port for controlling Magstim",
"PythonApp:Magstim int MSTriggerType= 0 0 0 2 // Trigger by: 0 SerialPort, 1 Contec1, 2 Contec2 (enumeration)",
"PythonApp:Magstim float MSDelay= 0.0 0.0 0.0 % // Time ms delay from criterion met to stim",
"PythonApp:Magstim int MSReqStimReady= 0 0 0 1 // Require ready response to trigger: 0 no, 1 yes (boolean)",
"PythonApp:Magstim float MSISIMin= 6 6 2 % // Minimum time s between stimuli",
"PythonApp:Magstim intlist MSIntensityA= 1 50 0 0 100 // TS if single-pulse, CS if double-pulse",
"PythonApp:Magstim intlist MSIntensityB= 1 0 0 0 100 // TS if double-pulse",
"PythonApp:Magstim floatlist MSPulseInterval= 1 0 0 0 999 // Double-pulse interval in ms",
]
states = [
"MagstimReady 1 0 0 0", #Whether or not the magstim returns ready
"MSIntensityA 16 0 0 0", #Intensity of StimA
"MSIntensityB 16 0 0 0", #Intensity of StimA
"ISIx10 16 0 0 0", #Double-pulse ISI in 0.1ms
]
@classmethod
def preflight(cls, app, sigprops):
if int(app.params['MSEnable'])==1:
app.magstimA = app.params['MSIntensityA'].val if len(app.params['MSIntensityA']) == app.nclasses else [app.params['MSIntensityA'].val[0] for x in range(app.nclasses)]
app.magstimB = app.params['MSIntensityB'].val if len(app.params['MSIntensityB']) == app.nclasses else [app.params['MSIntensityB'].val[0] for x in range(app.nclasses)]
app.magstimISI = app.params['MSPulseInterval'].val if len(app.params['MSPulseInterval']) == app.nclasses else [app.params['MSPulseInterval'].val[0] for x in range(app.nclasses)]
@classmethod
def initialize(cls, app, indim, outdim):
if int(app.params['MSEnable'])==1:
from magstim_python.Magstim.MagstimInterface import Bistim
serPort=app.params['MSSerialPort'].val
trigType=int(app.params['MSTriggerType'])
if trigType==0: #Use serial port trigger
app.trigbox=None
app.magstim=Bistim(port=serPort, stimDelay=app.params['MSDelay'].val)
else: #Use contec trigger
if not (hasattr(app,'trigbox') and app.trigbox): #Don't double up on the trigbox if RE-initializing
from caio_python.Caio.TriggerBox import TTL
app.trigbox=TTL(channel=trigType)
app.trigbox.set_TTL(channel=trigType, amplitude=5, width=2.5, offset=app.params['MSDelay'].val)
app.magstim=Bistim(port=serPort, trigbox=app.trigbox)
#app.intensity_detail_name = 'dat_TMS_powerA'
app.magstim.remocon = True
app.magstim.intensity = app.magstimA[0]
app.magstim.intensityb = app.magstimB[0]
app.magstim.ISI = app.magstimISI[0]
#app.magstim.armed = True
app.magstim.remocon = False
if int(app.params['ShowSignalTime']):
app.addstatemonitor('MagstimReady')
app.addstatemonitor('MSIntensityA')
app.addstatemonitor('MSIntensityB')
app.addstatemonitor('ISIx10')
@classmethod
def halt(cls,app):
if hasattr(app, 'magstim') and int(app.params['MSEnable'])==1:
#Clear magstim from memory, which will also clear the serial port.
app.magstim.__del__()
@classmethod
def startrun(cls,app):
if int(app.params['MSEnable'])==1:
app.forget('tms_trig')#Pretend that there was a stimulus at time 0 so that the min ISI check works on the first trial.
@classmethod
def stoprun(cls,app):
if int(app.params['MSEnable'])==1: pass
@classmethod
def transition(cls,app,phase):
if int(app.params['MSEnable'])==1:
if phase == 'intertrial':
app.magstim.remocon = False
elif phase == 'baseline':
t = app.target_codes[app.states['CurrentTrial']-1] - 1
app.magstim.remocon = True
app.magstim.set_stimarm(False)
newISI = app.magstimISI[t] if app.magstim.ISI in app.magstimISI else app.magstim.ISI
app.magstim.set_isi(newISI)
app.magstim.intensity = app.magstimA[t] if app.magstim.intensity in app.magstimA else app.magstim.intensity
new_intensityb = app.magstimB[t]
app.magstim.set_stimb(new_intensityb)
app.magstim.set_stimarm(True)
app.magstim.remocon = False
elif phase == 'gocue':
app.magstim.remocon = True
app.magstim.remocon = False
elif phase == 'task':
app.magstim.remocon = True
app.magstim.remocon = False
elif phase == 'response':
app.magstim.remocon = True
#app.magstim.armed = True
app.magstim.trigger()
app.states['MSIntensityA'] = app.magstim.intensity
app.states['MSIntensityB'] = app.magstim.intensityb
app.states['ISIx10'] = 10*app.magstim.ISI
app.remember('tms_trig')
app.magstim.armed = False
elif phase == 'stopcue':
pass
@classmethod
def process(cls,app,sig):
if int(app.params['MSEnable'])==1:
#Arm the coil if it is not armed and should be.
if not app.magstim.armed and not (app.in_phase('response') or app.in_phase('stopcue')):
#We avoid arming in response or stopcue so the arming artifact doesn't affect the ERP.
if not app.magstim.remocon: app.magstim.remocon = True
app.magstim.armed = True
app.magstim.remocon = False#Toggle remocon so that we can manually adjust the intensity.
####################################
# Update the StimulatorReady state #
####################################
stim_ready = app.magstim.armed if not app.params['MSReqStimReady'].val else (app.magstim.ready and app.magstim.armed)
#stim_ready = True #Use this for debugging
isiok = app.since('tms_trig')['msec'] >= 1000.0 * float(app.params['MSISIMin'])
app.states['MagstimReady'] = stim_ready and isiok
@classmethod
def event(cls, app, phasename, event):
if int(app.params['MSEnable'])==1 and event.type == pygame.locals.KEYDOWN and event.key in [pygame.K_UP, pygame.K_DOWN]:
#This has a pretty poor success rate. I wonder if it has to do with toggling the remocon state.
if not app.magstim.remocon: app.magstim.remocon = True
if event.key == pygame.K_UP: app.magstim.intensity = app.magstim.intensity + 1
if event.key == pygame.K_DOWN: app.magstim.intensity = app.magstim.intensity - 1
app.magstim.remocon = False
print ("magstim intensity " + str(app.magstim.intensity))