Skip to content

Commit c6c4df5

Browse files
committed
add ffteeg.py file which plot filtered eeg data, fft and brainpower bars.(low cut off filter-40 hz)
1 parent 8bfdf69 commit c6c4df5

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

ffteeg.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import numpy as np
2+
from numpy import hamming
3+
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QHBoxLayout, QMainWindow, QWidget
4+
from PyQt5.QtCore import Qt
5+
from pyqtgraph import PlotWidget
6+
import pyqtgraph as pg
7+
import pylsl
8+
import sys
9+
from scipy.signal import butter, filtfilt
10+
from scipy.fft import fft
11+
12+
class EEGMonitor(QMainWindow):
13+
def __init__(self):
14+
super().__init__()
15+
16+
self.setWindowTitle("Real-Time EEG Monitor with FFT and Brainwave Power")
17+
self.setGeometry(100, 100, 1200, 800)
18+
19+
# Main layout split into two halves: top for EEG, bottom for FFT and Brainwaves
20+
self.central_widget = QWidget()
21+
self.main_layout = QVBoxLayout(self.central_widget)
22+
23+
# First half for EEG signal plot
24+
self.eeg_plot_widget = PlotWidget(self)
25+
self.eeg_plot_widget.setBackground('w')
26+
self.eeg_plot_widget.showGrid(x=True, y=True)
27+
self.eeg_plot_widget.setLabel('bottom', 'EEG Plot')
28+
self.eeg_plot_widget.setYRange(0, 15000, padding=0)
29+
self.eeg_plot_widget.setXRange(0, 10, padding=0)
30+
self.eeg_plot_widget.setMouseEnabled(x=False, y=False) # Disable zoom
31+
self.main_layout.addWidget(self.eeg_plot_widget)
32+
33+
# Second half for FFT and Brainwave Power, aligned horizontally
34+
self.bottom_layout = QHBoxLayout()
35+
36+
# FFT Plot (left side of the second half)
37+
self.fft_plot = PlotWidget(self)
38+
self.fft_plot.setBackground('w')
39+
self.fft_plot.showGrid(x=True, y=True)
40+
self.fft_plot.setLabel('bottom', 'FFT')
41+
self.fft_plot.setYRange(0, 25000, padding=0)
42+
self.fft_plot.setXRange(0, 50, padding=0) # Set x-axis to 0 to 50 Hz
43+
self.fft_plot.setMouseEnabled(x=False, y=False) # Disable zoom
44+
self.bottom_layout.addWidget(self.fft_plot)
45+
46+
# Bar graph for brainwave power bands (right side of the second half)
47+
self.bar_chart_widget = pg.PlotWidget(self)
48+
self.bar_chart_widget.setBackground('w')
49+
self.bar_chart_widget.setLabel('bottom', 'Brainpower Bands')
50+
self.bar_chart_widget.setXRange(-0.5, 4.5)
51+
self.bar_chart_widget.setMouseEnabled(x=False, y=False) # Disable zoom
52+
# Add brainwave power bars
53+
self.brainwave_bars = pg.BarGraphItem(x=[0, 1, 2, 3, 4], height=[0, 0, 0, 0, 0], width=0.5, brush='g')
54+
self.bar_chart_widget.addItem(self.brainwave_bars)
55+
# Set x-ticks for brainwave types
56+
self.bar_chart_widget.getAxis('bottom').setTicks([[(0, 'Delta'), (1, 'Theta'), (2, 'Alpha'), (3, 'Beta'), (4, 'Gamma')]])
57+
self.bottom_layout.addWidget(self.bar_chart_widget)
58+
59+
# Add the bottom layout to the main layout
60+
self.main_layout.addLayout(self.bottom_layout)
61+
self.setCentralWidget(self.central_widget)
62+
63+
# Set up LSL stream inlet
64+
streams = pylsl.resolve_stream('name', 'BioAmpDataStream')
65+
if not streams:
66+
print("No LSL stream found!")
67+
sys.exit(0)
68+
self.inlet = pylsl.StreamInlet(streams[0])
69+
70+
# Sampling rate
71+
self.sampling_rate = int(self.inlet.info().nominal_srate())
72+
print(f"Sampling rate: {self.sampling_rate} Hz")
73+
74+
# Data and buffers
75+
self.buffer_size = self.sampling_rate * 10 # Fixed-size buffer for 10 seconds
76+
self.eeg_data = np.zeros(self.buffer_size) # Fixed-size array for circular buffer
77+
self.time_data = np.linspace(0, 10, self.buffer_size) # Fixed time array for plotting
78+
self.current_index = 0 # Index for overwriting data
79+
80+
# Low-pass filter (4th order, cutoff at 45 Hz)
81+
self.b_lowpass, self.a_lowpass = butter(4, 45 / (0.5 * self.sampling_rate), btype='low')
82+
83+
# Timer for updating the plot
84+
self.timer = pg.QtCore.QTimer()
85+
self.timer.timeout.connect(self.update_plot)
86+
self.timer.start(20)
87+
88+
self.eeg_curve = self.eeg_plot_widget.plot(self.time_data, self.eeg_data, pen=pg.mkPen('b', width=1)) #EEG Colour is blue
89+
self.fft_curve = self.fft_plot.plot(pen=pg.mkPen('r', width=1)) # FFT Colour is red
90+
91+
def update_plot(self):
92+
samples, _ = self.inlet.pull_chunk(timeout=0.0, max_samples=30)
93+
if samples:
94+
for sample in samples:
95+
# Overwrite the oldest data point in the buffer
96+
self.eeg_data[self.current_index] = sample[0]
97+
self.current_index = (self.current_index + 1) % self.buffer_size # Circular increment
98+
99+
# Apply low-pass filter
100+
filtered_eeg = filtfilt(self.b_lowpass, self.a_lowpass, self.eeg_data)
101+
102+
# Update the EEG plot with the filtered data
103+
self.eeg_curve.setData(self.time_data, filtered_eeg)
104+
105+
# Perform FFT with windowing (Hamming window)
106+
window = hamming(len(filtered_eeg)) # Apply Hamming window to reduce spectral leakage
107+
filtered_eeg_windowed = filtered_eeg * window # Element-wise multiply
108+
109+
eeg_fft = np.abs(fft(filtered_eeg_windowed))[:len(filtered_eeg_windowed) // 2] # Positive frequencies only
110+
freqs = np.fft.fftfreq(len(filtered_eeg_windowed), 1 / self.sampling_rate)[:len(filtered_eeg_windowed) // 2]
111+
112+
# Update FFT plot
113+
self.fft_curve.setData(freqs, eeg_fft)
114+
115+
# Calculate brainwave power
116+
brainwave_power = self.calculate_brainwave_power(eeg_fft, freqs)
117+
self.brainwave_bars.setOpts(height=brainwave_power)
118+
119+
def calculate_brainwave_power(self, fft_data, freqs):
120+
delta_power = np.sum(fft_data[(freqs >= 0.5) & (freqs <= 4)])
121+
theta_power = np.sum(fft_data[(freqs >= 4) & (freqs <= 8)])
122+
alpha_power = np.sum(fft_data[(freqs >= 8) & (freqs <= 13)])
123+
beta_power = np.sum(fft_data[(freqs >= 13) & (freqs <= 30)])
124+
gamma_power = np.sum(fft_data[(freqs >= 30) & (freqs <= 45)])
125+
126+
return [delta_power, theta_power, alpha_power, beta_power, gamma_power]
127+
128+
if __name__ == "__main__":
129+
app = QApplication(sys.argv)
130+
window = EEGMonitor()
131+
window.show()
132+
sys.exit(app.exec_())

0 commit comments

Comments
 (0)