Milo Sobral commited on
Commit
b6be113
2 Parent(s): f74a1ac 321613a

Merge pull request #6 from Portiloop/milo/dev

Browse files

Added ability to read from file and fixed delayer bugs

portiloop/src/capture.py CHANGED
@@ -20,7 +20,7 @@ from portiloop.src.stimulation import UpStateDelayer
20
 
21
  from portiloop.src.processing import FilterPipeline, int_to_float
22
  from portiloop.src.config import mod_config, LEADOFF_CONFIG, FRONTEND_CONFIG, to_ads_frequency
23
- from portiloop.src.utils import FileReader, LiveDisplay, DummyAlsaMixer, EDFRecorder, EDF_PATH
24
  from IPython.display import clear_output, display
25
  import ipywidgets as widgets
26
 
@@ -500,6 +500,7 @@ class Capture:
500
 
501
  self.b_capture.observe(self.on_b_capture, 'value')
502
  self.b_clock.observe(self.on_b_clock, 'value')
 
503
  self.b_frequency.observe(self.on_b_frequency, 'value')
504
  self.b_threshold.observe(self.on_b_threshold, 'value')
505
  self.b_duration.observe(self.on_b_duration, 'value')
@@ -909,6 +910,8 @@ class Capture:
909
  self.__capture_on = True
910
  p_msg_io, p_msg_io_2 = mp.Pipe()
911
  p_data_i, p_data_o = mp.Pipe(duplex=False)
 
 
912
 
913
  # Initialize filtering pipeline
914
  if filter:
@@ -941,7 +944,7 @@ class Capture:
941
  self._p_capture.start()
942
  print(f"PID capture: {self._p_capture.pid}")
943
  else:
944
- filename = "INSERT FILENAME" # TODO
945
  file_reader = FileReader(filename)
946
 
947
  # Initialize display if requested
@@ -974,7 +977,7 @@ class Capture:
974
 
975
  # Initialize stimulation delayer if requested
976
  if not self.spindle_detection_mode == 'Fast' and stimulator is not None:
977
- stimulation_delayer = UpStateDelayer(self.frequency, self.spindle_freq, self.spindle_detection_mode == 'Peak', time_to_buffer=0.1)
978
  stimulator.add_delayer(stimulation_delayer)
979
  else:
980
  stimulation_delayer = None
@@ -1006,7 +1009,14 @@ class Capture:
1006
  # Convert point from int to corresponding value in microvolts
1007
  n_array_raw = int_to_float(np.array([point]))
1008
  elif self.signal_input == "File":
1009
- n_array_raw, gt_stimulation = file_reader.get_point()
 
 
 
 
 
 
 
1010
 
1011
  # Go through filtering pipeline
1012
  if filter:
@@ -1025,7 +1035,7 @@ class Capture:
1025
 
1026
  # Adds point to buffer for delayed stimulation
1027
  if stimulation_delayer is not None:
1028
- stimulation_delayer.step(filtered_point[0][channel-1])
1029
 
1030
  # Check if detection is on or off
1031
  with self._pause_detect_lock:
@@ -1045,8 +1055,10 @@ class Capture:
1045
  if test_stimulus:
1046
  stimulator.test_stimulus()
1047
 
1048
- if self.signal_input == "File" and gt_stimulation:
1049
- stimulator.send_stimulation("GROUND_TRUTH_STIM", False)
 
 
1050
 
1051
  # Add point to the buffer to send to viz and recorder
1052
  buffer += filtered_point
@@ -1070,7 +1082,7 @@ class Capture:
1070
  p_data_i.close()
1071
  p_msg_io.close()
1072
  self._p_capture.join()
1073
- self.__capture_on = False
1074
 
1075
  if record:
1076
  recorder.close_recording_file()
 
20
 
21
  from portiloop.src.processing import FilterPipeline, int_to_float
22
  from portiloop.src.config import mod_config, LEADOFF_CONFIG, FRONTEND_CONFIG, to_ads_frequency
23
+ from portiloop.src.utils import FileReader, LiveDisplay, DummyAlsaMixer, EDFRecorder, EDF_PATH, RECORDING_PATH
24
  from IPython.display import clear_output, display
25
  import ipywidgets as widgets
26
 
 
500
 
501
  self.b_capture.observe(self.on_b_capture, 'value')
502
  self.b_clock.observe(self.on_b_clock, 'value')
503
+ self.b_signal_input.observe(self.on_b_signal_input, 'value')
504
  self.b_frequency.observe(self.on_b_frequency, 'value')
505
  self.b_threshold.observe(self.on_b_threshold, 'value')
506
  self.b_duration.observe(self.on_b_duration, 'value')
 
910
  self.__capture_on = True
911
  p_msg_io, p_msg_io_2 = mp.Pipe()
912
  p_data_i, p_data_o = mp.Pipe(duplex=False)
913
+ else:
914
+ p_msg_io, _ = mp.Pipe()
915
 
916
  # Initialize filtering pipeline
917
  if filter:
 
944
  self._p_capture.start()
945
  print(f"PID capture: {self._p_capture.pid}")
946
  else:
947
+ filename = RECORDING_PATH / 'test_recording.csv'
948
  file_reader = FileReader(filename)
949
 
950
  # Initialize display if requested
 
977
 
978
  # Initialize stimulation delayer if requested
979
  if not self.spindle_detection_mode == 'Fast' and stimulator is not None:
980
+ stimulation_delayer = UpStateDelayer(self.frequency, self.spindle_detection_mode == 'Peak', 0.3)
981
  stimulator.add_delayer(stimulation_delayer)
982
  else:
983
  stimulation_delayer = None
 
1009
  # Convert point from int to corresponding value in microvolts
1010
  n_array_raw = int_to_float(np.array([point]))
1011
  elif self.signal_input == "File":
1012
+ # Check if the message to stop has been sent
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
 
1021
  # Go through filtering pipeline
1022
  if filter:
 
1035
 
1036
  # Adds point to buffer for delayed stimulation
1037
  if stimulation_delayer is not None:
1038
+ stimulation_delayer.step_timesteps(filtered_point[0][channel-1])
1039
 
1040
  # Check if detection is on or off
1041
  with self._pause_detect_lock:
 
1055
  if test_stimulus:
1056
  stimulator.test_stimulus()
1057
 
1058
+ # Send the stimulation from the file reader
1059
+ if stimulator is not None:
1060
+ if self.signal_input == "File" and lacourse_stimulation:
1061
+ stimulator.send_stimulation("GROUND_TRUTH_STIM", False)
1062
 
1063
  # Add point to the buffer to send to viz and recorder
1064
  buffer += filtered_point
 
1082
  p_data_i.close()
1083
  p_msg_io.close()
1084
  self._p_capture.join()
1085
+ self.__capture_on = False
1086
 
1087
  if record:
1088
  recorder.close_recording_file()
portiloop/src/stimulation.py CHANGED
@@ -14,6 +14,11 @@ if ADS:
14
 
15
  import wave
16
  from scipy.signal import find_peaks
 
 
 
 
 
17
 
18
 
19
  # Abstract interface for developers:
@@ -54,14 +59,14 @@ class SleepSpindleRealTimeStimulator(Stimulator):
54
  channel_format='string',
55
  source_id='portiloop1') # TODO: replace this by unique device identifier
56
 
57
- lsl_markers_info_fast = pylsl.StreamInfo(name='Portiloop_stimuli_fast',
58
- type='Markers',
59
- channel_count=1,
60
- channel_format='string',
61
- source_id='portiloop1') # TODO: replace this by unique device identifier
62
 
63
  self.lsl_outlet_markers = pylsl.StreamOutlet(lsl_markers_info)
64
- self.lsl_outlet_markers_fast = pylsl.StreamOutlet(lsl_markers_info_fast)
65
 
66
  # Initialize Alsa stuff
67
  # Open WAV file and set PCM device
@@ -121,6 +126,7 @@ class SleepSpindleRealTimeStimulator(Stimulator):
121
  self.last_detected_ts = ts
122
 
123
  def send_stimulation(self, lsl_text, sound):
 
124
  # Send lsl stimulation
125
  self.lsl_outlet_markers.push_sample([lsl_text])
126
  # Send sound to patient
 
14
 
15
  import wave
16
  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:
 
59
  channel_format='string',
60
  source_id='portiloop1') # TODO: replace this by unique device identifier
61
 
62
+ # lsl_markers_info_fast = pylsl.StreamInfo(name='Portiloop_stimuli_fast',
63
+ # type='Markers',
64
+ # channel_count=1,
65
+ # channel_format='string',
66
+ # source_id='portiloop1') # TODO: replace this by unique device identifier
67
 
68
  self.lsl_outlet_markers = pylsl.StreamOutlet(lsl_markers_info)
69
+ # self.lsl_outlet_markers_fast = pylsl.StreamOutlet(lsl_markers_info_fast)
70
 
71
  # Initialize Alsa stuff
72
  # Open WAV file and set PCM device
 
126
  self.last_detected_ts = ts
127
 
128
  def send_stimulation(self, lsl_text, sound):
129
+ print(f"Stimulating with text: {lsl_text}")
130
  # Send lsl stimulation
131
  self.lsl_outlet_markers.push_sample([lsl_text])
132
  # Send sound to patient
portiloop/src/utils.py CHANGED
@@ -2,9 +2,13 @@ from EDFlib.edfwriter import EDFwriter
2
  from portilooplot.jupyter_plot import ProgressPlot
3
  from pathlib import Path
4
  import numpy as np
 
 
5
 
6
- EDF_PATH = Path.home() / 'workspace' / 'edf_recording'
7
 
 
 
 
8
 
9
 
10
  class DummyAlsaMixer:
@@ -102,7 +106,21 @@ class LiveDisplay():
102
 
103
  class FileReader:
104
  def __init__(self, filename):
105
- raise NotImplementedError
 
 
 
 
 
 
106
 
107
  def get_point(self):
108
- raise NotImplementedError
 
 
 
 
 
 
 
 
 
2
  from portilooplot.jupyter_plot import ProgressPlot
3
  from pathlib import Path
4
  import numpy as np
5
+ import csv
6
+ import time
7
 
 
8
 
9
+ EDF_PATH = Path.home() / 'workspace' / 'edf_recording'
10
+ # Path to the recordings
11
+ RECORDING_PATH = Path.home() / 'portiloop-software' / 'portiloop' / 'recordings'
12
 
13
 
14
  class DummyAlsaMixer:
 
106
 
107
  class FileReader:
108
  def __init__(self, filename):
109
+ file = open(filename, 'r')
110
+ # Open a csv file
111
+ print(f"Reading from file {filename}")
112
+ self.csv_reader = csv.reader(file, delimiter=',')
113
+ self.wait_time = 1/250.0
114
+ self.index = -1
115
+ self.last_time = time.time()
116
 
117
  def get_point(self):
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'