Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Mentalab-hub/explorepy into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
salman2135 committed Dec 9, 2024
2 parents 53f5965 + 26fc1c4 commit 73e9e26
Show file tree
Hide file tree
Showing 20 changed files with 448 additions and 9 deletions.
Binary file added docs/logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions examples/acquisition-example.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ def my_exg_function(packet):
#############


def my_env_function(packet):
"""A function that receives env packets(temperature, light, battery) and does some operations on the data"""
print("Received an environment packet: ", packet)
#############
# YOUR CODE #
#############


def my_orn_function(packet):
"""A function that receives orientation packets and does some operations on the data"""
timestamp, orn_data = packet.get_data()
Expand All @@ -38,6 +46,7 @@ def main():
# Subscribe your function to the stream publisher
exp_device.stream_processor.subscribe(callback=my_exg_function, topic=TOPICS.raw_ExG)
exp_device.stream_processor.subscribe(callback=my_orn_function, topic=TOPICS.raw_orn)
exp_device.stream_processor.subscribe(callback=my_env_function, topic=TOPICS.env)
try:
while True:
time.sleep(.5)
Expand Down
108 changes: 108 additions & 0 deletions examples/band_power_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import time
from collections import deque

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation

import explorepy
from explorepy.packet import EEG
from explorepy.stream_processor import TOPICS

rows, cols = 8, 1024
data_buf = deque([deque(maxlen=cols) for _ in range(rows)])
for row in data_buf:
row.extend(range(1024))


def get_data_buf():
output = np.array([[0] * 1024 for _ in range(rows)])
for i in range(len(output)):
output[i] = np.array(data_buf[i])
return output


def on_exg_received(packet: EEG):
_, data = packet.get_data()
for r in range(len(data)):
for column in range(len(data[r])):
data_buf[r].append(data[r][column])


exp_device = explorepy.Explore()
# Subscribe your function to the stream publisher

exp_device.connect(device_name="Explore_AAAK")
exp_device.stream_processor.subscribe(callback=on_exg_received, topic=TOPICS.raw_ExG)

# Define the frequency bands
bands = {
'Delta': (0.5, 4),
'Theta': (4, 8),
'Alpha': (8, 13),
'Beta': (13, 30),
'Gamma': (30, 50)
}

# FFT and signal parameters
sampling_rate = 250 # in Hz
num_channels = 8
num_samples = 1024

# Set up the figure and axes for subplots
fig, axs = plt.subplots(4, 2, figsize=(10, 15), sharex=True)
fig.tight_layout(pad=4.0) # Adjust layout padding

# Flatten the 2D array of axes for easier iteration
axs = axs.flatten()

# Initialize bar plots for each subplot
bars = []
for ax in axs:
bars.append(ax.bar(bands.keys(), [0] * len(bands), color='skyblue'))
ax.set_ylim(0, 1) # Set an initial y-limit, can adjust based on your data
ax.set_xlabel('Frequency Band')
ax.set_ylabel('Power')


def update(frame):
eeg_signals = get_data_buf() # Generate data for all channels

for idx, (axis, bar) in enumerate(zip(axs, bars)):
eeg_signal = eeg_signals[idx]

# Perform FFT
fft_values = np.fft.fft(eeg_signal)
fft_frequencies = np.fft.fftfreq(num_samples, 1 / sampling_rate)
fft_magnitude = np.abs(fft_values) ** 2 # Power spectrum

# Only consider positive frequencies
positive_frequencies = fft_frequencies[:num_samples // 2]
positive_magnitude = fft_magnitude[:num_samples // 2]

# Calculate band powers
band_powers = []
for band, (low, high) in bands.items():
indices = np.where((positive_frequencies >= low) & (positive_frequencies <= high))
band_power = np.sum(positive_magnitude[indices])
band_powers.append(band_power)

# Update the bar heights
for b, power in zip(bar, band_powers):
b.set_height(power)

# Optionally adjust y-axis limits based on data
axis.set_ylim(0, max(band_powers) * 1.1)
axis.set_title(f'Channel {idx + 1}')

return [b for bar in bars for b in bar] # Flatten the list of bars


# Create the animation object
ani = FuncAnimation(fig, update, interval=500, blit=True) # Update every 500 ms (0.5 seconds)

# Display the plot
plt.show()

while True:
time.sleep(1)
Binary file added examples/eegsynth_demo/MentalabEEGSynth.vcv
Binary file not shown.
10 changes: 10 additions & 0 deletions examples/eegsynth_demo/buffer.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[general]
debug=2
delay=0.010

[redis]
hostname=localhost
port=6379

[fieldtrip]
port=1972,1973,1974
17 changes: 17 additions & 0 deletions examples/eegsynth_demo/historycontrol.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[general]
debug=2

[redis]
hostname=localhost
port=6379

[history]
window=10
; window length for smoothing (s)
stepsize=0.05
; update time (s)

[input]
; control values to plot, separated by comma
freeze=0
channels=spectral.channel1.alpha,spectral.channel2.theta,spectral.channel2.beta
16 changes: 16 additions & 0 deletions examples/eegsynth_demo/lsl2ft.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[general]
debug=1

[fieldtrip]
hostname=localhost
port=1972

[redis]
hostname=localhost
port=6379

[lsl]
; this can be used to select the desired stream (in case there are multiple)
name=
type=ExG
timeout=30 ; in seconds
33 changes: 33 additions & 0 deletions examples/eegsynth_demo/outputmidi_loopback.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[general]
debug=1
delay=0.05
monophonic=1 ; boolean

[redis]
hostname=localhost
port=6379

[midi]
device=IAC Driver Bus 1
channel=1

[control]
; you can specify different MIDI message types here: controlXXX, noteXXX, polytouchXXX, aftertouch, pitchwheel, start, continue, stop, reset, note

[trigger]
; you can specify different MIDI message types here: controlXXX, noteXXX, polytouchXXX, aftertouch, pitchwheel, start, continue, stop, reset, note
note=post.channel1.alpha
start=post.channel1.alpha

[duration]
note=0.5 ; the note will be switched off after the specified time (in seconds)

[velocity]

[scale]
; scale and offset can be used to map Redis values to MIDI values between 0 to 127
; the default scale for all channels is 127

[offset]
; the default offset for all channels is 0
note=40
33 changes: 33 additions & 0 deletions examples/eegsynth_demo/outputmidi_loopback_2.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[general]
debug=1
delay=0.05
monophonic=1 ; boolean

[redis]
hostname=localhost
port=6379

[midi]
device=IAC Driver Bus 2
channel=1

[control]
; you can specify different MIDI message types here: controlXXX, noteXXX, polytouchXXX, aftertouch, pitchwheel, start, continue, stop, reset, note

[trigger]
; you can specify different MIDI message types here: controlXXX, noteXXX, polytouchXXX, aftertouch, pitchwheel, start, continue, stop, reset, note
note=post.channel2.thetabetaratio
start=post.channel2.thetabetaratio

[duration]
note=0.5 ; the note will be switched off after the specified time (in seconds)

[velocity]

[scale]
; scale and offset can be used to map Redis values to MIDI values between 0 to 127
; the default scale for all channels is 127

[offset]
; the default offset for all channels is 0
note=40
23 changes: 23 additions & 0 deletions examples/eegsynth_demo/postprocessing.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[general]
delay=0.05
debug=2

[redis]
hostname=localhost
port=6379

[initial]
; here you can specify the initial values of some control values

[input]
; the keys here can have an arbitrary name, but should map those in the output section
; the keys must be lower-case. values should not contain an equation, only one-to-one mappings
alpha_1=spectral.channel1.alpha
theta_2=spectral.channel2.theta
beta_2=spectral.channel2.beta

[output]
; besides +, -, /, *, the equations also support log, log2, log10, exp, power from numpy
; and compress, limit, rescale, normalizerange, normalizestandard from EEGsynth
post.channel1.alpha = limit(alpha_1 / 1750, 0, 0.8)
post.channel2.thetabetaratio = limit(theta_2/beta_2 / 6, 0, 0.9)
36 changes: 36 additions & 0 deletions examples/eegsynth_demo/preprocessing.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[general]
delay=0.10
debug=1

[redis]
hostname=localhost
port=6379

[input_fieldtrip]
hostname=localhost
port=1972
timeout=30

[output_fieldtrip]
hostname=localhost
port=1973

[processing]
window=0.12
;smoothing=0.2
reference=none
lowpassfilter=45
highpassfilter=2
filterorder=511
;downsample=1
notchfilter=50

[scale]
highpassfilter=1
lowpassfilter=1
notchfilter=1

[offset]
highpassfilter=0
lowpassfilter=0
notchfilter=0
5 changes: 5 additions & 0 deletions examples/eegsynth_demo/push2lsl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import explorepy

expdev = explorepy.Explore()
expdev.connect("Explore_AAAI")
expdev.push2lsl()
47 changes: 47 additions & 0 deletions examples/eegsynth_demo/spectral.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[general]
debug=2
delay=0.1

[redis]
hostname=localhost
port=6379

[fieldtrip]
hostname=localhost
port=1973
timeout=30

[input]
; this specifies the channels from the FieldTrip buffer
; the channel names (on the left) can be specified as you like
channel1=1
channel2=2
; channel3=3
; channel4=4
; channel5=5
; channel6=6
; channel7=7
; channel8=8

[processing]
; the sliding window is specified in seconds
window=5.0

[scale]
window=1

[offset]
window=0

[band]
; the frequency bands can be specified as you like, but must be all lower-case
; you should give the lower and upper range of each band
delta=2-5
theta=5-8
alpha=8-12
beta=12-30
gamma=35-45

[output]
; the results will be written to Redis as "spectral.channel1.alpha" etc.
prefix=spectral
5 changes: 3 additions & 2 deletions examples/ssvep_demo/ssvep.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""
import time
from threading import Lock
from psychopy_visionscience.radial import RadialStim
from psychopy import visual, event
import numpy as np
from analysis import CCAAnalysis
Expand All @@ -29,9 +30,9 @@ def __init__(self, window, size, position, n_frame, log_time=False):
pattern = np.ones((4, 4))
pattern[::2, ::2] *= -1
pattern[1::2, 1::2] *= -1
self._stim1 = visual.RadialStim(win=self._window, tex=pattern, pos=position,
self._stim1 = RadialStim(win=self._window, tex=pattern, pos=position,
size=size, radialCycles=1, texRes=256, opacity=1)
self._stim2 = visual.RadialStim(win=self._window, tex=pattern*-1, pos=position,
self._stim2 = RadialStim(win=self._window, tex=pattern*-1, pos=position,
size=size, radialCycles=1, texRes=256, opacity=1)
self._toggle_flag = False
self.log_time = log_time
Expand Down
Loading

0 comments on commit 73e9e26

Please sign in to comment.