msobral commited on
Commit
963de37
β€’
1 Parent(s): de0bff3

merge with backup

Browse files
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ dist/*
2
+ *.egg-info
3
+ __pycache__
4
+ .ipynb_checkpoints
__pycache__/frontend.cpython-37.pyc DELETED
Binary file (4.63 kB)
 
__pycache__/leds.cpython-37.pyc DELETED
Binary file (2.73 kB)
 
{src β†’ portiloop}/__init__.py RENAMED
File without changes
portiloop/capture.py ADDED
@@ -0,0 +1,487 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from time import sleep
2
+ import time
3
+ from playsound import playsound
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+ import os
7
+ from pathlib import Path
8
+ from datetime import datetime, timedelta
9
+ import multiprocessing as mp
10
+ from queue import Empty
11
+ import warnings
12
+ import shutil
13
+ from pyedflib import highlevel
14
+ from datetime import datetime
15
+
16
+ import matplotlib.pyplot as plt
17
+
18
+ from portilooplot.jupyter_plot import ProgressPlot
19
+ from portiloop.hardware.frontend import Frontend
20
+ from portiloop.hardware.leds import LEDs, Color
21
+
22
+ from IPython.display import clear_output, display
23
+ import ipywidgets as widgets
24
+
25
+
26
+ DEFAULT_FRONTEND_CONFIG = [
27
+ # nomenclature: name [default setting] [bits 7-0] : description
28
+ # Read only ID:
29
+ 0x3E, # ID [xx] [REV_ID[2:0], 1, DEV_ID[1:0], NU_CH[1:0]] : (RO)
30
+ # Global Settings Across Channels:
31
+ 0x96, # CONFIG1 [96] [1, DAISY_EN(bar), CLK_EN, 1, 0, DR[2:0]] : Datarate = 250 SPS
32
+ 0xC0, # CONFIG2 [C0] [1, 1, 0, INT_CAL, 0, CAL_AMP0, CAL_FREQ[1:0]] : No tests
33
+ 0x60, # CONFIG3 [60] [PD_REFBUF(bar), 1, 1, BIAS_MEAS, BIASREF_INT, PD_BIAS(bar), BIAS_LOFF_SENS, BIAS_STAT] : Power-down reference buffer, no bias
34
+ 0x00, # LOFF [00] [COMP_TH[2:0], 0, ILEAD_OFF[1:0], FLEAD_OFF[1:0]] : No lead-off
35
+ # Channel-Specific Settings:
36
+ 0x61, # CH1SET [61] [PD1, GAIN1[2:0], SRB2, MUX1[2:0]] : Channel 1 active, 24 gain, no SRB2 & input shorted
37
+ 0x61, # CH2SET [61] [PD2, GAIN2[2:0], SRB2, MUX2[2:0]] : Channel 2 active, 24 gain, no SRB2 & input shorted
38
+ 0x61, # CH3SET [61] [PD3, GAIN3[2:0], SRB2, MUX3[2:0]] : Channel 3 active, 24 gain, no SRB2 & input shorted
39
+ 0x61, # CH4SET [61] [PD4, GAIN4[2:0], SRB2, MUX4[2:0]] : Channel 4 active, 24 gain, no SRB2 & input shorted
40
+ 0x61, # CH5SET [61] [PD5, GAIN5[2:0], SRB2, MUX5[2:0]] : Channel 5 active, 24 gain, no SRB2 & input shorted
41
+ 0x61, # CH6SET [61] [PD6, GAIN6[2:0], SRB2, MUX6[2:0]] : Channel 6 active, 24 gain, no SRB2 & input shorted
42
+ 0x61, # CH7SET [61] [PD7, GAIN7[2:0], SRB2, MUX7[2:0]] : Channel 7 active, 24 gain, no SRB2 & input shorted
43
+ 0x61, # CH8SET [61] [PD8, GAIN8[2:0], SRB2, MUX8[2:0]] : Channel 8 active, 24 gain, no SRB2 & input shorted
44
+ 0x00, # BIAS_SENSP [00] [BIASP8, BIASP7, BIASP6, BIASP5, BIASP4, BIASP3, BIASP2, BIASP1] : No bias
45
+ 0x00, # BIAS_SENSN [00] [BIASN8, BIASN7, BIASN6, BIASN5, BIASN4, BIASN3, BIASN2, BIASN1] No bias
46
+ 0x00, # LOFF_SENSP [00] [LOFFP8, LOFFP7, LOFFP6, LOFFP5, LOFFP4, LOFFP3, LOFFP2, LOFFP1] : No lead-off
47
+ 0x00, # LOFF_SENSN [00] [LOFFM8, LOFFM7, LOFFM6, LOFFM5, LOFFM4, LOFFM3, LOFFM2, LOFFM1] : No lead-off
48
+ 0x00, # LOFF_FLIP [00] [LOFF_FLIP8, LOFF_FLIP7, LOFF_FLIP6, LOFF_FLIP5, LOFF_FLIP4, LOFF_FLIP3, LOFF_FLIP2, LOFF_FLIP1] : No lead-off flip
49
+ # Lead-Off Status Registers (Read-Only Registers):
50
+ 0x00, # LOFF_STATP [00] [IN8P_OFF, IN7P_OFF, IN6P_OFF, IN5P_OFF, IN4P_OFF, IN3P_OFF, IN2P_OFF, IN1P_OFF] : Lead-off positive status (RO)
51
+ 0x00, # LOFF_STATN [00] [IN8M_OFF, IN7M_OFF, IN6M_OFF, IN5M_OFF, IN4M_OFF, IN3M_OFF, IN2M_OFF, IN1M_OFF] : Laed-off negative status (RO)
52
+ # GPIO and OTHER Registers:
53
+ 0x0F, # GPIO [0F] [GPIOD[4:1], GPIOC[4:1]] : All GPIOs as inputs
54
+ 0x00, # MISC1 [00] [0, 0, SRB1, 0, 0, 0, 0, 0] : Disable SRBM
55
+ 0x00, # MISC2 [00] [00] : Unused
56
+ 0x00, # CONFIG4 [00] [0, 0, 0, 0, SINGLE_SHOT, 0, PD_LOFF_COMP(bar), 0] : Single-shot, lead-off comparator disabled
57
+ ]
58
+
59
+
60
+ FRONTEND_CONFIG = [
61
+ 0x3E, # ID (RO)
62
+ 0x95, # CONFIG1 [95] [1, DAISY_EN(bar), CLK_EN, 1, 0, DR[2:0]] : Datarate = 500 SPS
63
+ 0xD0, # CONFIG2 [C0] [1, 1, 0, INT_CAL, 0, CAL_AMP0, CAL_FREQ[1:0]]
64
+ 0xE0, # CONFIG3 [E0] [PD_REFBUF(bar), 1, 1, BIAS_MEAS, BIASREF_INT, PD_BIAS(bar), BIAS_LOFF_SENS, BIAS_STAT] : Power-down reference buffer, no bias
65
+ 0x00, # No lead-off
66
+ 0x03, # CH1SET [60] [PD1, GAIN1[2:0], SRB2, MUX1[2:0]]
67
+ 0x00, # CH2SET
68
+ 0x00, # CH3SET
69
+ 0x00, # CH4SET
70
+ 0x00, # CH5SET voltage
71
+ 0x00, # CH6SET voltage
72
+ 0x00, # CH7SET test
73
+ 0x04, # CH8SET temperature
74
+ 0x00, # BIAS_SENSP
75
+ 0x00, # BIAS_SENSN
76
+ 0xFF, # LOFF_SENSP Lead-off on all positive pins?
77
+ 0xFF, # LOFF_SENSN Lead-off on all negative pins?
78
+ 0x00, # Normal lead-off
79
+ 0x00, # Lead-off positive status (RO)
80
+ 0x00, # Lead-off negative status (RO)
81
+ 0x00, # All GPIOs as output ?
82
+ 0x20, # Enable SRB1
83
+ ]
84
+
85
+ def mod_config(config, datarate):
86
+ possible_datarates = [(250, 0x06),
87
+ (500, 0x05),
88
+ (1000, 0x04),
89
+ (2000, 0x03),
90
+ (4000, 0x02),
91
+ (8000, 0x01),
92
+ (16000, 0x00)]
93
+ mod_dr = 0x00
94
+ for i, j in possible_datarates:
95
+ if i >= datarate:
96
+ mod_dr = j
97
+ break
98
+
99
+ new_cf1 = config[1] & 0xF8
100
+ new_cf1 = new_cf1 | j
101
+ config[1] = new_cf1
102
+ print(f"DEBUG: new cf1: {hex(config[1])}")
103
+ return config
104
+
105
+ def filter_24(value):
106
+ return (value * 4.5) / (2**23 - 1) # 23 because 1 bit is lost for sign
107
+
108
+ def filter_2scomplement_np(value):
109
+ v = np.where((value & (1 << 23)) != 0, value - (1 << 24), value)
110
+ return filter_24(v)
111
+
112
+ class LiveDisplay():
113
+ def __init__(self, datapoint_dim=8, window_len=100):
114
+ self.datapoint_dim = datapoint_dim
115
+ self.queue = mp.Queue()
116
+ channel_names = [f"channel#{i+1}" for i in range(datapoint_dim)]
117
+ channel_names[0] = "voltage"
118
+ channel_names[7] = "temperature"
119
+ self.pp = ProgressPlot(plot_names=channel_names, max_window_len=window_len)
120
+
121
+ def add_datapoints(self, datapoints):
122
+ """
123
+ Adds 8 lists of datapoints to the plot
124
+
125
+ Args:
126
+ datapoints: list of 8 lists of floats (or list of 8 floats)
127
+ """
128
+ disp_list = []
129
+ for datapoint in datapoints:
130
+ d = [[elt] for elt in datapoint]
131
+ disp_list.append(d)
132
+ self.pp.update_with_datapoints(disp_list)
133
+
134
+ def add_datapoint(self, datapoint):
135
+ disp_list = [[elt] for elt in datapoint]
136
+ self.pp.update(disp_list)
137
+
138
+
139
+ def _capture_process(q_data, q_out, q_in, duration, frequency, python_clock=True):
140
+ """
141
+ Args:
142
+ q_data: multiprocessing.Queue: captured datapoints are put in the queue
143
+ q_out: mutliprocessing.Queue: to pass messages to the parent process
144
+ 'STOP': end of the the process
145
+ q_in: mutliprocessing.Queue: to pass messages from the parent process
146
+ 'STOP': stops the process
147
+ """
148
+ if duration <= 0:
149
+ duration = np.inf
150
+
151
+ sample_time = 1 / frequency
152
+
153
+ frontend = Frontend()
154
+ leds = LEDs()
155
+ leds.led2(Color.PURPLE)
156
+ leds.aquisition(True)
157
+
158
+ try:
159
+ data = frontend.read_regs(0x00, 1)
160
+ assert data == [0x3E], "The communication with the ADS cannot be established."
161
+ leds.led2(Color.BLUE)
162
+
163
+ config = FRONTEND_CONFIG
164
+ if python_clock: # set ADS to 2 * frequency
165
+ config = mod_config(config, 2 * frequency)
166
+ else: # set ADS to frequency
167
+ config = mod_config(config, frequency)
168
+
169
+ frontend.write_regs(0x00, config)
170
+ data = frontend.read_regs(0x00, len(config))
171
+ assert data == config, f"Wrong config: {data} vs {config}"
172
+ frontend.start()
173
+ leds.led2(Color.PURPLE)
174
+ while not frontend.is_ready():
175
+ pass
176
+
177
+ # Set up of leds
178
+ leds.aquisition(True)
179
+ sleep(0.5)
180
+ leds.aquisition(False)
181
+ sleep(0.5)
182
+ leds.aquisition(True)
183
+
184
+ c = True
185
+
186
+ it = 0
187
+ t_start = time.time()
188
+ t_max = t_start + duration
189
+ t = t_start
190
+
191
+ # first sample:
192
+ reading = frontend.read()
193
+ datapoint = reading.channels()
194
+ q_data.put(datapoint)
195
+
196
+ t_next = t + sample_time
197
+
198
+ # sampling loop:
199
+ while c and t < t_max:
200
+ t = time.time()
201
+ if python_clock:
202
+ if t <= t_next:
203
+ time.sleep(t_next - t)
204
+ t_next += sample_time
205
+ reading = frontend.read()
206
+ else:
207
+ reading = frontend.wait_new_data()
208
+ datapoint = reading.channels()
209
+ q_data.put(datapoint)
210
+
211
+
212
+ # Check for messages # this takes too long :/
213
+ # try:
214
+ # message = q_in.get_nowait()
215
+ # if message == 'STOP':
216
+ # c = False
217
+ # except Empty:
218
+ # pass
219
+ it += 1
220
+ t = time.time()
221
+ tot = (t - t_start) / it
222
+
223
+ print(f"Average frequency: {1 / tot} Hz for {it} samples")
224
+
225
+ leds.aquisition(False)
226
+
227
+ finally:
228
+ frontend.close()
229
+ leds.close()
230
+ q_in.close()
231
+ q_out.put('STOP')
232
+
233
+
234
+ class Capture:
235
+ def __init__(self):
236
+
237
+ self.filename = Path.home() / 'edf_recording' / f"recording_{now.strftime('%m_%d_%Y_%H_%M_%S')}.edf"
238
+ self._p_capture = None
239
+ self.__capture_on = False
240
+ self.frequency = 250
241
+ self.duration = 10
242
+ self.record = False
243
+ self.display = False
244
+ self.recording_file = None
245
+ self.python_clock = True
246
+
247
+ self.binfile = None
248
+ self.temp_path = Path.home() / '.temp'
249
+
250
+ # widgets
251
+
252
+ self.b_capture = widgets.ToggleButtons(
253
+ options=['Stop', 'Start'],
254
+ description='Capture:',
255
+ disabled=False,
256
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
257
+ tooltips=['Stop capture', 'Start capture'],
258
+ # icons=['check'] * 2
259
+ )
260
+
261
+ self.b_clock = widgets.ToggleButtons(
262
+ options=['Coral', 'ADS'],
263
+ description='Clock:',
264
+ disabled=False,
265
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
266
+ tooltips=['Use Coral clock (very precise, not very timely)',
267
+ 'Use ADS clock (not very precise, very timely)'],
268
+ # icons=['check'] * 2
269
+ )
270
+
271
+ self.b_filename = widgets.Text(
272
+ value=self.filename,
273
+ description='Filename:',
274
+ placeholder='All files will be in the edf_recording folder'
275
+ disabled=False
276
+ )
277
+
278
+ self.b_frequency = widgets.IntText(
279
+ value=250,
280
+ description='Freq (Hz):',
281
+ disabled=False
282
+ )
283
+
284
+ self.b_duration = widgets.IntText(
285
+ value=10,
286
+ description='Time (s):',
287
+ disabled=False
288
+ )
289
+
290
+ self.b_record = widgets.Checkbox(
291
+ value=False,
292
+ description='Record',
293
+ disabled=False,
294
+ indent=False
295
+ )
296
+
297
+ self.b_display = widgets.Checkbox(
298
+ value=False,
299
+ description='Display',
300
+ disabled=False,
301
+ indent=False
302
+ )
303
+
304
+ self.b_capture.observe(self.on_b_capture, 'value')
305
+ self.b_clock.observe(self.on_b_clock, 'value')
306
+ self.b_frequency.observe(self.on_b_frequency, 'value')
307
+ self.b_duration.observe(self.on_b_duration, 'value')
308
+ self.b_record.observe(self.on_b_record, 'value')
309
+ self.b_display.observe(self.on_b_display, 'value')
310
+ self.b_filename.observe(self.on_b_filename, 'value')
311
+
312
+ self.display_buttons()
313
+
314
+ def __del__(self):
315
+ self.b_capture.close()
316
+
317
+ def display_buttons(self):
318
+ display(widgets.VBox([self.b_frequency,
319
+ self.b_duration,
320
+ widgets.HBox([self.b_record, self.b_display]),
321
+ self.b_clock,
322
+ self.b_capture]))
323
+
324
+ def on_b_capture(self, value):
325
+ val = value['new']
326
+ if val == 'Start':
327
+ self.start_capture(
328
+ record=self.record,
329
+ viz=self.display,
330
+ width=500,
331
+ python_clock=self.python_clock)
332
+ elif val == 'Stop':
333
+ clear_output()
334
+ self.display_buttons()
335
+ else:
336
+ print(f"This option is not supported: {val}.")
337
+
338
+ def on_b_clock(self, value):
339
+ val = value['new']
340
+ if val == 'Coral':
341
+ self.python_clock = True
342
+ elif val == 'ADS':
343
+ self.python_clock = False
344
+ else:
345
+ print(f"This option is not supported: {val}.")
346
+
347
+ def on_b_frequency(self, value):
348
+ val = value['new']
349
+ if val > 0:
350
+ self.frequency = val
351
+ else:
352
+ print(f"Unsupported frequency: {val} Hz")
353
+
354
+ def on_b_filename(self, value):
355
+ val = value['new']
356
+ if val != '':
357
+ self.filename = Path.home() / 'edf_recording' / val
358
+ else:
359
+ now = datetime.now()
360
+ self.filename = Path.home() / 'edf_recording' / f"recording_{now.strftime('%m_%d_%Y_%H_%M_%S')}.edf"
361
+
362
+ def on_b_duration(self, value):
363
+ val = value['new']
364
+ if val > 0:
365
+ self.duration = val
366
+ else:
367
+ print(f"Unsupported duration: {val} s")
368
+
369
+ def on_b_record(self, value):
370
+ val = value['new']
371
+ self.record = val
372
+
373
+ def on_b_display(self, value):
374
+ val = value['new']
375
+ self.display = val
376
+
377
+ def open_recording_file(self):
378
+ print(f"Will store edf recording in {self.filename}")
379
+ os.mkdir(self.temp_path)
380
+ self.binfile = open(self.temp_path / 'data.bin', 'wb')
381
+
382
+ def close_recording_file(self):
383
+
384
+ print('Saving recording data...')
385
+ # Channel names
386
+ channels = ['Voltage', 'Ch2', 'Ch3', 'Ch4', 'Ch5', 'Ch6', 'Ch7', 'Temperature']
387
+
388
+ # Read binary data
389
+ data = np.fromfile(self.temp_path / 'data.bin', dtype=float)
390
+ data = data.reshape((8, int(data.shape[0]/8)))
391
+
392
+ # Declare and write EDF format file
393
+ signal_headers = highlevel.make_signal_headers(channels, sample_frequency=self.frequency)
394
+ header = highlevel.make_header(patientname='patient_x', gender='Female')
395
+ highlevel.write_edf(self.filename, data, signal_headers, header)
396
+
397
+ # Close and delete temp binary file
398
+ self.binfile.close()
399
+ shutil.rmtree(self.temp_path)
400
+
401
+ print('...done')
402
+
403
+ def add_recording_data(self, data):
404
+ np.array(data).tofile(self.binfile)
405
+
406
+ def start_capture(self,
407
+ record=True,
408
+ viz=False,
409
+ width=500,
410
+ python_clock=True):
411
+ self.q_messages_send = mp.Queue()
412
+ self.q_messages_recv = mp.Queue()
413
+ self.q_data = mp.Queue()
414
+
415
+ if self.__capture_on:
416
+ print("Capture is already ongoing, ignoring command.")
417
+ return
418
+ else:
419
+ self.__capture_on = True
420
+ SAMPLE_TIME = 1 / frequency
421
+ self._p_capture = mp.Process(target=_capture_process, args=(self.q_data,
422
+ self.q_messages_recv,
423
+ self.q_messages_send,
424
+ self.duration,
425
+ self.frequency,
426
+ python_clock))
427
+ self._p_capture.start()
428
+
429
+ if viz:
430
+ live_disp = LiveDisplay(window_len=width)
431
+
432
+ if record:
433
+ self.open_recording_file()
434
+
435
+ cc = True
436
+ while cc:
437
+ try:
438
+ mess = self.q_messages_recv.get_nowait()
439
+ if mess == 'STOP':
440
+ cc = False
441
+ except Empty:
442
+ pass
443
+
444
+ # retrieve all data points from q_data and put them in a list of np.array:
445
+ res = []
446
+ c = True
447
+ while c and len(res) < 25:
448
+ try:
449
+ point = self.q_data.get(timeout=SAMPLE_TIME)
450
+ res.append(point)
451
+ except Empty:
452
+ c = False
453
+ if len(res) == 0:
454
+ continue
455
+ n_array = np.array(res)
456
+ n_array = filter_2scomplement_np(n_array)
457
+
458
+ to_add = n_array.tolist()
459
+
460
+ if viz:
461
+ live_disp.add_datapoints(to_add)
462
+ if record:
463
+ self.add_recording_data(to_add)
464
+
465
+ # empty q_data
466
+ cc = True
467
+ while cc:
468
+ try:
469
+ _ = self.q_data.get_nowait()
470
+ except Empty:
471
+ cc = False
472
+
473
+ self.q_messages_recv.close()
474
+ self.q_data.close()
475
+
476
+ if record:
477
+ self.close_recording_file()
478
+
479
+ # print("DEBUG: joining capture process...")
480
+ self._p_capture.join()
481
+ # print("DEBUG: capture process joined.")
482
+ self.__capture_on = False
483
+
484
+
485
+ if __name__ == "__main__":
486
+ # TODO: Argparse this
487
+ pass
{src β†’ portiloop}/demo/acquisition_demo.py RENAMED
File without changes
{src β†’ portiloop}/demo/led_demo.py RENAMED
File without changes
{src β†’ portiloop}/hardware/__init__.py RENAMED
File without changes
{src β†’ portiloop}/hardware/frontend.py RENAMED
File without changes
{src β†’ portiloop}/hardware/leds.py RENAMED
File without changes
{src β†’ portiloop}/nn/demo_net.py RENAMED
File without changes
{src β†’ portiloop}/notebooks/test_capture.ipynb RENAMED
@@ -1,20 +1,60 @@
1
  {
2
  "cells": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  {
4
  "cell_type": "code",
5
  "execution_count": 1,
6
  "id": "22d798fb",
7
  "metadata": {},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  "outputs": [
9
- {
10
- "name": "stdout",
11
- "output_type": "stream",
12
- "text": [
13
- "Configuring EEG Frontend\n",
14
- "EEG Frontend configured\n",
15
- "Ready for data\n"
16
- ]
17
- },
18
  {
19
  "data": {
20
  "text/html": [
@@ -9863,7 +9903,7 @@
9863
  " };\n",
9864
  " };\n",
9865
  "})();\n",
9866
- "</script><svg id=\"18b92035-5da0-4fb4-8aaf-e9327efef884\" class=\"learning-curve\"></svg><script> window.setupLearningCurve({\"id\": \"18b92035-5da0-4fb4-8aaf-e9327efef884\", \"width\": 600, \"height\": 1690, \"lineConfig\": {\"line-1\": {\"name\": \"line-1\", \"color\": \"#1f77b4\"}}, \"facetConfig\": {\"channel#1\": {\"name\": \"channel#1\", \"limit\": [null, null]}, \"channel#2\": {\"name\": \"channel#2\", \"limit\": [null, null]}, \"channel#3\": {\"name\": \"channel#3\", \"limit\": [null, null]}, \"channel#4\": {\"name\": \"channel#4\", \"limit\": [null, null]}, \"channel#5\": {\"name\": \"channel#5\", \"limit\": [null, null]}, \"channel#6\": {\"name\": \"channel#6\", \"limit\": [null, null]}, \"channel#7\": {\"name\": \"channel#7\", \"limit\": [null, null]}, \"channel#8\": {\"name\": \"channel#8\", \"limit\": [null, null]}}, \"xAxisConfig\": {\"name\": \"iteration\", \"limit\": [null, null]}, \"max_window_len\": 100});</script>"
9867
  ],
9868
  "text/plain": [
9869
  "<IPython.core.display.HTML object>"
@@ -9874,11 +9914,11 @@
9874
  },
9875
  {
9876
  "data": {
9877
- "text/html": [
9878
- "<script> window.appendLearningCurve([]);</script>"
9879
  ],
9880
  "text/plain": [
9881
- "<IPython.core.display.HTML object>"
9882
  ]
9883
  },
9884
  "metadata": {},
@@ -9888,14 +9928,63 @@
9888
  "name": "stdout",
9889
  "output_type": "stream",
9890
  "text": [
9891
- "Saving Method is not yet implemented\n"
 
 
 
 
 
 
 
 
 
 
 
9892
  ]
9893
  }
9894
  ],
9895
  "source": [
9896
- "from capture import Capture\n",
 
 
 
 
 
 
 
9897
  "\n",
9898
- "cap = Capture()"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9899
  ]
9900
  }
9901
  ],
 
1
  {
2
  "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 11,
6
+ "id": "199cbac1",
7
+ "metadata": {},
8
+ "outputs": [
9
+ {
10
+ "data": {
11
+ "application/vnd.jupyter.widget-view+json": {
12
+ "model_id": "69ac3694fc054d05af462214263ed101",
13
+ "version_major": 2,
14
+ "version_minor": 0
15
+ },
16
+ "text/plain": [
17
+ "ToggleButtons(description='Recording:', options=('Stop', 'Start'), tooltips=('Stop capture', 'Start capture'),…"
18
+ ]
19
+ },
20
+ "metadata": {},
21
+ "output_type": "display_data"
22
+ }
23
+ ],
24
+ "source": [
25
+ "import ipywidgets as widgets\n",
26
+ "\n",
27
+ "widgets.ToggleButtons(\n",
28
+ " options=['Stop', 'Start'],\n",
29
+ " description='Recording:',\n",
30
+ " disabled=False,\n",
31
+ " button_style='', # 'success', 'info', 'warning', 'danger' or ''\n",
32
+ " tooltips=['Stop capture', 'Start capture'],\n",
33
+ " # icons=['check'] * 2\n",
34
+ ")"
35
+ ]
36
+ },
37
  {
38
  "cell_type": "code",
39
  "execution_count": 1,
40
  "id": "22d798fb",
41
  "metadata": {},
42
+ "outputs": [],
43
+ "source": [
44
+ "from portilooplot.jupyter_plot import ProgressPlot\n",
45
+ "import random\n",
46
+ "import pyinstrument\n",
47
+ "import tracemalloc"
48
+ ]
49
+ },
50
+ {
51
+ "cell_type": "code",
52
+ "execution_count": 2,
53
+ "id": "b3634f53",
54
+ "metadata": {
55
+ "scrolled": false
56
+ },
57
  "outputs": [
 
 
 
 
 
 
 
 
 
58
  {
59
  "data": {
60
  "text/html": [
 
9903
  " };\n",
9904
  " };\n",
9905
  "})();\n",
9906
+ "</script><svg id=\"b324761b-9bc3-4572-8eeb-ad443c1263e9\" class=\"learning-curve\"></svg><script> window.setupLearningCurve({\"id\": \"b324761b-9bc3-4572-8eeb-ad443c1263e9\", \"width\": 600, \"height\": 1690, \"lineConfig\": {\"line-1\": {\"name\": \"line-1\", \"color\": \"#1f77b4\"}}, \"facetConfig\": {\"1\": {\"name\": \"1\", \"limit\": [null, null]}, \"2\": {\"name\": \"2\", \"limit\": [null, null]}, \"3\": {\"name\": \"3\", \"limit\": [null, null]}, \"4\": {\"name\": \"4\", \"limit\": [null, null]}, \"5\": {\"name\": \"5\", \"limit\": [null, null]}, \"6\": {\"name\": \"6\", \"limit\": [null, null]}, \"7\": {\"name\": \"7\", \"limit\": [null, null]}, \"8\": {\"name\": \"8\", \"limit\": [null, null]}}, \"xAxisConfig\": {\"name\": \"iteration\", \"limit\": [null, null]}, \"max_window_len\": 100});</script>"
9907
  ],
9908
  "text/plain": [
9909
  "<IPython.core.display.HTML object>"
 
9914
  },
9915
  {
9916
  "data": {
9917
+ "application/javascript": [
9918
+ "window.appendLearningCurve([{\"x\": 24950.0, \"y\": {\"1\": {\"line-1\": 0.3192000176432853}, \"2\": {\"line-1\": 0.3192000176432853}, \"3\": {\"line-1\": 0.3192000176432853}, \"4\": {\"line-1\": 0.3192000176432853}, \"5\": {\"line-1\": 0.3192000176432853}, \"6\": {\"line-1\": 0.3192000176432853}, \"7\": {\"line-1\": 0.3192000176432853}, \"8\": {\"line-1\": 0.3192000176432853}}}, {\"x\": 24951.0, \"y\": {\"1\": {\"line-1\": 0.33522360992059597}, \"2\": {\"line-1\": 0.33522360992059597}, \"3\": {\"line-1\": 0.33522360992059597}, \"4\": {\"line-1\": 0.33522360992059597}, \"5\": {\"line-1\": 0.33522360992059597}, \"6\": {\"line-1\": 0.33522360992059597}, \"7\": {\"line-1\": 0.33522360992059597}, \"8\": {\"line-1\": 0.33522360992059597}}}, {\"x\": 24952.0, \"y\": {\"1\": {\"line-1\": 0.3164730653418474}, \"2\": {\"line-1\": 0.3164730653418474}, \"3\": {\"line-1\": 0.3164730653418474}, \"4\": {\"line-1\": 0.3164730653418474}, \"5\": {\"line-1\": 0.3164730653418474}, \"6\": {\"line-1\": 0.3164730653418474}, \"7\": {\"line-1\": 0.3164730653418474}, \"8\": {\"line-1\": 0.3164730653418474}}}, {\"x\": 24953.0, \"y\": {\"1\": {\"line-1\": 0.7234433331019386}, \"2\": {\"line-1\": 0.7234433331019386}, \"3\": {\"line-1\": 0.7234433331019386}, \"4\": {\"line-1\": 0.7234433331019386}, \"5\": {\"line-1\": 0.7234433331019386}, \"6\": {\"line-1\": 0.7234433331019386}, \"7\": {\"line-1\": 0.7234433331019386}, \"8\": {\"line-1\": 0.7234433331019386}}}, {\"x\": 24954.0, \"y\": {\"1\": {\"line-1\": 0.270371213217371}, \"2\": {\"line-1\": 0.270371213217371}, \"3\": {\"line-1\": 0.270371213217371}, \"4\": {\"line-1\": 0.270371213217371}, \"5\": {\"line-1\": 0.270371213217371}, \"6\": {\"line-1\": 0.270371213217371}, \"7\": {\"line-1\": 0.270371213217371}, \"8\": {\"line-1\": 0.270371213217371}}}, {\"x\": 24955.0, \"y\": {\"1\": {\"line-1\": 0.5321540827253876}, \"2\": {\"line-1\": 0.5321540827253876}, \"3\": {\"line-1\": 0.5321540827253876}, \"4\": {\"line-1\": 0.5321540827253876}, \"5\": {\"line-1\": 0.5321540827253876}, \"6\": {\"line-1\": 0.5321540827253876}, \"7\": {\"line-1\": 0.5321540827253876}, \"8\": {\"line-1\": 0.5321540827253876}}}, {\"x\": 24956.0, \"y\": {\"1\": {\"line-1\": 0.3485689495839569}, \"2\": {\"line-1\": 0.3485689495839569}, \"3\": {\"line-1\": 0.3485689495839569}, \"4\": {\"line-1\": 0.3485689495839569}, \"5\": {\"line-1\": 0.3485689495839569}, \"6\": {\"line-1\": 0.3485689495839569}, \"7\": {\"line-1\": 0.3485689495839569}, \"8\": {\"line-1\": 0.3485689495839569}}}, {\"x\": 24957.0, \"y\": {\"1\": {\"line-1\": 0.051934111397848426}, \"2\": {\"line-1\": 0.051934111397848426}, \"3\": {\"line-1\": 0.051934111397848426}, \"4\": {\"line-1\": 0.051934111397848426}, \"5\": {\"line-1\": 0.051934111397848426}, \"6\": {\"line-1\": 0.051934111397848426}, \"7\": {\"line-1\": 0.051934111397848426}, \"8\": {\"line-1\": 0.051934111397848426}}}, {\"x\": 24958.0, \"y\": {\"1\": {\"line-1\": 0.26356332272216065}, \"2\": {\"line-1\": 0.26356332272216065}, \"3\": {\"line-1\": 0.26356332272216065}, \"4\": {\"line-1\": 0.26356332272216065}, \"5\": {\"line-1\": 0.26356332272216065}, \"6\": {\"line-1\": 0.26356332272216065}, \"7\": {\"line-1\": 0.26356332272216065}, \"8\": {\"line-1\": 0.26356332272216065}}}, {\"x\": 24959.0, \"y\": {\"1\": {\"line-1\": 0.4438471625095537}, \"2\": {\"line-1\": 0.4438471625095537}, \"3\": {\"line-1\": 0.4438471625095537}, \"4\": {\"line-1\": 0.4438471625095537}, \"5\": {\"line-1\": 0.4438471625095537}, \"6\": {\"line-1\": 0.4438471625095537}, \"7\": {\"line-1\": 0.4438471625095537}, \"8\": {\"line-1\": 0.4438471625095537}}}, {\"x\": 24960.0, \"y\": {\"1\": {\"line-1\": 0.2505028892401099}, \"2\": {\"line-1\": 0.2505028892401099}, \"3\": {\"line-1\": 0.2505028892401099}, \"4\": {\"line-1\": 0.2505028892401099}, \"5\": {\"line-1\": 0.2505028892401099}, \"6\": {\"line-1\": 0.2505028892401099}, \"7\": {\"line-1\": 0.2505028892401099}, \"8\": {\"line-1\": 0.2505028892401099}}}, {\"x\": 24961.0, \"y\": {\"1\": {\"line-1\": 0.036704347270042015}, \"2\": {\"line-1\": 0.036704347270042015}, \"3\": {\"line-1\": 0.036704347270042015}, \"4\": {\"line-1\": 0.036704347270042015}, \"5\": {\"line-1\": 0.036704347270042015}, \"6\": {\"line-1\": 0.036704347270042015}, \"7\": {\"line-1\": 0.036704347270042015}, \"8\": {\"line-1\": 0.036704347270042015}}}, {\"x\": 24962.0, \"y\": {\"1\": {\"line-1\": 0.13774621721114289}, \"2\": {\"line-1\": 0.13774621721114289}, \"3\": {\"line-1\": 0.13774621721114289}, \"4\": {\"line-1\": 0.13774621721114289}, \"5\": {\"line-1\": 0.13774621721114289}, \"6\": {\"line-1\": 0.13774621721114289}, \"7\": {\"line-1\": 0.13774621721114289}, \"8\": {\"line-1\": 0.13774621721114289}}}, {\"x\": 24963.0, \"y\": {\"1\": {\"line-1\": 0.7908747669189246}, \"2\": {\"line-1\": 0.7908747669189246}, \"3\": {\"line-1\": 0.7908747669189246}, \"4\": {\"line-1\": 0.7908747669189246}, \"5\": {\"line-1\": 0.7908747669189246}, \"6\": {\"line-1\": 0.7908747669189246}, \"7\": {\"line-1\": 0.7908747669189246}, \"8\": {\"line-1\": 0.7908747669189246}}}, {\"x\": 24964.0, \"y\": {\"1\": {\"line-1\": 0.8634461951524458}, \"2\": {\"line-1\": 0.8634461951524458}, \"3\": {\"line-1\": 0.8634461951524458}, \"4\": {\"line-1\": 0.8634461951524458}, \"5\": {\"line-1\": 0.8634461951524458}, \"6\": {\"line-1\": 0.8634461951524458}, \"7\": {\"line-1\": 0.8634461951524458}, \"8\": {\"line-1\": 0.8634461951524458}}}, {\"x\": 24965.0, \"y\": {\"1\": {\"line-1\": 0.47295109195932383}, \"2\": {\"line-1\": 0.47295109195932383}, \"3\": {\"line-1\": 0.47295109195932383}, \"4\": {\"line-1\": 0.47295109195932383}, \"5\": {\"line-1\": 0.47295109195932383}, \"6\": {\"line-1\": 0.47295109195932383}, \"7\": {\"line-1\": 0.47295109195932383}, \"8\": {\"line-1\": 0.47295109195932383}}}, {\"x\": 24966.0, \"y\": {\"1\": {\"line-1\": 0.3549535177097197}, \"2\": {\"line-1\": 0.3549535177097197}, \"3\": {\"line-1\": 0.3549535177097197}, \"4\": {\"line-1\": 0.3549535177097197}, \"5\": {\"line-1\": 0.3549535177097197}, \"6\": {\"line-1\": 0.3549535177097197}, \"7\": {\"line-1\": 0.3549535177097197}, \"8\": {\"line-1\": 0.3549535177097197}}}, {\"x\": 24967.0, \"y\": {\"1\": {\"line-1\": 0.20099822523262356}, \"2\": {\"line-1\": 0.20099822523262356}, \"3\": {\"line-1\": 0.20099822523262356}, \"4\": {\"line-1\": 0.20099822523262356}, \"5\": {\"line-1\": 0.20099822523262356}, \"6\": {\"line-1\": 0.20099822523262356}, \"7\": {\"line-1\": 0.20099822523262356}, \"8\": {\"line-1\": 0.20099822523262356}}}, {\"x\": 24968.0, \"y\": {\"1\": {\"line-1\": 9.127447566004143e-05}, \"2\": {\"line-1\": 9.127447566004143e-05}, \"3\": {\"line-1\": 9.127447566004143e-05}, \"4\": {\"line-1\": 9.127447566004143e-05}, \"5\": {\"line-1\": 9.127447566004143e-05}, \"6\": {\"line-1\": 9.127447566004143e-05}, \"7\": {\"line-1\": 9.127447566004143e-05}, \"8\": {\"line-1\": 9.127447566004143e-05}}}, {\"x\": 24969.0, \"y\": {\"1\": {\"line-1\": 0.9030248742413123}, \"2\": {\"line-1\": 0.9030248742413123}, \"3\": {\"line-1\": 0.9030248742413123}, \"4\": {\"line-1\": 0.9030248742413123}, \"5\": {\"line-1\": 0.9030248742413123}, \"6\": {\"line-1\": 0.9030248742413123}, \"7\": {\"line-1\": 0.9030248742413123}, \"8\": {\"line-1\": 0.9030248742413123}}}, {\"x\": 24970.0, \"y\": {\"1\": {\"line-1\": 0.0019911372141314665}, \"2\": {\"line-1\": 0.0019911372141314665}, \"3\": {\"line-1\": 0.0019911372141314665}, \"4\": {\"line-1\": 0.0019911372141314665}, \"5\": {\"line-1\": 0.0019911372141314665}, \"6\": {\"line-1\": 0.0019911372141314665}, \"7\": {\"line-1\": 0.0019911372141314665}, \"8\": {\"line-1\": 0.0019911372141314665}}}, {\"x\": 24971.0, \"y\": {\"1\": {\"line-1\": 0.09135437238859168}, \"2\": {\"line-1\": 0.09135437238859168}, \"3\": {\"line-1\": 0.09135437238859168}, \"4\": {\"line-1\": 0.09135437238859168}, \"5\": {\"line-1\": 0.09135437238859168}, \"6\": {\"line-1\": 0.09135437238859168}, \"7\": {\"line-1\": 0.09135437238859168}, \"8\": {\"line-1\": 0.09135437238859168}}}, {\"x\": 24972.0, \"y\": {\"1\": {\"line-1\": 0.41000245251515033}, \"2\": {\"line-1\": 0.41000245251515033}, \"3\": {\"line-1\": 0.41000245251515033}, \"4\": {\"line-1\": 0.41000245251515033}, \"5\": {\"line-1\": 0.41000245251515033}, \"6\": {\"line-1\": 0.41000245251515033}, \"7\": {\"line-1\": 0.41000245251515033}, \"8\": {\"line-1\": 0.41000245251515033}}}, {\"x\": 24973.0, \"y\": {\"1\": {\"line-1\": 0.9587549770789981}, \"2\": {\"line-1\": 0.9587549770789981}, \"3\": {\"line-1\": 0.9587549770789981}, \"4\": {\"line-1\": 0.9587549770789981}, \"5\": {\"line-1\": 0.9587549770789981}, \"6\": {\"line-1\": 0.9587549770789981}, \"7\": {\"line-1\": 0.9587549770789981}, \"8\": {\"line-1\": 0.9587549770789981}}}, {\"x\": 24974.0, \"y\": {\"1\": {\"line-1\": 0.17328394460595942}, \"2\": {\"line-1\": 0.17328394460595942}, \"3\": {\"line-1\": 0.17328394460595942}, \"4\": {\"line-1\": 0.17328394460595942}, \"5\": {\"line-1\": 0.17328394460595942}, \"6\": {\"line-1\": 0.17328394460595942}, \"7\": {\"line-1\": 0.17328394460595942}, \"8\": {\"line-1\": 0.17328394460595942}}}, {\"x\": 24975.0, \"y\": {\"1\": {\"line-1\": 0.52537218523594}, \"2\": {\"line-1\": 0.52537218523594}, \"3\": {\"line-1\": 0.52537218523594}, \"4\": {\"line-1\": 0.52537218523594}, \"5\": {\"line-1\": 0.52537218523594}, \"6\": {\"line-1\": 0.52537218523594}, \"7\": {\"line-1\": 0.52537218523594}, \"8\": {\"line-1\": 0.52537218523594}}}, {\"x\": 24976.0, \"y\": {\"1\": {\"line-1\": 0.7795619002997662}, \"2\": {\"line-1\": 0.7795619002997662}, \"3\": {\"line-1\": 0.7795619002997662}, \"4\": {\"line-1\": 0.7795619002997662}, \"5\": {\"line-1\": 0.7795619002997662}, \"6\": {\"line-1\": 0.7795619002997662}, \"7\": {\"line-1\": 0.7795619002997662}, \"8\": {\"line-1\": 0.7795619002997662}}}, {\"x\": 24977.0, \"y\": {\"1\": {\"line-1\": 0.6209864283463551}, \"2\": {\"line-1\": 0.6209864283463551}, \"3\": {\"line-1\": 0.6209864283463551}, \"4\": {\"line-1\": 0.6209864283463551}, \"5\": {\"line-1\": 0.6209864283463551}, \"6\": {\"line-1\": 0.6209864283463551}, \"7\": {\"line-1\": 0.6209864283463551}, \"8\": {\"line-1\": 0.6209864283463551}}}, {\"x\": 24978.0, \"y\": {\"1\": {\"line-1\": 0.6621486973544856}, \"2\": {\"line-1\": 0.6621486973544856}, \"3\": {\"line-1\": 0.6621486973544856}, \"4\": {\"line-1\": 0.6621486973544856}, \"5\": {\"line-1\": 0.6621486973544856}, \"6\": {\"line-1\": 0.6621486973544856}, \"7\": {\"line-1\": 0.6621486973544856}, \"8\": {\"line-1\": 0.6621486973544856}}}, {\"x\": 24979.0, \"y\": {\"1\": {\"line-1\": 0.13815788459250367}, \"2\": {\"line-1\": 0.13815788459250367}, \"3\": {\"line-1\": 0.13815788459250367}, \"4\": {\"line-1\": 0.13815788459250367}, \"5\": {\"line-1\": 0.13815788459250367}, \"6\": {\"line-1\": 0.13815788459250367}, \"7\": {\"line-1\": 0.13815788459250367}, \"8\": {\"line-1\": 0.13815788459250367}}}, {\"x\": 24980.0, \"y\": {\"1\": {\"line-1\": 0.5582136582227902}, \"2\": {\"line-1\": 0.5582136582227902}, \"3\": {\"line-1\": 0.5582136582227902}, \"4\": {\"line-1\": 0.5582136582227902}, \"5\": {\"line-1\": 0.5582136582227902}, \"6\": {\"line-1\": 0.5582136582227902}, \"7\": {\"line-1\": 0.5582136582227902}, \"8\": {\"line-1\": 0.5582136582227902}}}, {\"x\": 24981.0, \"y\": {\"1\": {\"line-1\": 0.12096353108859637}, \"2\": {\"line-1\": 0.12096353108859637}, \"3\": {\"line-1\": 0.12096353108859637}, \"4\": {\"line-1\": 0.12096353108859637}, \"5\": {\"line-1\": 0.12096353108859637}, \"6\": {\"line-1\": 0.12096353108859637}, \"7\": {\"line-1\": 0.12096353108859637}, \"8\": {\"line-1\": 0.12096353108859637}}}, {\"x\": 24982.0, \"y\": {\"1\": {\"line-1\": 0.3913129520983236}, \"2\": {\"line-1\": 0.3913129520983236}, \"3\": {\"line-1\": 0.3913129520983236}, \"4\": {\"line-1\": 0.3913129520983236}, \"5\": {\"line-1\": 0.3913129520983236}, \"6\": {\"line-1\": 0.3913129520983236}, \"7\": {\"line-1\": 0.3913129520983236}, \"8\": {\"line-1\": 0.3913129520983236}}}, {\"x\": 24983.0, \"y\": {\"1\": {\"line-1\": 0.848783349855704}, \"2\": {\"line-1\": 0.848783349855704}, \"3\": {\"line-1\": 0.848783349855704}, \"4\": {\"line-1\": 0.848783349855704}, \"5\": {\"line-1\": 0.848783349855704}, \"6\": {\"line-1\": 0.848783349855704}, \"7\": {\"line-1\": 0.848783349855704}, \"8\": {\"line-1\": 0.848783349855704}}}, {\"x\": 24984.0, \"y\": {\"1\": {\"line-1\": 0.3325979805468605}, \"2\": {\"line-1\": 0.3325979805468605}, \"3\": {\"line-1\": 0.3325979805468605}, \"4\": {\"line-1\": 0.3325979805468605}, \"5\": {\"line-1\": 0.3325979805468605}, \"6\": {\"line-1\": 0.3325979805468605}, \"7\": {\"line-1\": 0.3325979805468605}, \"8\": {\"line-1\": 0.3325979805468605}}}, {\"x\": 24985.0, \"y\": {\"1\": {\"line-1\": 0.10351164697782511}, \"2\": {\"line-1\": 0.10351164697782511}, \"3\": {\"line-1\": 0.10351164697782511}, \"4\": {\"line-1\": 0.10351164697782511}, \"5\": {\"line-1\": 0.10351164697782511}, \"6\": {\"line-1\": 0.10351164697782511}, \"7\": {\"line-1\": 0.10351164697782511}, \"8\": {\"line-1\": 0.10351164697782511}}}, {\"x\": 24986.0, \"y\": {\"1\": {\"line-1\": 0.6191488065036974}, \"2\": {\"line-1\": 0.6191488065036974}, \"3\": {\"line-1\": 0.6191488065036974}, \"4\": {\"line-1\": 0.6191488065036974}, \"5\": {\"line-1\": 0.6191488065036974}, \"6\": {\"line-1\": 0.6191488065036974}, \"7\": {\"line-1\": 0.6191488065036974}, \"8\": {\"line-1\": 0.6191488065036974}}}, {\"x\": 24987.0, \"y\": {\"1\": {\"line-1\": 0.03300166615359168}, \"2\": {\"line-1\": 0.03300166615359168}, \"3\": {\"line-1\": 0.03300166615359168}, \"4\": {\"line-1\": 0.03300166615359168}, \"5\": {\"line-1\": 0.03300166615359168}, \"6\": {\"line-1\": 0.03300166615359168}, \"7\": {\"line-1\": 0.03300166615359168}, \"8\": {\"line-1\": 0.03300166615359168}}}, {\"x\": 24988.0, \"y\": {\"1\": {\"line-1\": 0.6506440391458781}, \"2\": {\"line-1\": 0.6506440391458781}, \"3\": {\"line-1\": 0.6506440391458781}, \"4\": {\"line-1\": 0.6506440391458781}, \"5\": {\"line-1\": 0.6506440391458781}, \"6\": {\"line-1\": 0.6506440391458781}, \"7\": {\"line-1\": 0.6506440391458781}, \"8\": {\"line-1\": 0.6506440391458781}}}, {\"x\": 24989.0, \"y\": {\"1\": {\"line-1\": 0.11620637720310334}, \"2\": {\"line-1\": 0.11620637720310334}, \"3\": {\"line-1\": 0.11620637720310334}, \"4\": {\"line-1\": 0.11620637720310334}, \"5\": {\"line-1\": 0.11620637720310334}, \"6\": {\"line-1\": 0.11620637720310334}, \"7\": {\"line-1\": 0.11620637720310334}, \"8\": {\"line-1\": 0.11620637720310334}}}, {\"x\": 24990.0, \"y\": {\"1\": {\"line-1\": 0.18603116230585737}, \"2\": {\"line-1\": 0.18603116230585737}, \"3\": {\"line-1\": 0.18603116230585737}, \"4\": {\"line-1\": 0.18603116230585737}, \"5\": {\"line-1\": 0.18603116230585737}, \"6\": {\"line-1\": 0.18603116230585737}, \"7\": {\"line-1\": 0.18603116230585737}, \"8\": {\"line-1\": 0.18603116230585737}}}, {\"x\": 24991.0, \"y\": {\"1\": {\"line-1\": 0.6720271980041743}, \"2\": {\"line-1\": 0.6720271980041743}, \"3\": {\"line-1\": 0.6720271980041743}, \"4\": {\"line-1\": 0.6720271980041743}, \"5\": {\"line-1\": 0.6720271980041743}, \"6\": {\"line-1\": 0.6720271980041743}, \"7\": {\"line-1\": 0.6720271980041743}, \"8\": {\"line-1\": 0.6720271980041743}}}, {\"x\": 24992.0, \"y\": {\"1\": {\"line-1\": 0.8210805851642515}, \"2\": {\"line-1\": 0.8210805851642515}, \"3\": {\"line-1\": 0.8210805851642515}, \"4\": {\"line-1\": 0.8210805851642515}, \"5\": {\"line-1\": 0.8210805851642515}, \"6\": {\"line-1\": 0.8210805851642515}, \"7\": {\"line-1\": 0.8210805851642515}, \"8\": {\"line-1\": 0.8210805851642515}}}, {\"x\": 24993.0, \"y\": {\"1\": {\"line-1\": 0.13081465549513815}, \"2\": {\"line-1\": 0.13081465549513815}, \"3\": {\"line-1\": 0.13081465549513815}, \"4\": {\"line-1\": 0.13081465549513815}, \"5\": {\"line-1\": 0.13081465549513815}, \"6\": {\"line-1\": 0.13081465549513815}, \"7\": {\"line-1\": 0.13081465549513815}, \"8\": {\"line-1\": 0.13081465549513815}}}, {\"x\": 24994.0, \"y\": {\"1\": {\"line-1\": 0.6710151586939207}, \"2\": {\"line-1\": 0.6710151586939207}, \"3\": {\"line-1\": 0.6710151586939207}, \"4\": {\"line-1\": 0.6710151586939207}, \"5\": {\"line-1\": 0.6710151586939207}, \"6\": {\"line-1\": 0.6710151586939207}, \"7\": {\"line-1\": 0.6710151586939207}, \"8\": {\"line-1\": 0.6710151586939207}}}, {\"x\": 24995.0, \"y\": {\"1\": {\"line-1\": 0.05117302968623272}, \"2\": {\"line-1\": 0.05117302968623272}, \"3\": {\"line-1\": 0.05117302968623272}, \"4\": {\"line-1\": 0.05117302968623272}, \"5\": {\"line-1\": 0.05117302968623272}, \"6\": {\"line-1\": 0.05117302968623272}, \"7\": {\"line-1\": 0.05117302968623272}, \"8\": {\"line-1\": 0.05117302968623272}}}, {\"x\": 24996.0, \"y\": {\"1\": {\"line-1\": 0.18704954168745047}, \"2\": {\"line-1\": 0.18704954168745047}, \"3\": {\"line-1\": 0.18704954168745047}, \"4\": {\"line-1\": 0.18704954168745047}, \"5\": {\"line-1\": 0.18704954168745047}, \"6\": {\"line-1\": 0.18704954168745047}, \"7\": {\"line-1\": 0.18704954168745047}, \"8\": {\"line-1\": 0.18704954168745047}}}, {\"x\": 24997.0, \"y\": {\"1\": {\"line-1\": 0.644596713251439}, \"2\": {\"line-1\": 0.644596713251439}, \"3\": {\"line-1\": 0.644596713251439}, \"4\": {\"line-1\": 0.644596713251439}, \"5\": {\"line-1\": 0.644596713251439}, \"6\": {\"line-1\": 0.644596713251439}, \"7\": {\"line-1\": 0.644596713251439}, \"8\": {\"line-1\": 0.644596713251439}}}, {\"x\": 24998.0, \"y\": {\"1\": {\"line-1\": 0.036533816975556466}, \"2\": {\"line-1\": 0.036533816975556466}, \"3\": {\"line-1\": 0.036533816975556466}, \"4\": {\"line-1\": 0.036533816975556466}, \"5\": {\"line-1\": 0.036533816975556466}, \"6\": {\"line-1\": 0.036533816975556466}, \"7\": {\"line-1\": 0.036533816975556466}, \"8\": {\"line-1\": 0.036533816975556466}}}, {\"x\": 24999.0, \"y\": {\"1\": {\"line-1\": 0.12407743368207114}, \"2\": {\"line-1\": 0.12407743368207114}, \"3\": {\"line-1\": 0.12407743368207114}, \"4\": {\"line-1\": 0.12407743368207114}, \"5\": {\"line-1\": 0.12407743368207114}, \"6\": {\"line-1\": 0.12407743368207114}, \"7\": {\"line-1\": 0.12407743368207114}, \"8\": {\"line-1\": 0.12407743368207114}}}]);"
9919
  ],
9920
  "text/plain": [
9921
+ "<IPython.core.display.Javascript object>"
9922
  ]
9923
  },
9924
  "metadata": {},
 
9928
  "name": "stdout",
9929
  "output_type": "stream",
9930
  "text": [
9931
+ "FINISH\n",
9932
+ "Top 10\n",
9933
+ "/home/mendel/.local/lib/python3.7/site-packages/portilooplot/plot_learning_curve.py:186: size=32.0 MiB, count=287463, average=117 B\n",
9934
+ "/home/mendel/.local/lib/python3.7/site-packages/portilooplot/plot_learning_curve.py:188: size=10.1 MiB, count=58586, average=180 B\n",
9935
+ "/home/mendel/.local/lib/python3.7/site-packages/portilooplot/plot_learning_curve.py:185: size=4455 KiB, count=37997, average=120 B\n",
9936
+ "/tmp/ipykernel_5820/4036902884.py:18: size=464 KiB, count=19605, average=24 B\n",
9937
+ "/home/mendel/.local/lib/python3.7/site-packages/portilooplot/plot_learning_curve.py:184: size=464 KiB, count=19778, average=24 B\n",
9938
+ "/home/mendel/.local/lib/python3.7/site-packages/portilooplot/plot_learning_curve.py:191: size=174 KiB, count=1, average=174 KiB\n",
9939
+ "/home/mendel/.local/lib/python3.7/site-packages/IPython/core/display.py:350: size=163 KiB, count=1395, average=120 B\n",
9940
+ "/home/mendel/.local/lib/python3.7/site-packages/IPython/core/display.py:400: size=162 KiB, count=1378, average=120 B\n",
9941
+ "/usr/lib/python3.7/inspect.py:2882: size=131 KiB, count=800, average=168 B\n",
9942
+ "/home/mendel/.local/lib/python3.7/site-packages/decorator.py:203: size=122 KiB, count=1201, average=104 B\n"
9943
  ]
9944
  }
9945
  ],
9946
  "source": [
9947
+ "tracemalloc.start()\n",
9948
+ "\n",
9949
+ "channel_names = [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\"]\n",
9950
+ "window_len = 100\n",
9951
+ "pp=ProgressPlot(plot_names=channel_names, max_window_len=window_len)\n",
9952
+ "\n",
9953
+ "# from pyinstrument import Profiler\n",
9954
+ "# from pyinstrument.renderers import ConsoleRenderer\n",
9955
  "\n",
9956
+ "# pro = Profiler()\n",
9957
+ "\n",
9958
+ "# pro.start()\n",
9959
+ "\n",
9960
+ "for i in range(500):\n",
9961
+ " l = []\n",
9962
+ " nb_pts = 500 # random.randint(0, 5)\n",
9963
+ " for _ in range(nb_pts):\n",
9964
+ " l.append([[random.random()]] * 8)\n",
9965
+ " if i == 400:\n",
9966
+ " snapshot = tracemalloc.take_snapshot()\n",
9967
+ " top_stats= snapshot.statistics('lineno')\n",
9968
+ " pp.update_with_datapoints(l)\n",
9969
+ " \n",
9970
+ "print(\"FINISH\")\n",
9971
+ "\n",
9972
+ "print(\"Top 10\")\n",
9973
+ "for stat in top_stats[:10]:\n",
9974
+ " print(stat)\n",
9975
+ "\n",
9976
+ "# session = pro.stop()\n",
9977
+ "\n",
9978
+ "# cr = ConsoleRenderer(unicode=True, color=True, show_all=True)\n",
9979
+ "# print(cr.render(session))"
9980
+ ]
9981
+ },
9982
+ {
9983
+ "cell_type": "markdown",
9984
+ "id": "2f712706",
9985
+ "metadata": {},
9986
+ "source": [
9987
+ "# "
9988
  ]
9989
  }
9990
  ],
portiloop/notebooks/tests.ipynb ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "7b2fc5da",
7
+ "metadata": {
8
+ "scrolled": false
9
+ },
10
+ "outputs": [
11
+ {
12
+ "ename": "ModuleNotFoundError",
13
+ "evalue": "No module named 'pyedflib'",
14
+ "output_type": "error",
15
+ "traceback": [
16
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
17
+ "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
18
+ "\u001b[0;32m/tmp/ipykernel_3997/3113449869.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mportiloop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcapture\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mCapture\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mcap\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mCapture\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
19
+ "\u001b[0;32m~/capture_stream/portiloop-prog/portiloop/capture.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mwarnings\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mshutil\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mpyedflib\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mhighlevel\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mdatetime\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdatetime\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
20
+ "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'pyedflib'"
21
+ ]
22
+ }
23
+ ],
24
+ "source": [
25
+ "from portiloop.capture import Capture\n",
26
+ "\n",
27
+ "cap = Capture()"
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "code",
32
+ "execution_count": null,
33
+ "id": "475b8dde",
34
+ "metadata": {},
35
+ "outputs": [],
36
+ "source": []
37
+ }
38
+ ],
39
+ "metadata": {
40
+ "kernelspec": {
41
+ "display_name": "Python 3 (ipykernel)",
42
+ "language": "python",
43
+ "name": "python3"
44
+ },
45
+ "language_info": {
46
+ "codemirror_mode": {
47
+ "name": "ipython",
48
+ "version": 3
49
+ },
50
+ "file_extension": ".py",
51
+ "mimetype": "text/x-python",
52
+ "name": "python",
53
+ "nbconvert_exporter": "python",
54
+ "pygments_lexer": "ipython3",
55
+ "version": "3.7.3"
56
+ }
57
+ },
58
+ "nbformat": 4,
59
+ "nbformat_minor": 5
60
+ }
pyproject.toml DELETED
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools"]
3
- build-backend = "setuptools.build_meta"
 
 
 
 
setup.cfg DELETED
@@ -1,16 +0,0 @@
1
- [metadata]
2
- name=portiloop
3
- version=0.0.1
4
- description="Library for portiloop"
5
-
6
- [options]
7
- package_dir=
8
- =src
9
- packages=find:
10
- install_requires=
11
- matplotlib
12
- numpy
13
- portilooplot
14
-
15
- [options.packages.find]
16
- where=src
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
setup.py CHANGED
@@ -3,9 +3,10 @@ from setuptools import setup, find_packages
3
  setup(
4
  name='portiloop',
5
  version='0.0.1',
6
- packages=[package for package in find_packages(where='src')],
7
  description='Library for portiloop',
8
  install_requires=['numpy',
9
  'matplotlib',
10
- 'portilooplot']
 
11
  )
 
3
  setup(
4
  name='portiloop',
5
  version='0.0.1',
6
+ packages=[package for package in find_packages()],
7
  description='Library for portiloop',
8
  install_requires=['numpy',
9
  'matplotlib',
10
+ 'portilooplot',
11
+ 'ipywidgets']
12
  )
src/capture.py DELETED
@@ -1,363 +0,0 @@
1
- from abc import abstractmethod
2
- import threading
3
- import time
4
- import logging
5
- import random
6
- from queue import Queue
7
-
8
- from hardware.frontend import Frontend
9
- from hardware.leds import LEDs, Color
10
- from portilooplot.jupyter_plot import ProgressPlot
11
-
12
- import ctypes
13
- import numpy as np
14
- import json
15
-
16
- FRONTEND_CONFIG = [
17
- 0x3E, # ID (RO)
18
- 0x95, # CONFIG1 [95] [1, DAISY_EN(bar), CLK_EN, 1, 0, DR[2:0]] : Datarate = 500 SPS
19
- 0xD0, # CONFIG2 [C0] [1, 1, 0, INT_CAL, 0, CAL_AMP0, CAL_FREQ[1:0]]
20
- 0xE0, # CONFIG3 [E0] [PD_REFBUF(bar), 1, 1, BIAS_MEAS, BIASREF_INT, PD_BIAS(bar), BIAS_LOFF_SENS, BIAS_STAT] : Power-down reference buffer, no bias
21
- 0x00, # No lead-off
22
- 0x03, # CH1SET [60] [PD1, GAIN1[2:0], SRB2, MUX1[2:0]] test signal
23
- 0x00, # CH2SET
24
- 0x00, # CH3SET
25
- 0x00, # CH4SET voltage DVDD / 4
26
- 0x03, # CH5SET voltage 0.5 Γ— (AVDD + AVSS)
27
- 0x03, # CH6SET
28
- 0x05, # CH7SET test
29
- 0x04, # CH8SET temperature
30
- 0x00, # BIAS_SENSP
31
- 0x00, # BIAS_SENSN
32
- 0xFF, # LOFF_SENSP Lead-off on all positive pins?
33
- 0xFF, # LOFF_SENSN Lead-off on all negative pins?
34
- 0x00, # Normal lead-off
35
- 0x00, # Lead-off positive status (RO)
36
- 0x00, # Lead-off negative status (RO)
37
- 0x00, # All GPIOs as output ?
38
- 0x20, # Enable SRB1
39
- ]
40
-
41
- class Datapoint:
42
- '''
43
- Class to represent a single reading
44
- '''
45
- def __init__(self, raw_datapoint, temperature=[], num_channels=8):
46
- # Initialize necessary data structures
47
- self.num_channels = num_channels
48
- self.reading = np.empty(num_channels, dtype=float)
49
-
50
- assert len(temperature) <= len(raw_datapoint), "Temperature array length must be lesser or equal to number of channels"
51
- self.temperature = temperature
52
-
53
- self._filter_datapoint(raw_datapoint, Datapoint.filter_2scomplement)
54
-
55
- def _filter_datapoint(self, raw_datapoint, filter):
56
- # Filter one datapoint with the given filter
57
- assert len(raw_datapoint) == self.num_channels, "Datapoint dimensions do not match channel number"
58
- for idx, point in enumerate(raw_datapoint):
59
- # If the given index is a temperature, add that filter to get correct reading
60
- if idx in self.temperature:
61
- filter = lambda x : Datapoint.filter_temp(filter(x))
62
- self.reading[idx] = filter(point)
63
-
64
- def get_datapoint(self):
65
- '''
66
- Get readings of all channels in numpy array format
67
- '''
68
- return self.reading
69
-
70
- def get_channel(self, channel_idx):
71
- '''
72
- Reading at the channel specified by channel_idx (0-7)
73
- Returns a tuple (value(float), temperature(boolean)) --> temperature is True if channel is a temperature
74
- '''
75
- assert 0 <= channel_idx < self.num_channels - 1, "Channel index must be in range [0 - channel_num-1]"
76
- return self.reading[channel_idx], (channel_idx in self.temperature)
77
-
78
- def get_portilooplot(self):
79
- '''
80
- Returns the portilooplot ready version of the Datapoint
81
- '''
82
- return [[point] for point in self.reading]
83
-
84
- @staticmethod
85
- def filter_2scomplement(value):
86
- '''
87
- Convert from binary two's complement to binary int
88
- '''
89
- if (value & (1 << 23)) != 0:
90
- value = value - (1 << 24)
91
- return Datapoint.filter_23(value)
92
-
93
- @staticmethod
94
- def filter_23(value):
95
- '''
96
- Convert from binary int to normal int
97
- '''
98
- return (value * 4.5) / (2**23 - 1) # 23 because 1 bit is lost for sign
99
-
100
- @staticmethod
101
- def filter_temp(value):
102
- '''
103
- Convert from voltage reading to temperature reading in Celcius
104
- '''
105
- return int((value * 1000000.0 - 145300.0) / 490.0 + 25.0)
106
-
107
-
108
- class CaptureThread(threading.Thread):
109
- '''
110
- Producer thread which reads from the EEG device. Thread does not process the data
111
- '''
112
-
113
- def __init__(self, q, freq=250, timeout=None, target=None, name=None):
114
- super(CaptureThread, self).__init__()
115
- self.timeout = timeout
116
- self.target = target
117
- self.name = name
118
- self.q = q
119
- self.freq = freq
120
- self.frontend = Frontend()
121
- self.leds = LEDs()
122
-
123
- def run(self):
124
- '''
125
- Run the data capture continuously or until timeout
126
- '''
127
- self.init_checks()
128
- start_time = time.time()
129
- prev_ts = time.time()
130
- ts_len = 1 / self.freq
131
-
132
- while True:
133
- if not self.q.full():
134
- # Wait for frontend and for minimum time limit
135
- while not self.frontend.is_ready() and not time.time() - prev_ts >= ts_len:
136
- pass
137
- prev_ts = time.time()
138
-
139
- # Read values and add to q
140
- values = self.frontend.read()
141
- self.q.put(values.channels())
142
-
143
- # Wait until reading is fully ompleted
144
- while self.frontend.is_ready():
145
- pass
146
-
147
- # Check for timeout
148
- if self.timeout is not None:
149
- if time.time() - start_time > self.timeout:
150
- break
151
- return
152
-
153
- def init_checks(self):
154
- '''
155
- Run Initial threads to the registers to make sure we can start reading
156
- '''
157
- print("Configuring EEG Frontend")
158
- self.frontend.write_regs(0x00, FRONTEND_CONFIG)
159
- data = self.frontend.read_regs(0x00, len(FRONTEND_CONFIG))
160
- assert data == FRONTEND_CONFIG, f"Wrong config: {data} vs {FRONTEND_CONFIG}"
161
- self.frontend.start()
162
- print("EEG Frontend configured")
163
- self.leds.led2(Color.PURPLE)
164
- while not self.frontend.is_ready():
165
- pass
166
- print("Ready for data")
167
-
168
-
169
- class FilterThread(threading.Thread):
170
- def __init__(self, q, target=None, name=None, temperature=[], num_channels=8):
171
- '''
172
- Consume raw datapoint from the Capture points, filter them, put resulting datapoint objects into all queues in list
173
- '''
174
- super(FilterThread, self).__init__()
175
- self.target = target
176
- self.name = name
177
-
178
- # Initialize thread safe datastructures for both consuming and producing
179
- self.raw_q = q
180
- self.qs = []
181
-
182
- # Initilialize settings for the filtering
183
- self.temperature = temperature
184
- self.num_channels = num_channels
185
-
186
- return
187
-
188
- def run(self):
189
- while True:
190
- raw_data = None
191
- # Get an item from CaptureThread
192
- if not self.raw_q.empty():
193
- raw_data = self.raw_q.get()
194
- # assert raw_data is not None, "Got a None item from CaptureThread in FilterThread"
195
- if raw_data is None:
196
- continue
197
- datapoint = Datapoint(raw_data, )
198
-
199
- # Put Item to all ConsumerThreads
200
- for q in self.qs:
201
- if not q.full():
202
- q.put(datapoint)
203
- return
204
-
205
- def add_q(self, q):
206
- '''
207
- Add a Queue to the list of queues where filtered values get added
208
- '''
209
- self.qs.append(q)
210
-
211
- def remove_q(self, q):
212
- '''
213
- Remove a queue from the list
214
- '''
215
- self.qs.remove(q)
216
-
217
- def update_settings(self, temperature=None, num_channels=None):
218
- '''
219
- Update Settings on the go
220
- '''
221
- if self.temperatures is not None:
222
- self.temperature = temperature
223
- if num_channels is not None:
224
- self.num_channels = num_channels
225
-
226
-
227
- class ConsumerThread(threading.Thread):
228
- def __init__(self, q, target=None, name=None):
229
- '''
230
- Implemetns basic consumer logic, needs _consume_item() to be implemented
231
- '''
232
- super(ConsumerThread,self).__init__()
233
- self.target = target
234
- self.name = name
235
- self.q = q
236
-
237
- def run(self):
238
- try:
239
- while True:
240
- item = None
241
- if not self.q.empty():
242
- item = self.q.get()
243
-
244
- assert item is not None, "Got a None value from FilterThread in ConsumerThread"
245
- self._consume_item(item)
246
- except Exception:
247
- self._on_exit()
248
-
249
- def get_id(self):
250
-
251
- # returns id of the respective thread
252
- if hasattr(self, '_thread_id'):
253
- return self._thread_id
254
- for id, thread in threading._active.items():
255
- if thread is self:
256
- return id
257
-
258
- def raise_exception(self):
259
- thread_id = self.get_id()
260
- res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
261
- ctypes.py_object(SystemExit))
262
- if res > 1:
263
- ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
264
- print('Exception raise failure')
265
-
266
- @abstractmethod
267
- def _consume_item(self, item):
268
- raise NotImplementedError("_consume_item needs to be implemented in Subclass of ConsumerThread")
269
-
270
- @abstractmethod
271
- def _on_exit(self):
272
- raise NotImplementedError("_on_exit needs to be implemented in Subclass of ConsumerThread")
273
-
274
-
275
- class DisplayThread(ConsumerThread):
276
- def __init__(self, q, max_window_len=100, num_channel=8, target=None, name=None):
277
- super().__init__(q, target, name)
278
- self.pp = ProgressPlot(plot_names=[f"channel#{i+1}" for i in range(num_channel)], max_window_len=max_window_len)
279
-
280
- def _consume_item(self, item):
281
- self.pp.update(item.get_portilooplot())
282
-
283
- def _on_exit(self):
284
- self.pp.finalize()
285
-
286
-
287
- class SaveThread(ConsumerThread):
288
- def __init__(self, q, default_loc='', target=None, name=None):
289
- super().__init__(q, target, name)
290
- self.save = []
291
- self.default_loc = default_loc
292
-
293
- def to_disk(self, destination):
294
- print('Saving Method is not yet implemented')
295
- pass
296
-
297
- def _consume_item(self, item):
298
- self.save.append(item.get_datapoint().to_list())
299
-
300
- def _on_exit(self):
301
- self.to_disk(self.default_loc)
302
-
303
-
304
- class Capture:
305
- def __init__(self, viz=True, record=True):
306
- self.viz = viz
307
- self.record = record
308
-
309
- # Initialize data structures for capture and filtering
310
- raw_q = Queue()
311
- self.capture_thread = CaptureThread(raw_q, name='CapThread')
312
- self.filter_thread = FilterThread(raw_q, name='FilterThread')
313
-
314
- # Declare data structures for viz and record functionality
315
- self.viz_q = None
316
- self.record_q = None
317
- self.viz_thread = None
318
- self.record_thread = None
319
-
320
- self.capture_thread.start()
321
- self.filter_thread.start()
322
-
323
- if viz:
324
- self.start_viz()
325
-
326
- if record:
327
- self.start_record()
328
-
329
- def start_viz(self):
330
- self.viz_q = Queue()
331
- self.viz_thread = DisplayThread(self.viz_q, name='VizThread')
332
- self.filter_thread.add_q(self.viz_q)
333
- self.viz_thread.start()
334
-
335
- def stop_viz(self):
336
- self.filter_thread.remove_q(self.viz_q)
337
- self.viz_q = None
338
- self.viz_thread.raise_exception()
339
-
340
- def start_record(self):
341
- self.record_q = Queue()
342
- self.record_thread = SaveThread(self.record_q, name='RecThread')
343
- self.filter_thread.add_q(self.record_q)
344
- self.record_thread.start()
345
-
346
- def stop_record(self):
347
- self.filter_thread.remove_q(self.viz_q)
348
- self.viz_q = None
349
- self.record_thread.raise_exception()
350
-
351
- def save(self, destination=None):
352
- if destination is not None:
353
- self.record_thread.save(destination)
354
- else:
355
- self.record_thread.save()
356
-
357
-
358
-
359
-
360
-
361
- if __name__ == "__main__":
362
- # TODO: Argparse this
363
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/notebooks/tests.ipynb DELETED
The diff for this file is too large to render. See raw diff