<jupyter_start><jupyter_text>As first step we read the raw matlab file as numpy array. From the info file:Stimulation frequencies: 9, 10, 12 and 15 Hz (column 1, 2, 3 and 4 in classInfo_4_5.m) Sampling rate: 256 Hz CH1: sample time CH2-9: EEG CH10: trigger info (LED on...1, LED off...0); CH11: LDA classification outputSo:ch2-9 (index 1-8) is the 8 electrodes raw eeg recordingch10 (index 9) is the trigger channel that encode the light blinking events.As the first step we load the raw numpy matrix in memory and then transform it in a MNE RawArray datastructure.Make you sure to check the units! As convention, MNE expect raw data in Volt, loaded data is in micro volts!<jupyter_code>def load_session(path, sampling_freq, eeg_ch_names):
    # load raw numpy data
    mat = mat73.loadmat(path)
    mat = mat['y']

    # build metadata structure
    ch_names = eeg_ch_names + ['stim1'] 
    ch_types = (['eeg'] * 8) + ['stim']
    info = mne.create_info(ch_names, sfreq=sampling_freq, ch_types=ch_types)

    mat[1:9] = mat[1:9] * 1E-6 # mne expect raw data in Volt, loaded data is in micro volts!
    raw = mne.io.RawArray(mat[1:10], info) # index 1-10 only for EEG and stim channels

    return raw
ls
from pathlib import Path
import mat73
import mne


fs = 256
CH_NAMES = ['P1','P3','P5','P7','P10','O2','O7','O8']
subj1_session1 = Path('new_data/subject_1_fvep_led_training_1.mat')

raw = load_session(subj1_session1, fs, CH_NAMES)

raw.plot(duration=50, color={'eeg':'blue'})<jupyter_output>Creating RawArray with float64 data, n_channels=9, n_times=57728
    Range : 0 ... 57727 =      0.000 ...   225.496 secs
Ready.<jupyter_text>Now we must convert the raw signal (a continuous stream of data) into a segmented one. Each signal segment is a trial (mne call them Epochs). The epoched signal is derived from the stimulus "stim" channel. The stim channel is 1 when stimul is presented to the subject, 0 otherwise (ie. is in rest state).There is one big problem for this use case: the stim channel that we received doesn't encode which kind of stimulus is presented to the subject, in other words we don't know if when the stim channel is one, the subject is looking at 9 Hz, 10 Hz, 12 Hz or 15 Hz blinking led.However, we are lucky enough because the trial sequence is always the same (look at classInfo_4_5.m file) and we know that a single trial is 7s long. So, starting from this information, we must reconstruct a stim channel that encodes also the kind of the event.Let's start!As the first step we must find the events encoded in the stim channel, for this task MNE have a nice useful function called find_events. This function return events encoded as an array of triples:(𝑡𝑖𝑚𝑒_𝑠𝑡𝑒𝑝,𝑒𝑣𝑒𝑛𝑡_𝑖𝑑𝑜𝑓𝑝𝑟𝑒𝑣𝑖𝑜𝑢𝑠𝑒𝑣𝑒𝑛𝑡,𝑒𝑣𝑒𝑛𝑡_𝑖𝑑) Moreover we must distinguish between the onset time step and the offset time step.:The former is the time step when the stim channe goes from 0 to 1, ie. the event beginningThe latter is the time step whetn the stim channle goes from 1 to 0, ie the event end.find_event function have an output parameter that if set to "step" return both onset and offset steps.Once we obtained this event start-end information, we must split the event in 4 sub-parts, the stim signal of each one must encode the flickering frequency with unique event id. (just integers 1,2,3,4).<jupyter_code>raw.get_data()
import matplotlib.pyplot as plt
import matplotlib as mpl

def add_labels_to_stim(raw, n_trials=4):
    events = mne.find_events(raw, stim_channel='stim1', output='step')

    on_set_mask = events[:,2] == 1 # stim channel goes from 0 to 1
    on_set_events = events[on_set_mask, 0]

    off_set_mask = events[:,2] == 0  # stim channel goes from 1 to 0
    off_set_events = events[off_set_mask,0]

    raw_copy = raw.copy() # do not effect the original raw signal
    data = raw_copy._data # directly access the underlying numpy data!

    # loop trough events and encode the label as a stim channel event_id
    for i, (on_set, off_set) in enumerate(zip(on_set_events, off_set_events)):
        trial_label = (i % n_trials) +1 # just a trick to avoid looping i in range(1,5)
        data[8, on_set:off_set] = trial_label # modify the stim channel (index 8)

    return raw_copy

raw2 = add_labels_to_stim(raw)

orig_stim, times = raw[8]
modified_stim, times = raw2[8]


fig, axes = plt.subplots(ncols=2, figsize=(15,8))

axes[0].plot(times, orig_stim[0])
axes[0].set_title('Original Stim Signal')
axes[1].plot(times, modified_stim[0])
axes[1].set_title('Label Encoded Stim Signal')

for ax in axes:
    ax.yaxis.set_major_locator(mpl.ticker.MaxNLocator(integer=True))
    ax.set_xlabel('s')
    ax.set_ylabel('event id')
raw2.plot(start=7, duration=50)
import numpy as np

data = raw2.get_data()
np.save(file='data_raw.npy', arr=data)
raw2.save("data_raw.fif", overwrite=True)<jupyter_output>Writing /Users/beatricevillata/Desktop/MLJC/BCI_SSVEP/SSVEP_IEEE_SMC_2021/Pre-Processing/data_raw.fif
Closing /Users/beatricevillata/Desktop/MLJC/BCI_SSVEP/SSVEP_IEEE_SMC_2021/Pre-Processing/data_raw.fif
[done]
{'fraction_non_alphanumeric': 0.0891897272546287, 'fraction_numerical': 0.03205255823213219, 'mean_word_length': 4.514818880351262, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '362485', 'license': 'MIT', 'n_tokens_mistral': 1900, 'n_tokens_neox': 1754, 'n_words': 632, 'path': 'Pre-Processing/.ipynb_checkpoints/mne_SSVEP_gab-checkpoint.ipynb', 'repo_name': 'sazio/SSVEP_IEEE_SMC_2021', 'size': '5023'}