MiloSobral commited on
Commit
35af352
1 Parent(s): 2e2e678

Implemented stimulators for trains of spindles

Browse files
portiloop/notebooks/tests.ipynb CHANGED
@@ -2,52 +2,23 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 1,
6
  "id": "16651843",
7
  "metadata": {
8
  "scrolled": false
9
  },
10
- "outputs": [
11
- {
12
- "data": {
13
- "application/vnd.jupyter.widget-view+json": {
14
- "model_id": "f46843d136af4c79a73841b997fa3284",
15
- "version_major": 2,
16
- "version_minor": 0
17
- },
18
- "text/plain": [
19
- "VBox(children=(Accordion(children=(GridBox(children=(Label(value='CH2'), Label(value='CH3'), Label(value='CH4'…"
20
- ]
21
- },
22
- "metadata": {},
23
- "output_type": "display_data"
24
- },
25
- {
26
- "name": "stderr",
27
- "output_type": "stream",
28
- "text": [
29
- "Exception in thread Thread-3:\n",
30
- "Traceback (most recent call last):\n",
31
- " File \"C:\\Users\\milos\\AppData\\Local\\Programs\\Python\\Python37\\lib\\threading.py\", line 917, in _bootstrap_inner\n",
32
- " self.run()\n",
33
- " File \"C:\\Users\\milos\\AppData\\Local\\Programs\\Python\\Python37\\lib\\threading.py\", line 865, in run\n",
34
- " self._target(*self._args, **self._kwargs)\n",
35
- " File \"c:\\users\\milos\\documents\\github\\portiloop-software\\portiloop\\src\\capture.py\", line 927, in start_capture\n",
36
- " detector = detector_cls(threshold, channel=channel) if detector_cls is not None else None\n",
37
- " File \"c:\\users\\milos\\documents\\github\\portiloop-software\\portiloop\\src\\detection.py\", line 56, in __init__\n",
38
- " self.interpreters.append(edgetpu.make_interpreter(model_path))\n",
39
- "NameError: name 'edgetpu' is not defined\n",
40
- "\n"
41
- ]
42
- }
43
- ],
44
  "source": [
45
  "from portiloop.src.capture import Capture\n",
46
  "from portiloop.src.detection import SleepSpindleRealTimeDetector\n",
47
- "from portiloop.src.stimulation import SleepSpindleRealTimeStimulator\n",
 
 
48
  "\n",
49
  "my_detector_class = SleepSpindleRealTimeDetector # you may want to implement yours\n",
50
- "my_stimulator_class = SleepSpindleRealTimeStimulator # you may also want to implement yours\n",
 
 
51
  "\n",
52
  "cap = Capture(detector_cls=my_detector_class, stimulator_cls=my_stimulator_class)"
53
  ]
@@ -55,7 +26,7 @@
55
  ],
56
  "metadata": {
57
  "kernelspec": {
58
- "display_name": "Python 3.7.0b2 ('venv': venv)",
59
  "language": "python",
60
  "name": "python3"
61
  },
@@ -69,7 +40,7 @@
69
  "name": "python",
70
  "nbconvert_exporter": "python",
71
  "pygments_lexer": "ipython3",
72
- "version": "3.7.0b2"
73
  },
74
  "vscode": {
75
  "interpreter": {
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
+ "execution_count": null,
6
  "id": "16651843",
7
  "metadata": {
8
  "scrolled": false
9
  },
10
+ "outputs": [],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  "source": [
12
  "from portiloop.src.capture import Capture\n",
13
  "from portiloop.src.detection import SleepSpindleRealTimeDetector\n",
14
+ "from portiloop.src.stimulation import (SleepSpindleRealTimeStimulator,\n",
15
+ " SpindleTrainRealTimeStimulator,\n",
16
+ " IsolatedSpindleRealTimeStimulator)\n",
17
  "\n",
18
  "my_detector_class = SleepSpindleRealTimeDetector # you may want to implement yours\n",
19
+ "my_stimulator_class = SleepSpindleRealTimeStimulator # all spindles\n",
20
+ "# my_stimulator_class = SpindleTrainRealTimeStimulator # uncomment for spindle trains only\n",
21
+ "# my_stimulator_class = IsolatedSpindleRealTimeStimulator # uncomment for isolated spindles only\n",
22
  "\n",
23
  "cap = Capture(detector_cls=my_detector_class, stimulator_cls=my_stimulator_class)"
24
  ]
 
26
  ],
27
  "metadata": {
28
  "kernelspec": {
29
+ "display_name": "Python 3",
30
  "language": "python",
31
  "name": "python3"
32
  },
 
40
  "name": "python",
41
  "nbconvert_exporter": "python",
42
  "pygments_lexer": "ipython3",
43
+ "version": "3.7.3"
44
  },
45
  "vscode": {
46
  "interpreter": {
portiloop/src/capture.py CHANGED
@@ -1013,8 +1013,11 @@ class Capture:
1013
  with self._lock_msg_out:
1014
  if self._msg_out == "STOP":
1015
  break
1016
-
1017
- index, raw_point, off_filtered_point, past_stimulation, lacourse_stimulation = file_reader.get_point()
 
 
 
1018
  n_array_raw = np.array([0, raw_point, 0, 0, 0, 0, 0, 0])
1019
  n_array_raw = np.reshape(n_array_raw, (1, 8))
1020
 
 
1013
  with self._lock_msg_out:
1014
  if self._msg_out == "STOP":
1015
  break
1016
+
1017
+ file_point = file_reader.get_point()
1018
+ if file_point is None:
1019
+ break
1020
+ index, raw_point, off_filtered_point, past_stimulation, lacourse_stimulation = file_point
1021
  n_array_raw = np.array([0, raw_point, 0, 0, 0, 0, 0, 0])
1022
  n_array_raw = np.reshape(n_array_raw, (1, 8))
1023
 
portiloop/src/detection.py CHANGED
@@ -14,14 +14,12 @@ import numpy as np
14
 
15
  class Detector(ABC):
16
 
17
- def __init__(self, threshold=None):
18
  """
19
- If implementing __init__() in your subclass, it must take threshold as a keyword argument.
20
- This is the value of the threshold that the user can set in the Portiloop GUI.
21
- Caution: even if you don't need this manual threshold in your application,
22
- your implementation of __init__() still needs to have this keyword argument.
23
  """
24
  self.threshold = threshold
 
25
 
26
  @abstractmethod
27
  def detect(self, datapoints):
@@ -47,10 +45,16 @@ DEFAULT_MODEL_PATH = str(Path(__file__).parent.parent / "models/portiloop_model_
47
  # print(DEFAULT_MODEL_PATH)
48
 
49
  class SleepSpindleRealTimeDetector(Detector):
50
- def __init__(self, threshold=0.5, num_models_parallel=8, window_size=54, seq_stride=42, model_path=None, verbose=False, channel=2):
 
 
 
 
 
 
 
51
  model_path = DEFAULT_MODEL_PATH if model_path is None else model_path
52
  self.verbose = verbose
53
- self.channel = channel
54
  self.num_models_parallel = num_models_parallel
55
 
56
  self.interpreters = []
@@ -78,12 +82,15 @@ class SleepSpindleRealTimeDetector(Detector):
78
 
79
  self.current_stride_counter = self.stride_counters[0] - 1
80
 
81
- super().__init__(threshold)
82
 
83
  def detect(self, datapoints):
84
  """
85
  Takes datapoints as input and outputs a detection signal.
86
  datapoints is a list of lists of n channels: may contain several datapoints.
 
 
 
87
  """
88
  res = []
89
  for inp in datapoints:
 
14
 
15
  class Detector(ABC):
16
 
17
+ def __init__(self, threshold=None, channel=None):
18
  """
19
+ Mandatory arguments are from the in the Portiloop GUI.
 
 
 
20
  """
21
  self.threshold = threshold
22
+ self.channel = channel
23
 
24
  @abstractmethod
25
  def detect(self, datapoints):
 
45
  # print(DEFAULT_MODEL_PATH)
46
 
47
  class SleepSpindleRealTimeDetector(Detector):
48
+ def __init__(self,
49
+ threshold=0.5,
50
+ num_models_parallel=8,
51
+ window_size=54,
52
+ seq_stride=42,
53
+ model_path=None,
54
+ verbose=False,
55
+ channel=2):
56
  model_path = DEFAULT_MODEL_PATH if model_path is None else model_path
57
  self.verbose = verbose
 
58
  self.num_models_parallel = num_models_parallel
59
 
60
  self.interpreters = []
 
82
 
83
  self.current_stride_counter = self.stride_counters[0] - 1
84
 
85
+ super().__init__(threshold, channel)
86
 
87
  def detect(self, datapoints):
88
  """
89
  Takes datapoints as input and outputs a detection signal.
90
  datapoints is a list of lists of n channels: may contain several datapoints.
91
+
92
+ The output signal is a list of tuples (is_spindle, is_train_of_spindles).
93
+
94
  """
95
  res = []
96
  for inp in datapoints:
portiloop/src/stimulation.py CHANGED
@@ -17,8 +17,6 @@ from scipy.signal import find_peaks
17
  import numpy as np
18
  import matplotlib.pyplot as plt
19
 
20
- import alsaaudio
21
- import pylsl
22
 
23
 
24
  # Abstract interface for developers:
@@ -153,6 +151,55 @@ class SleepSpindleRealTimeStimulator(Stimulator):
153
  self.delayer = delayer
154
  self.delayer.stimulate = lambda: self.send_stimulation("DELAY_STIM", True)
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  # Class that delays stimulation to always stimulate peak or through
157
  class UpStateDelayer:
158
  def __init__(self, sample_freq, peak, time_to_buffer, stimulate=None):
 
17
  import numpy as np
18
  import matplotlib.pyplot as plt
19
 
 
 
20
 
21
 
22
  # Abstract interface for developers:
 
151
  self.delayer = delayer
152
  self.delayer.stimulate = lambda: self.send_stimulation("DELAY_STIM", True)
153
 
154
+
155
+ class SpindleTrainRealTimeStimulator(SleepSpindleRealTimeStimulator):
156
+ def __init__(self):
157
+ self.max_spindle_train_t = 6.0
158
+ super().__init__()
159
+
160
+ def stimulate(self, detection_signal):
161
+ for sig in detection_signal:
162
+ # We detect a stimulation
163
+ if sig:
164
+ # Record time of stimulation
165
+ ts = time.time()
166
+
167
+ # Check if time since last stimulation is long enough
168
+ elapsed = ts - self.last_detected_ts
169
+ if self.wait_t < elapsed < self.max_spindle_train_t:
170
+ if self.delayer is not None:
171
+ # If we have a delayer, notify it
172
+ self.delayer.detected()
173
+ # Send the LSL marer for the fast stimulation
174
+ self.send_stimulation("FAST_STIM", False)
175
+ else:
176
+ self.send_stimulation("STIM", True)
177
+
178
+ self.last_detected_ts = ts
179
+
180
+
181
+ class IsolatedSpindleRealTimeStimulator(SpindleTrainRealTimeStimulator):
182
+ def stimulate(self, detection_signal):
183
+ for sig in detection_signal:
184
+ # We detect a stimulation
185
+ if sig:
186
+ # Record time of stimulation
187
+ ts = time.time()
188
+
189
+ # Check if time since last stimulation is long enough
190
+ elapsed = ts - self.last_detected_ts
191
+ if self.max_spindle_train_t < elapsed:
192
+ if self.delayer is not None:
193
+ # If we have a delayer, notify it
194
+ self.delayer.detected()
195
+ # Send the LSL marer for the fast stimulation
196
+ self.send_stimulation("FAST_STIM", False)
197
+ else:
198
+ self.send_stimulation("STIM", True)
199
+
200
+ self.last_detected_ts = ts
201
+
202
+
203
  # Class that delays stimulation to always stimulate peak or through
204
  class UpStateDelayer:
205
  def __init__(self, sample_freq, peak, time_to_buffer, stimulate=None):
portiloop/src/utils.py CHANGED
@@ -118,9 +118,12 @@ class FileReader:
118
  """
119
  Returns the next point in the file
120
  """
121
- point = next(self.csv_reader)
122
- self.index += 1
123
- while time.time() - self.last_time < self.wait_time:
124
- continue
125
- self.last_time = time.time()
126
- return self.index, float(point[0]), float(point[1]), point[2] == '1', point[3] == '1'
 
 
 
 
118
  """
119
  Returns the next point in the file
120
  """
121
+ try:
122
+ point = next(self.csv_reader)
123
+ self.index += 1
124
+ while time.time() - self.last_time < self.wait_time:
125
+ continue
126
+ self.last_time = time.time()
127
+ return self.index, float(point[0]), float(point[1]), point[2] == '1', point[3] == '1'
128
+ except StopIteration:
129
+ return None
setup.py CHANGED
@@ -1,27 +1,39 @@
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()],
7
  description='Portiloop software library',
8
- install_requires=['wheel',
9
- 'EDFlib-Python',
10
- 'numpy',
11
- 'portilooplot',
12
- 'ipywidgets',
13
- 'python-periphery',
14
- 'scipy',
15
- 'matplotlib',
16
- ],
17
- extras_require={
18
- 'Portiloop': ['pycoral',
19
- 'spidev',
20
- 'pylsl-coral',
21
- 'pyalsaaudio'],
22
- 'PC': ['gradio',
23
- 'tensorflow',
24
- 'pyxdf',
25
- 'wonambi']
26
- },
27
  )
 
1
  from setuptools import setup, find_packages
2
+ import io
3
+
4
+
5
+ def is_coral():
6
+ try:
7
+ with io.open('/sys/firmware/devicetree/base/model', 'r') as m:
8
+ if 'phanbell' in m.read().lower(): return True
9
+ except Exception: pass
10
+ return False
11
+
12
+ requirements_list = ['wheel',
13
+ 'EDFlib-Python',
14
+ 'numpy',
15
+ 'portilooplot',
16
+ 'ipywidgets',
17
+ 'python-periphery',
18
+ 'scipy',
19
+ 'matplotlib']
20
+
21
+ if is_coral():
22
+ requirements_list += ['pycoral',
23
+ 'spidev',
24
+ 'pylsl-coral',
25
+ 'pyalsaaudio']
26
+ else:
27
+ requirements_list += ['gradio',
28
+ 'tensorflow',
29
+ 'pyxdf',
30
+ 'wonambi']
31
+
32
 
33
  setup(
34
  name='portiloop',
35
  version='0.0.1',
36
  packages=[package for package in find_packages()],
37
  description='Portiloop software library',
38
+ install_requires=requirements_list,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  )