Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ dependencies = [
'pyyaml',
'bleak==0.22.3',
'pylsl',
'numba']
'numba',
'eegprep']

[tool.setuptools]
package-dir = {"" = "src"}
Expand Down
22 changes: 9 additions & 13 deletions src/explorepy/asr_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
asr_process
)

from explorepy.filters import ExGFilter

from explorepy.filters import bp_filter

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -137,7 +136,6 @@ def __init__(self, stream_proc, in_topic):
self.instantiate_buffers()
self.is_initialized = True
self.lifecycle_state = State.STABLE
self.filter = None

@property
def cutoff(self):
Expand Down Expand Up @@ -186,12 +184,11 @@ def on_calib_data_received(self, packet):
"Error writing calibration packet, timer has not been set correctly or calibration length is invalid!"
)
if (time.time() - self.calib_started_at) > self.calibration_length:
self.calibration_data_available = True
self.stop_calibration()
return
self.calibration_data_input = np.append(
self.calibration_data_input,
self.filter.apply(packet, in_place=False).get_data()[1],
packet.get_data()[1],
axis=1,
)

Expand Down Expand Up @@ -222,8 +219,10 @@ def clean_data(self):
return
if self.to_clean_ts[0][0] <= 1.0:
return
filt_sig = bp_filter(self.to_clean.copy(), 45, 55, btype='bandstop', fs=self.sr)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this band so wide?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the standard setting for pre and post-processing(similar settings to our P300 example).

filtered_data = bp_filter(filt_sig, .1, 40, fs=self.sr)
try:
ret = asr_pipeline(self.to_clean, self.sr, self.ch_count, self._state)
ret = asr_pipeline(filtered_data, self.sr, self.ch_count, self._state)
self.cleaned_data_available = True
idx = np.searchsorted(self.to_clean_ts[0], self.last_cleaned_timestamp)
self.cleaned_data = ret[:, idx:]
Expand Down Expand Up @@ -259,12 +258,6 @@ def start_calibration(self, calib_length: float = -1.0):
f"[{self._min_calibration_length},{self._max_calibration_length}]")
logger.info(f"Starting ASR calibration for {self.calibration_length}s...")
self.calib_started_at = time.time()
self.filter = ExGFilter(
cutoff_freq=(1, 45),
filter_type='bandpass',
s_rate=self.sr,
n_chan=self.ch_count,
)
self.stream_processor.subscribe(self.on_calib_data_received, topic=self.in_topic)

def stop_calibration(self):
Expand All @@ -277,13 +270,16 @@ def stop_calibration(self):

def set_state_from_calibration_data(self, calib_data):
self.lifecycle_state = State.CLEANING
cleaned, state = clean_calib_data(calib_data, self.sr)
filt_sig = bp_filter(calib_data, 45, 55, btype='bandstop', fs=self.sr)
filtered_data = bp_filter(filt_sig, .1, 40, self.sr)
cleaned, state = clean_calib_data(filtered_data, self.sr)
if cleaned is None:
self.calibration_data_available = False
self.lifecycle_state = state
return
try:
self._state = asr_calibrate(cleaned, self.sr, cutoff=self._cutoff)
self.calibration_data_available = True
self.lifecycle_state = state
except np.linalg.LinAlgError as e:
self.calibration_data_available = False
Expand Down
8 changes: 8 additions & 0 deletions src/explorepy/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging

import numpy as np
from scipy import signal
from scipy.signal import (
butter,
iirfilter,
Expand All @@ -17,6 +18,13 @@
logger = logging.getLogger(__name__)


@staticmethod
def bp_filter(exg, lf, hf, fs, btype='bandpass'):
N = 5
b, a = signal.butter(N, [lf, hf], btype=btype, fs=fs)
return signal.filtfilt(b, a, exg, axis=1)


class ExGFilter:
"""Filter class for ExG signals"""
def __init__(self, cutoff_freq, filter_type, s_rate, n_chan, order=5):
Expand Down
Loading