Open In Colab

Lecture 3b: EEG Tonic / Phasic Analysis#

Signal Processing for Interactive Systems

Cumhur Erkut (cer@create.aau.dk)

Aalborg University Copenhagen

Last edited: 2025-03-05

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import butter, filtfilt, stft, istft
# @title EEG signal generation
# Remember to read the EEG signal from the file if you have one
# Here we generate a synthetic signal with tonic and phasic components
fs = 256  # Sampling frequency (Hz)
t = np.arange(0, 10, 1/fs)  # 10 seconds of data
tonic = np.sin(2 * np.pi * 0.5 * t)  # Slow tonic component (~0.5 Hz)
phasic = np.sin(2 * np.pi * 10 * t) + np.sin(2 * np.pi * 20 * t)  # Faster phasic components
signal = tonic + phasic + np.random.normal(0, 0.1, len(t))  # Combined EEG signal

Use different signal processing methods#

# 1. Low-Pass Filter Approach
def lowpass_filter(signal, cutoff, fs, order=4):
    nyquist = fs / 2
    b, a = butter(order, cutoff / nyquist, btype='low')
    return filtfilt(b, a, signal) # zero phase filtering

tonic_lp = lowpass_filter(signal, 1, fs)
phasic_lp = signal - tonic_lp
# 2. Moving Average Approach
def moving_average(signal, window_size):
    return np.convolve(signal, np.ones(window_size)/window_size, mode='same')

tonic_ma = moving_average(signal, int(fs / 4))
phasic_ma = signal - tonic_ma
# 3. Short-Time Fourier Transform (STFT) Approach
f, time, Zxx = stft(signal, fs=fs, nperseg=256)
tonic_stft = Zxx.copy()
tonic_stft[np.abs(f) > 1, :] = 0  # Keep only low frequencies
phasic_stft = Zxx.copy()
phasic_stft[np.abs(f) <= 1, :] = 0  # Remove low frequencies
_, tonic_stft_rec = istft(tonic_stft, fs)
_, phasic_stft_rec = istft(phasic_stft, fs)

Plot Results#

methods = ['Low-Pass Filter', 'Moving Average', 'STFT']
tonic_signals = [tonic_lp, tonic_ma, tonic_stft_rec]
phasic_signals = [phasic_lp, phasic_ma, phasic_stft_rec]

plt.figure(figsize=(12, 10))
for i, method in enumerate(methods):
    plt.subplot(len(methods), 2, 2*i+1)
    plt.plot(t, tonic_signals[i], label=f'Tonic ({method})', color='r')
    plt.legend()

    plt.subplot(len(methods), 2, 2*i+2)
    plt.plot(t, phasic_signals[i], label=f'Phasic ({method})', color='g')
    plt.legend()

plt.xlabel("Time (s)")
plt.suptitle("Comparison of Tonic / Phasic Separation Methods")
plt.show()
../_images/0032e008766685815ef9e46e95e3dc34f85538b7111ce032ff841c57ffb86377.png