-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassocLearnRig_app.py
More file actions
300 lines (269 loc) · 10.5 KB
/
assocLearnRig_app.py
File metadata and controls
300 lines (269 loc) · 10.5 KB
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
####This creates the GUI which subsequently instantiates all hardware objects
import PySimpleGUI as sg
from picamera import PiCamera
import RPi.GPIO as GPIO
import pivideostream as pvid
import cv2
import numpy as np
import time
import sys
import signal
from arduinoRig import arduinoRig
#For realtime visualizations
from matplotlib import cm
import matplotlib.pyplot as pl
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg
from matplotlib.figure import Figure
from random import randint
####### LED PWM SETUP #######
LED_PIN = 18 # BCM pin for LED
GPIO.setmode(GPIO.BCM) # use Broadcom pin numbering
GPIO.setup(LED_PIN, GPIO.OUT)
led_pwm = GPIO.PWM(LED_PIN, 1000) # 1 kHz PWM
led_pwm.start(0) # start at 0% duty (off)
# GLOBAL VARS/plotting functions
n2show = 4;
cmap_r = np.zeros((n2show,4));cmap_r[:,0]=np.linspace(0.1,1,n2show);cmap_r[:,3]=1#
cmap_e = np.zeros((n2show,4));cmap_e[:,1]=np.linspace(0.1,1,n2show);cmap_e[:,2]=np.linspace(0,1,1,n2show);cmap_e[:,3]=1
def draw_figure(canvas, figure, loc=(0, 0)):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg
def update_plot(ax,vls=np.nan,rot_time=np.nan,rot=np.nan,eb_time=np.nan,eb=np.nan,h=np.nan,vh=np.nan):#Adding whichever elements are available to the plot
#Handling vertical line stimulus plots
ymin,ymax = ax.get_ylim()
if not vh:
vh = [ax.vlines(vls[0],ymin,ymax,linestyle='--',color='black')]
vh.append(ax.vlines(vls[0]+vls[1]-vls[2],ymin,ymax,linestyle='--',color='black'))
vh.append(ax.vlines(vls[0]+vls[1],ymin,ymax,linestyle='--',color='black'))
if isinstance(rot, np.ndarray) and rot.size > 0:
rmax, rmin = rot.max(), rot.min()
if rmax > ymax or rmin < ymin:
[v.remove() for v in vh]
ymax, ymin = max(rmax, ymax), min(rmin, ymin)
vh = [ax.vlines(vls[0], ymin, ymax, linestyle='--', color='black')]
vh.append(ax.vlines(vls[0] + vls[1] - vls[2], ymin, ymax, linestyle='--', color='black'))
vh.append(ax.vlines(vls[0] + vls[1], ymin, ymax, linestyle='--', color='black'))
#Handling the rotary plots
if not h and not np.isnan(rot).all():
h = [ax.plot(rot_time,rot)]
elif not np.isnan(rot).all():
h.append(ax.plot(rot_time,rot))
if len(h)>n2show:
h[0][0].remove()
h.remove(h[0])
[z[0][0].set(color=z[1]) for z in zip(h,cmap_r)]
else:
[z[0][0].set(color=z[1]) for z in zip(h,cmap_r)]
return h,vh
def clear_plot(ax):
#Clear plot and axis handles
ax.cla()
ax.grid()
h=[];vh=[]
return h,vh
####Build the GUI####
sg.theme("DarkAmber")#BluePurple also has a nice asthetic
###Define the window layout
camera_layout = [
##Camera control and display panels
[],
[sg.Image(filename="", key="-IMAGE-",size=(300,240))],
[sg.Button("Stream",size=(9,1)),sg.Button("End Stream",size=(9,1)),
sg.Button("Save Stream",size=(9,1)),sg.Button("End Recording",size=(9,1))],
]
trial_layout = [
##Arduino, file name, and session controls
[sg.Button("Start Session",size=(14,1),button_color=('white','springgreen4')),
sg.Button("Stop Session",size=(14,1),button_color=('white','firebrick3')),
sg.Text("Trial",size=(18,1), key='trialNum')],
[
sg.Text("Animal ID"),
sg.Input(size=(25,1),key="Animal"),
sg.Button('Set',bind_return_key=True)
],
[
sg.Text("Session type"),
sg.Radio('DTSC',"RADIO",default=True,key="DTSC"),
sg.Radio('DEC',"RADIO",key="DEC")
],
[sg.HSeparator()],
[sg.Column([
[sg.T("Number trials"),sg.Input(size=(10,1),key="numTrial",default_text="110")],
[sg.T("ITI low (ms)"),sg.Input(size=(10,1),key="ITIlow",default_text="1000")],
[sg.T("Percent CS"),sg.Input(size=(10,1),key="percentCS",default_text="10")],
[sg.T("CS duration (ms)"),sg.Input(size=(10,1),key="CSdur",default_text="250")],
[sg.T("Pre-CS duration (ms)"),sg.Input(size=(10,1),key="preCSdur",default_text="200")]
]),
sg.Column([
[sg.T("Trial duration (ms)"),sg.Input(size=(10,1),key="trialDur",default_text="1000")],
[sg.T("ITI high (ms)"),sg.Input(size=(10,1),key="ITIhigh",default_text="1500")],
[sg.T("Percent US"),sg.Input(size=(10,1),key="percentUS",default_text="0")],
[sg.T("US duration (ms)"),sg.Input(size=(10,1),key="USdur",default_text="30")]
],vertical_alignment='top')],
[sg.Button("Upload to Microcontroller"),sg.Button("Current Microcontroller settings")],
[sg.Text("Background LED intensity (0-100)"),
sg.Input(key="LED_INTENSITY", size=(5,1), default_text="0"),
sg.Button("Set LED"), sg.Button("LED Off")]
]
graph_layout = [
[sg.Canvas(size=(300, 200),
key='graph')]
]
exit_layout = [
[sg.Button("End Program",size=(30,2))]
]
##full GUI layout
layout = [
[
[sg.Frame("Camera controls",camera_layout,title_location='n',element_justification='c'),
sg.Frame("Arduino session controls",trial_layout,title_location='n',element_justification='l')],
[sg.Frame("Graph test",graph_layout,title_location='n',element_justification = 'c'),
sg.Frame("Exit test",exit_layout)]
]
]
####Create the window, arduinoRig, and piStream####
window = sg.Window("Associative Learning control GUI", layout)
rig = arduinoRig()
event, values = window.read(timeout=0)
current_trial = rig.trial['trialNumber']
window['trialNum'].update("Trial number = "+str(current_trial))
vs = None
# draw the initial plot in the window
graph_elem = window['graph']
graph = graph_elem.TKCanvas
fig = Figure(figsize=[4,3])
ax = fig.add_subplot(111)
ax.set_xlabel("time")
ax.set_ylabel("some stuff (A.U.)")
ax.grid()
fig_agg = draw_figure(graph, fig)
colors = cm.jet(np.linspace(0,1,5))#We will ultimately show up to five traces at once
vls = [float(values['preCSdur']),float(values['CSdur']),float(values['USdur'])]
rot=np.nan;rot_time=np.nan;eb=np.nan;eb_time=np.nan;h=[];vh=[];
####Handling the GUI, rig, and piCamera####
#Loop booleans and variables
streaming = False#state of camera output
frame = None#current frame for output to GUI video
dispNow = True#to display current fame on GUI video
lastpicTime = 0#timing when next frame should be output to GUI video
#What to do if keyboard interrupt called
def signal_handler(sig, frame):
if vs is not None:
vs.end()
print("vs.end()")
rig.end()
print("\nProgram ended")
sys.exit(0)
signal.signal(signal.SIGINT,signal_handler)
####GUI read loop
while True:
#Update the trial counter where necessary
if current_trial != rig.trial['trialNumber']:
current_trial = rig.trial['trialNumber']
window['trialNum'].update("Trial Number = "+str(current_trial))
#Get whatever input the user applied to GUI
event, values = window.read(timeout=0)
#If the camera is on, check if we need to update GUI frame
if streaming:
now = time.perf_counter()
dispTime = now - lastpicTime
if dispTime>0.05:
frame = vs.read()
lastpicTime = time.perf_counter()
#Here tell the GUI to look for a flag to update the current plot
if rig.data_handler.rotary_ready:
rot,rot_time = rig.data_handler.get_rotary()
rig.data_handler.rotary_ready = False
h,vh = update_plot(ax,vls,rot=rot,rot_time=rot_time,h=h,vh=vh)
fig_agg.draw()
##Button options, left panel
if event == "End Program" or event == sg.WIN_CLOSED:
break
elif event == "Stream":
if not streaming:
if vs is None:
vs = pvid.piCamHandler()
try:
n = int(values['numTrial'])
except:
n = None
if vs is None:
vs = pvid.piCamHandler(total_trials=n)
else:
vs.reset_cam()
streaming = True
print("Start stream")
else:
print("Camera already streaming!")
elif event == "End Stream":
if vs is not None:
vs.endStream()
streaming = False
frame = None
print("End Stream")
elif event == "Save Stream":
if streaming and not vs.saving.value:
vs.guiStartRecording()
print("Save Stream")
elif event == "End Recording":
if streaming and vs.saving.value:
vs.guiStopRecording()
print("End Recording")
##Button options right panel
elif event == "Start Session":
rig.startSession()
h,vh = clear_plot(ax)
h,vh = update_plot(ax,vls,h=h,vh=vh)
fig_agg.draw()
while not rig.fnameReady:
pass
fStub = rig.getFstub()
if vs is not None:
vs.passFstub(fStub)
elif event == "Stop Session":
rig.stopSession()
elif event == "Set":
rig.animalID = values['Animal']
print("Set animalID: ",values['Animal'])
elif event == "Upload to Microcontroller":
if values['DTSC']:
rig.settrial('isDTSC',1)
elif values['DEC']:
rig.settrial('isDTSC',0)
for item in values.items():
if item[0] not in ['Animal','DTSC','DEC','graph']:
rig.settrial(item[0],item[1])
time.sleep(0.001)
vls = [float(values['preCSdur']),float(values['CSdur']),float(values['USdur'])]
elif event == "Current Microcontroller settings":
rig.GetArduinoState()
# LED controls
elif event == "Set LED":
try:
intensity = float(values['LED_INTENSITY'])
intensity = max(0, min(100, intensity))
led_pwm.ChangeDutyCycle(intensity)
print(f"LED intensity set to {intensity}%")
except ValueError:
print("Invalid intensity value")
elif event == "LED Off":
led_pwm.ChangeDutyCycle(0)
print("LED turned off")
##Process and display the current frame capture in GUI if streaming
if streaming and frame is not None:
if len(frame)>0:
data = cv2.resize(cv2.imdecode(np.frombuffer(frame, dtype=np.uint8), cv2.IMREAD_GRAYSCALE),(300,240))
imgbytes = cv2.imencode('.png',data)[1].tobytes()
window["-IMAGE-"].update(data=imgbytes)
frame = None
time.sleep(0.005)
##Do this when the program is ended
window.close()
if vs is not None:
vs.end()
print("vs.end()")
rig.end()
print("rig.end()")
print('Program ended')