Spaces:
Sleeping
Sleeping
msobral
commited on
Commit
Β·
963de37
1
Parent(s):
de0bff3
merge with backup
Browse files- .gitignore +4 -0
- __pycache__/frontend.cpython-37.pyc +0 -0
- __pycache__/leds.cpython-37.pyc +0 -0
- {src β portiloop}/__init__.py +0 -0
- portiloop/capture.py +487 -0
- {src β portiloop}/demo/acquisition_demo.py +0 -0
- {src β portiloop}/demo/led_demo.py +0 -0
- {src β portiloop}/hardware/__init__.py +0 -0
- {src β portiloop}/hardware/frontend.py +0 -0
- {src β portiloop}/hardware/leds.py +0 -0
- {src β portiloop}/nn/demo_net.py +0 -0
- {src β portiloop}/notebooks/test_capture.ipynb +105 -16
- portiloop/notebooks/tests.ipynb +60 -0
- pyproject.toml +0 -3
- setup.cfg +0 -16
- setup.py +3 -2
- src/capture.py +0 -363
- src/notebooks/tests.ipynb +0 -0
.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=\"
|
9867 |
],
|
9868 |
"text/plain": [
|
9869 |
"<IPython.core.display.HTML object>"
|
@@ -9874,11 +9914,11 @@
|
|
9874 |
},
|
9875 |
{
|
9876 |
"data": {
|
9877 |
-
"
|
9878 |
-
"<script> window.appendLearningCurve([]);</script>"
|
9879 |
],
|
9880 |
"text/plain": [
|
9881 |
-
"<IPython.core.display.
|
9882 |
]
|
9883 |
},
|
9884 |
"metadata": {},
|
@@ -9888,14 +9928,63 @@
|
|
9888 |
"name": "stdout",
|
9889 |
"output_type": "stream",
|
9890 |
"text": [
|
9891 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9892 |
]
|
9893 |
}
|
9894 |
],
|
9895 |
"source": [
|
9896 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9897 |
"\n",
|
9898 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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(
|
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
|
|