Spaces:
Running
Running
msobral
commited on
Commit
•
de0bff3
1
Parent(s):
e614a99
create package and added recent code
Browse files- pyproject.toml +3 -0
- setup.cfg +16 -0
- setup.py +11 -0
- src/__init__.py +0 -0
- src/capture.py +45 -17
- src/hardware/__init__.py +0 -0
- src/hardware/frontend.py +7 -1
- src/notebooks/test_capture.ipynb +0 -0
- src/notebooks/tests.ipynb +0 -0
pyproject.toml
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
[build-system]
|
2 |
+
requires = ["setuptools"]
|
3 |
+
build-backend = "setuptools.build_meta"
|
setup.cfg
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from setuptools import setup, find_packages
|
2 |
+
|
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 |
+
)
|
src/__init__.py
ADDED
File without changes
|
src/capture.py
CHANGED
@@ -3,16 +3,40 @@ import threading
|
|
3 |
import time
|
4 |
import logging
|
5 |
import random
|
6 |
-
import Queue
|
7 |
|
8 |
-
from frontend import Frontend
|
9 |
-
from 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 |
|
17 |
class Datapoint:
|
18 |
'''
|
@@ -21,7 +45,7 @@ class Datapoint:
|
|
21 |
def __init__(self, raw_datapoint, temperature=[], num_channels=8):
|
22 |
# Initialize necessary data structures
|
23 |
self.num_channels = num_channels
|
24 |
-
self.reading = np.
|
25 |
|
26 |
assert len(temperature) <= len(raw_datapoint), "Temperature array length must be lesser or equal to number of channels"
|
27 |
self.temperature = temperature
|
@@ -114,21 +138,24 @@ class CaptureThread(threading.Thread):
|
|
114 |
|
115 |
# Read values and add to q
|
116 |
values = self.frontend.read()
|
117 |
-
self.q.put(values)
|
118 |
|
119 |
# Wait until reading is fully ompleted
|
120 |
while self.frontend.is_ready():
|
121 |
pass
|
122 |
|
123 |
# Check for timeout
|
124 |
-
if
|
125 |
-
|
|
|
126 |
return
|
127 |
|
128 |
def init_checks(self):
|
129 |
'''
|
130 |
Run Initial threads to the registers to make sure we can start reading
|
131 |
'''
|
|
|
|
|
132 |
data = self.frontend.read_regs(0x00, len(FRONTEND_CONFIG))
|
133 |
assert data == FRONTEND_CONFIG, f"Wrong config: {data} vs {FRONTEND_CONFIG}"
|
134 |
self.frontend.start()
|
@@ -164,14 +191,15 @@ class FilterThread(threading.Thread):
|
|
164 |
# Get an item from CaptureThread
|
165 |
if not self.raw_q.empty():
|
166 |
raw_data = self.raw_q.get()
|
167 |
-
|
168 |
-
|
|
|
169 |
datapoint = Datapoint(raw_data, )
|
170 |
|
171 |
# Put Item to all ConsumerThreads
|
172 |
for q in self.qs:
|
173 |
if not q.full():
|
174 |
-
q.put(
|
175 |
return
|
176 |
|
177 |
def add_q(self, q):
|
@@ -279,9 +307,9 @@ class Capture:
|
|
279 |
self.record = record
|
280 |
|
281 |
# Initialize data structures for capture and filtering
|
282 |
-
raw_q = Queue
|
283 |
-
self.capture_thread = CaptureThread(raw_q)
|
284 |
-
self.filter_thread = FilterThread(raw_q)
|
285 |
|
286 |
# Declare data structures for viz and record functionality
|
287 |
self.viz_q = None
|
@@ -299,8 +327,8 @@ class Capture:
|
|
299 |
self.start_record()
|
300 |
|
301 |
def start_viz(self):
|
302 |
-
self.viz_q = Queue
|
303 |
-
self.viz_thread = DisplayThread(self.viz_q)
|
304 |
self.filter_thread.add_q(self.viz_q)
|
305 |
self.viz_thread.start()
|
306 |
|
@@ -310,8 +338,8 @@ class Capture:
|
|
310 |
self.viz_thread.raise_exception()
|
311 |
|
312 |
def start_record(self):
|
313 |
-
self.record_q = Queue
|
314 |
-
self.record_thread = SaveThread(self.record_q)
|
315 |
self.filter_thread.add_q(self.record_q)
|
316 |
self.record_thread.start()
|
317 |
|
|
|
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 |
'''
|
|
|
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
|
|
|
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()
|
|
|
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):
|
|
|
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
|
|
|
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 |
|
|
|
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 |
|
src/hardware/__init__.py
ADDED
File without changes
|
src/hardware/frontend.py
CHANGED
@@ -51,6 +51,7 @@ class Frontend:
|
|
51 |
self.pwdn = GPIO("/dev/gpiochip2", 12, "out")
|
52 |
self._start = GPIO("/dev/gpiochip3", 29, "out")
|
53 |
self.drdy = GPIO("/dev/gpiochip3", 28, "in")
|
|
|
54 |
self.dev = SpiDev()
|
55 |
self.dev.open(0, 0)
|
56 |
self.dev.max_speed_hz = 1000000
|
@@ -105,6 +106,11 @@ class Frontend:
|
|
105 |
|
106 |
def is_ready(self):
|
107 |
return not self.drdy.read()
|
|
|
|
|
|
|
|
|
|
|
108 |
|
109 |
def close(self):
|
110 |
-
self.dev.close()
|
|
|
51 |
self.pwdn = GPIO("/dev/gpiochip2", 12, "out")
|
52 |
self._start = GPIO("/dev/gpiochip3", 29, "out")
|
53 |
self.drdy = GPIO("/dev/gpiochip3", 28, "in")
|
54 |
+
self.drdy.edge = "falling"
|
55 |
self.dev = SpiDev()
|
56 |
self.dev.open(0, 0)
|
57 |
self.dev.max_speed_hz = 1000000
|
|
|
106 |
|
107 |
def is_ready(self):
|
108 |
return not self.drdy.read()
|
109 |
+
|
110 |
+
def wait_new_data(self):
|
111 |
+
self.drdy.poll(timeout=None) # poll the falling edge event
|
112 |
+
self.drdy.read_event() # consume the event
|
113 |
+
return self.read() # read SPI with RDATA
|
114 |
|
115 |
def close(self):
|
116 |
+
self.dev.close()
|
src/notebooks/test_capture.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
src/notebooks/tests.ipynb
CHANGED
The diff for this file is too large to render.
See raw diff
|
|