Skip to content

Commit bcf2599

Browse files
authored
Merge pull request #34 from PayalLakra/bio_amptool
Adding EEG based Beetle game under app.py
2 parents 6091d59 + 63d5415 commit bcf2599

File tree

3 files changed

+80
-41
lines changed

3 files changed

+80
-41
lines changed

test/beetle.py renamed to beetle.py

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,85 +6,125 @@
66
from scipy.signal import iirnotch, butter, lfilter
77
import math
88

9-
pygame.init()
10-
11-
# Screen setup
12-
screen = pygame.display.set_mode((800, 600))
13-
pygame.display.set_caption('Beetle Game')
14-
15-
# Beetle properties
16-
beetle_x, beetle_y = 380, 530
17-
focus_speed_upward = 8 # Speed when moving upward
18-
focus_speed_downward = 4 # Speed when moving downward
19-
focus_timeout = 2 # Time in seconds to stabilize focus
20-
focus_threshold = 5 # Threshold for beta power
21-
22-
# LSL stream setup
9+
# Initialize LSL stream
2310
streams = resolve_stream('name', 'BioAmpDataStream')
2411
if not streams:
2512
print("No LSL stream found!")
26-
pygame.quit()
2713
exit()
2814

2915
inlet = StreamInlet(streams[0])
3016
print("LSL Stream Started")
3117
sampling_rate = int(inlet.info().nominal_srate())
3218
print(f"Sampling rate: {sampling_rate} Hz")
3319

34-
b_notch, a_notch = iirnotch(50.0 / (500 / 2), 30.0)
35-
b_band, a_band = butter(4, [0.5/ (sampling_rate / 2), 48.0 / (sampling_rate / 2)], btype='band')
20+
b_notch, a_notch = iirnotch(50.0 / (sampling_rate / 2), 30.0)
21+
b_band, a_band = butter(4, [0.5 / (sampling_rate / 2), 48.0 / (sampling_rate / 2)], btype='band')
3622

3723
# Buffer for EEG data and Focus tracking variables
3824
buffer = []
3925
buffer_size = 500
40-
focus_timer = 0
41-
last_focus_time = time.time()
42-
last_time = time.time()
4326

44-
# Load the beetle image
45-
beetle_image = pygame.image.load('beetle.jpg')
27+
# Beetle properties
28+
beetle_x, beetle_y = 380, 530
29+
focus_speed_upward = 15
30+
focus_speed_downward = 5
31+
focus_timeout = 2
32+
focus_threshold = None
33+
34+
# Initialize Pygame
35+
pygame.init()
36+
screen = pygame.display.set_mode((800, 600))
37+
pygame.display.set_caption('Beetle Game')
38+
39+
# Load beetle image
40+
beetle_image = pygame.image.load('media\\beetle.jpg')
4641
beetle_image = pygame.transform.scale(beetle_image, (80, 80))
4742

48-
# Function to apply filters
43+
# Function to display a message on the screen
44+
def show_message(message, duration=3):
45+
start_time = time.time()
46+
font = pygame.font.Font(None, 50)
47+
text = font.render(message, True, (0, 0, 0))
48+
text_rect = text.get_rect(center=(400, 300))
49+
50+
while time.time() - start_time < duration:
51+
screen.fill((255, 255, 255))
52+
screen.blit(text, text_rect)
53+
pygame.display.update()
54+
55+
# Apply filters
4956
def apply_filters(eeg_point):
5057
filtered = lfilter(b_notch, a_notch, [eeg_point])
5158
filtered_point = lfilter(b_band, a_band, filtered)
5259
return filtered_point[0]
5360

5461
def calculate_focus_level(eeg_data, sampling_rate=500):
55-
window = np.hanning(len(eeg_data)) # Apply a Hanning window
62+
window = np.hanning(len(eeg_data))
5663
eeg_data_windowed = eeg_data * window
5764
fft_data = np.abs(np.fft.fft(eeg_data_windowed))[:len(eeg_data_windowed) // 2]
5865
fft_data /= len(eeg_data_windowed)
5966
freqs = np.fft.fftfreq(len(eeg_data_windowed), d=1 / sampling_rate)[:len(eeg_data_windowed) // 2]
6067

61-
# Compute power in different bands
6268
delta_power = math.sqrt(np.sum((fft_data[(freqs >= 0.5) & (freqs <= 4)]) ** 2))
6369
theta_power = math.sqrt(np.sum((fft_data[(freqs >= 4) & (freqs <= 8)]) ** 2))
6470
alpha_power = math.sqrt(np.sum((fft_data[(freqs >= 8) & (freqs <= 13)]) ** 2))
6571
beta_power = math.sqrt(np.sum((fft_data[(freqs >= 13) & (freqs <= 30)]) ** 2))
6672
gamma_power = math.sqrt(np.sum((fft_data[(freqs >= 30) & (freqs <= 45)]) ** 2))
6773

68-
power = (beta_power + gamma_power) / (theta_power + alpha_power)
74+
power = (beta_power + gamma_power) / (delta_power + theta_power + alpha_power + beta_power + gamma_power)
6975
print(power)
7076
return power
7177

78+
# Calibration Phase
79+
show_message("Sit still and relax for 10 seconds", 3)
80+
81+
print("Starting Calibration... Please relax and stay still for 10 seconds.")
82+
calibration_data = []
83+
calibration_duration = 10
84+
calibration_start_time = time.time()
85+
86+
while time.time() - calibration_start_time < calibration_duration:
87+
sample, _ = inlet.pull_sample(timeout=0.1)
88+
if sample:
89+
filtered_sample = apply_filters(sample[0])
90+
calibration_data.append(filtered_sample)
91+
92+
if len(calibration_data) >= buffer_size:
93+
eeg_data = np.array(calibration_data)
94+
baseline_focus_levels = [calculate_focus_level(eeg_data[i:i + buffer_size]) for i in range(0, len(eeg_data), buffer_size)]
95+
96+
mean_focus = np.mean(baseline_focus_levels)
97+
std_focus = np.std(baseline_focus_levels)
98+
99+
focus_threshold = mean_focus + 1.5 * std_focus
100+
print(f"Calibration Complete. Focus Threshold set at: {focus_threshold:.2f}")
101+
else:
102+
print("Calibration failed due to insufficient data.")
103+
exit()
104+
105+
# Show Game Start Message
106+
show_message("Game Starting...", 1)
107+
108+
# Update beetle position
72109
def update_beetle_position(focus_level, is_focus_stable):
73110
global beetle_y
74111
if is_focus_stable:
75-
beetle_y = max(10 + beetle_image.get_height() // 2, beetle_y - focus_speed_upward) # Prevent moving above border
112+
beetle_y = max(10 + beetle_image.get_height() // 2, beetle_y - focus_speed_upward)
76113
else:
77-
beetle_y = min(580 - beetle_image.get_height() // 2, beetle_y + focus_speed_downward) # Prevent moving below border
114+
beetle_y = min(580 - beetle_image.get_height() // 2, beetle_y + focus_speed_downward)
78115

79-
# Game loop
116+
print("STARTING GAME...")
80117
running = True
118+
focus_timer = 0
119+
last_focus_time = time.time()
120+
last_time = time.time()
121+
81122
while running:
82123
try:
83124
for event in pygame.event.get():
84125
if event.type == pygame.QUIT:
85126
running = False
86127

87-
# Read EEG data from the LSL stream
88128
sample, _ = inlet.pull_sample(timeout=0.1)
89129
if sample:
90130
filtered_sample = apply_filters(sample[0])
@@ -93,33 +133,27 @@ def update_beetle_position(focus_level, is_focus_stable):
93133
current_time = time.time()
94134
if current_time - last_time >= 1:
95135
last_time = current_time
96-
release_count = int(len(buffer) * 0.2) # Remove oldest 20%
97-
buffer = buffer[release_count:] # Trim buffer
136+
buffer = buffer[int(len(buffer) * 0.2):]
98137

99-
if len(buffer) >= buffer_size: # Process EEG data when the buffer is full
100-
eeg_data = np.array(buffer) # Use filtered data
101-
buffer = [] # Clear the buffer
138+
if len(buffer) >= buffer_size:
139+
eeg_data = np.array(buffer)
140+
buffer = []
102141

103142
focus_level = calculate_focus_level(eeg_data)
104143

105144
if focus_level > focus_threshold:
106145
focus_timer = min(focus_timeout, focus_timer + (current_time - last_focus_time))
107146
is_focus_stable = focus_timer >= focus_timeout
108147
update_beetle_position(focus_level, is_focus_stable)
109-
110-
elif 7 <= focus_level <= 8: # No movement of the beetle
111-
print("Beetle remains stationary.")
112-
113148
else:
114149
focus_timer = max(0, focus_timer - (current_time - last_focus_time))
115150
is_focus_stable = focus_timer >= focus_timeout
116151
update_beetle_position(focus_level, is_focus_stable)
117152

118153
last_focus_time = current_time
119154

120-
# Clear the screen and draw the beetle
121155
screen.fill("#FFFFFF")
122-
pygame.draw.rect(screen, (0, 0, 0), (10, 10, 780, 580), 5) # Draw border
156+
pygame.draw.rect(screen, (0, 0, 0), (10, 10, 780, 580), 5)
123157
screen.blit(beetle_image, (beetle_x - beetle_image.get_width() // 2, beetle_y - beetle_image.get_height() // 2))
124158

125159
pygame.display.update()
File renamed without changes.

templates/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ <h1>Chords-Python Applications</h1>
6464
{% if not lsl_started %}disabled{% endif %}>
6565
EEG Tug of War Game
6666
</button>
67+
<button type="submit" name="app_name" value="beetle"
68+
class="{% if 'beetle' in running_apps %}running{% else %}not-running{% endif %}"
69+
{% if not lsl_started %}disabled{% endif %}>
70+
EEG Beetle Game
71+
</button>
6772
<button type="submit" name="app_name" value="gui"
6873
class="{% if 'gui' in running_apps %}running{% else %}not-running{% endif %}"
6974
{% if not lsl_started %}disabled{% endif %}>

0 commit comments

Comments
 (0)