Skip to content

Commit bc3ea37

Browse files
authored
Merge pull request #19 from PayalLakra/bio_amptool
Updation in EMG and EOG
2 parents 65cbd83 + 1ae560c commit bc3ea37

File tree

5 files changed

+126
-22
lines changed

5 files changed

+126
-22
lines changed

Notebooks/ECG.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77113,8 +77113,8 @@
7711377113
"\n",
7711477114
" # Define a window around the R-peak to capture the full PQRST complex\n",
7711577115
" window_size = int(0.5 * 250) # Half a second before and after the R-peak \n",
77116-
" start_idx = max(0, highest_r_peak_idx - window_size) \n",
77117-
" end_idx = min(len(ecg_signal), highest_r_peak_idx + window_size) \n",
77116+
" # start_idx = max(0, highest_r_peak_idx - window_size) \n",
77117+
" # end_idx = min(len(ecg_signal), highest_r_peak_idx + window_size) \n",
7711877118
"\n",
7711977119
" # Extract the time and ECG data for the window\n",
7712077120
" window_time = time[start_idx:end_idx]\n",
@@ -115915,7 +115915,7 @@
115915115915
],
115916115916
"metadata": {
115917115917
"kernelspec": {
115918-
"display_name": "venv",
115918+
"display_name": "Python 3 (ipykernel)",
115919115919
"language": "python",
115920115920
"name": "python3"
115921115921
},
@@ -115929,7 +115929,7 @@
115929115929
"name": "python",
115930115930
"nbconvert_exporter": "python",
115931115931
"pygments_lexer": "ipython3",
115932-
"version": "3.10.10"
115932+
"version": "3.11.7"
115933115933
}
115934115934
},
115935115935
"nbformat": 4,

README.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Chords Python script is designed to interface with an Arduino-based bioamplifier
2727

2828
## Installation
2929

30-
1. Ensure you have Python 3.10 installed.
30+
1. Ensure you have latest version of Python installed.
3131
2. Create Virtual Environment
3232
```bash
3333
python -m venv venv
@@ -95,7 +95,7 @@ To use the script, run it from the command line with various options:
9595
## Applications
9696
9797
> [!IMPORTANT]
98-
Before using the below Applications make sure you are in Application folder.
98+
Before using the below Applications make sure you are in application folder.
9999
100100
### GUI
101101
@@ -123,9 +123,9 @@ To use the script, run it from the command line with various options:
123123
124124
`check_win_condition()`: Determines if either player has won based on the ball's position.
125125
126-
### Heart Rate
126+
### HEART RATE
127127
128-
- `python heartbeat.ecg.py`:Enable a GUI with real-time ECG and heart rate
128+
- `python heartbeat.ecg.py`:Enable a GUI with real-time ECG and heart rate.
129129
130130
#### Script Functions
131131
@@ -141,6 +141,22 @@ To use the script, run it from the command line with various options:
141141
142142
`update_heart_rate(self)`: Calculates and updates the heart rate based on detected R-peaks in the ECG signal.
143143
144+
### EMG ENVELOPE
145+
146+
- `python emgenvelope.py` :Enable a GUI with real-time EMG & its Envelope.
147+
148+
#### Script Functions
149+
150+
`update_plot` : Updates the plot with latest Filtered EMG Data and its Envelope.
151+
152+
### EOG
153+
154+
- `python eog.py` :Enable a GUI with real-time EOG.
155+
156+
#### Script Functions
157+
158+
`update_plot` : Updates the plot with latest Filtered EOG Data.
159+
144160
## Troubleshooting
145161
146162
- **Arduino Not Detected:** Ensure the Arduino is properly connected and powered. Check the serial port and baud rate settings.

applications/emgenvelope.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,21 @@ def __init__(self):
1616
# Create layout
1717
layout = QVBoxLayout()
1818

19-
# Create plot widgets for raw EMG and EMG envelope
20-
self.raw_emg_plot = PlotWidget(self)
21-
self.raw_emg_plot.setBackground('w')
22-
self.raw_emg_plot.showGrid(x=True, y=True)
23-
self.raw_emg_plot.setTitle("Raw EMG Signal")
19+
# Create plot widgets for Filtered EMG and EMG envelope
20+
self.emg_plot = PlotWidget(self)
21+
self.emg_plot.setBackground('w')
22+
self.emg_plot.showGrid(x=True, y=True)
23+
self.emg_plot.setMouseEnabled(x=False, y=False) # Disable zoom
24+
self.emg_plot.setTitle("Filtered EMG Signal (High Pass:70 Hz)")
2425

2526
self.envelope_plot = PlotWidget(self)
2627
self.envelope_plot.setBackground('w')
2728
self.envelope_plot.showGrid(x=True, y=True)
28-
self.envelope_plot.setTitle("EMG Envelope")
29+
self.envelope_plot.setMouseEnabled(x=False, y=False) # Disable zoom
30+
self.envelope_plot.setTitle("EMG Envelope (Samples Average:10%)")
2931

3032
# Add plots to layout
31-
layout.addWidget(self.raw_emg_plot)
33+
layout.addWidget(self.emg_plot)
3234
layout.addWidget(self.envelope_plot)
3335

3436
# Central widget
@@ -55,23 +57,23 @@ def __init__(self):
5557

5658
self.b, self.a = butter(4, 70.0 / (0.5 * self.sampling_rate), btype='high')
5759

58-
# Moving RMS window size (50 for 250 sampling rate and 100 for 500 sampling rate)
60+
# Moving RMS window size (25 for 250 sampling rate and 50 for 500 sampling rate)
5961
self.rms_window_size = int(0.1 * self.sampling_rate)
6062

6163
# Set fixed axis ranges
62-
self.raw_emg_plot.setXRange(0, 10, padding=0)
64+
self.emg_plot.setXRange(0, 10, padding=0)
6365
self.envelope_plot.setXRange(0, 10, padding=0)
6466

65-
# Set y-axis limits based on sampling rate for raw EMG
67+
# Set y-axis limits based on sampling rate for Filtered EMG
6668
if self.sampling_rate == 250:
67-
self.raw_emg_plot.setYRange(-((2**10)/2), ((2**10)/2), padding=0) # for R3
69+
self.emg_plot.setYRange(-((2**10)/2), ((2**10)/2), padding=0) # for R3
6870
self.envelope_plot.setYRange(0, ((2**10)/2), padding=0) # for R3
6971
elif self.sampling_rate == 500:
70-
self.raw_emg_plot.setYRange(-((2**14)/2), ((2**14)/2), padding=0) # for R4
72+
self.emg_plot.setYRange(-((2**14)/2), ((2**14)/2), padding=0) # for R4
7173
self.envelope_plot.setYRange(0, ((2**14)/2), padding=0) # for R4
7274

7375
# Plot curves for EMG data and envelope
74-
self.emg_curve = self.raw_emg_plot.plot(self.time_data, self.emg_data, pen=pg.mkPen('b', width=1))
76+
self.emg_curve = self.emg_plot.plot(self.time_data, self.emg_data, pen=pg.mkPen('b', width=1))
7577
self.envelope_curve = self.envelope_plot.plot(self.time_data, self.emg_data, pen=pg.mkPen('r', width=2))
7678

7779
# Timer for plot update

applications/eog.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import numpy as np
2+
from scipy.signal import butter, filtfilt
3+
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QMainWindow, QWidget
4+
from pyqtgraph import PlotWidget
5+
import pyqtgraph as pg
6+
import pylsl
7+
import sys
8+
9+
class EOGMonitor(QMainWindow):
10+
def __init__(self):
11+
super().__init__()
12+
13+
self.setWindowTitle("Real-Time EOG Monitor - Eye Blink Detection")
14+
self.setGeometry(100, 100, 800, 400)
15+
16+
# Create layout
17+
layout = QVBoxLayout()
18+
19+
# Create plot widget for EOG
20+
self.eog_plot = PlotWidget(self)
21+
self.eog_plot.setBackground('w')
22+
self.eog_plot.showGrid(x=True, y=True)
23+
self.eog_plot.setTitle("Filtered EOG Signal (Low Pass: 10 Hz)")
24+
25+
# Add plot to layout
26+
layout.addWidget(self.eog_plot)
27+
28+
# Central widget
29+
central_widget = QWidget()
30+
central_widget.setLayout(layout)
31+
self.setCentralWidget(central_widget)
32+
33+
# Set up LSL stream inlet
34+
streams = pylsl.resolve_stream('name', 'BioAmpDataStream')
35+
if not streams:
36+
print("No LSL stream found!")
37+
sys.exit(0)
38+
self.inlet = pylsl.StreamInlet(streams[0])
39+
40+
# Sampling rate
41+
self.sampling_rate = int(self.inlet.info().nominal_srate())
42+
print(f"Sampling rate: {self.sampling_rate} Hz")
43+
44+
# Data and buffer
45+
self.buffer_size = self.sampling_rate * 5 # 5 seconds buffer for recent data
46+
self.eog_data = np.zeros(self.buffer_size)
47+
self.time_data = np.linspace(0, 5, self.buffer_size)
48+
self.current_index = 0
49+
50+
# Low-pass filter for EOG (10 Hz)
51+
self.b, self.a = butter(4, 10.0 / (0.5 * self.sampling_rate), btype='low')
52+
53+
# Set fixed axis ranges
54+
self.eog_plot.setXRange(0, 5, padding=0)
55+
if self.sampling_rate == 250:
56+
self.eog_plot.setYRange(-((2**10)/2), ((2**10)/2), padding=0)
57+
elif self.sampling_rate == 500:
58+
self.eog_plot.setYRange(-((2**14)/2), ((2**14)/2), padding=0)
59+
60+
# Plot curve for EOG data
61+
self.eog_curve = self.eog_plot.plot(self.time_data, self.eog_data, pen=pg.mkPen('b', width=1))
62+
63+
# Timer for plot update
64+
self.timer = pg.QtCore.QTimer()
65+
self.timer.timeout.connect(self.update_plot)
66+
self.timer.start(15)
67+
68+
def update_plot(self):
69+
samples, _ = self.inlet.pull_chunk(timeout=0.0, max_samples=30)
70+
if samples:
71+
for sample in samples:
72+
# Overwrite the oldest data point in the buffer
73+
self.eog_data[self.current_index] = sample[0]
74+
self.current_index = (self.current_index + 1) % self.buffer_size
75+
76+
# Apply only the low-pass filter to the EOG data
77+
filtered_eog = filtfilt(self.b, self.a, self.eog_data)
78+
79+
# Update curve with the low-pass filtered EOG signal
80+
self.eog_curve.setData(self.time_data, filtered_eog)
81+
82+
if __name__ == "__main__":
83+
app = QApplication(sys.argv)
84+
window = EOGMonitor()
85+
window.show()
86+
sys.exit(app.exec_())

chords_requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
numpy==1.26.4
1+
numpy==2.1.3
22
pylsl==1.16.2
33
pyserial==3.5

0 commit comments

Comments
 (0)