KBB MMN Triggers (PsychoPy) - LeoLedesma237/LeoWebsite GitHub Wiki

Below is the code for the triggers in the MMN task. Triggers are signals sent by software -in our case PsychoPy- that give information of the stimulus type and its latency. This is crucial for EEG research that incorporates event related potentials analysis. PsychoPy has code templates available for trigger sending via this link: Sending triggers via serial port. This will function for devices that have a serial port (like we do).

Our MMN Task is comprised of three blocks/routines that are presented in this order:

  1. Tone_trial
  2. Speech_trial
  3. words_trial

We start first by identifying which port name needs to be added into the code. Open 'Device Manager' and scroll to Ports (COM & LPT). Once there, remove and plug in the TriggerBox from your computer's port and notice which port name disappears/reappears from this window. That will be the name of the port to write into the code. Ours is COM4.

The next steps will almost be identically to those in the link mentioned above. Sending triggers vial serial port

In your routine, create a Code Component and give it a name (ex: trigger.code1). When opened, there are several tabs. The following codes needs to go into their respective tabs.

Tone Task

Tab: Begin Experiment

This is the tab where the name of the serial port goes.

# Import the necessary libraries
import serial

# Open a connection to the serial port (replace 'COM4' with your actual port)
ser = serial.Serial('COM4', baudrate=115200)

Tab: Begin Routine

stimulus_pulse_started = False
stimulus_pulse_ended = False

Tab: Each Frame

This is the chunk of code where most of the modifications for each researcher's study design can be written in. For starters, Tone_Task represents the name of the Sound Component in our routine. Therefore, you would want to change this to the name of your Component while keeping the .status that follows (ex: Image_Task.status). The if statements below this correspond to the name of the triggers you want to send to actiChamp. They must be numeric. We are telling PsychoPy specifically that we want our deviant stimuli to be coded in the EEG as the number 1 and our standard stimuli to be coded as the number 2. We can do this by using information already available in the excel sheet created to run the MMN task. One of these columns is named Trial_Type and contains the values Deviant or Standard for each cell. Thus, the information from that variable can be used to send the triggers we want to the EEG for later analysis. The remaining portion of the code should be left as is.

if Tone_Task.status == STARTED and not stimulus_pulse_started:

    # Check if the current stimulus is the deviant or standard
    if Trial_Type == 'Deviant':
        trigger_code = '1'  # If it's deviant
    else:
        trigger_code = '2'  # If its standard
    
    # Send the trigger signal
    win.callOnFlip(ser.write, str.encode(trigger_code))
    
    # Record the start time of the trigger pulse
    stimulus_pulse_start_time = globalClock.getTime()
    
    # Mark that the trigger pulse has started
    stimulus_pulse_started = True

if stimulus_pulse_started and not stimulus_pulse_ended:
    if globalClock.getTime() - stimulus_pulse_start_time >= 5:
        # Send the trigger signal to end the pulse
        win.callOnFlip(ser.write, str.encode('0'))
        
        # Mark that the trigger pulse has ended
        stimulus_pulse_ended = True

tab: End Experiment

port.close()

Speech Task

Tab: Begin Experiment

Leave this blank since we already coded this information in the previous task


Tab: Begin Routine

stimulus_pulse_started = False
stimulus_pulse_ended = False

Tab: Each Frame

For this task, the Sound Component is named Speech_Task, thus that was used for the first if statement in the code. The excel file that codes for the rows for this task also contains a column named Trial_Type that contains the values Deviant or Standard in each cell. Just like before, if the cell contains a Deviant value, then the trigger code will send the number 3. If not then the trigger code will send the number 4.

if Speech_Task.status == STARTED and not stimulus_pulse_started:

    # Check if the current stimulus is the deviant or standard
    if Trial_Type == 'Deviant':
        trigger_code = '3'  # If it's deviant
    else:
        trigger_code = '4'  # If its standard
    
    # Send the trigger signal
    win.callOnFlip(ser.write, str.encode(trigger_code))
    
    # Record the start time of the trigger pulse
    stimulus_pulse_start_time = globalClock.getTime()
    
    # Mark that the trigger pulse has started
    stimulus_pulse_started = True

if stimulus_pulse_started and not stimulus_pulse_ended:
    if globalClock.getTime() - stimulus_pulse_start_time >= 5:
        # Send the trigger signal to end the pulse
        win.callOnFlip(ser.write, str.encode('0'))
        
        # Mark that the trigger pulse has ended
        stimulus_pulse_ended = True

Word Task

Tab: Begin Experiment


Tab: Begin Routine

stimulus_pulse_started = False
stimulus_pulse_ended = False

Tab: Each Frame

Same logic as the previous tasks apply. The Sound Component in this task is named Word_Task and the excel file that created it also contains a column named Trial_Type that is populated with Deviant and Standard cells.

if Word_Task.status == STARTED and not stimulus_pulse_started:

    # Check if the current stimulus is the deviant or standard
    if Trial_Type == 'Deviant':
        trigger_code = '5'  # If it's deviant
    else:
        trigger_code = '6'  # If its standard
    
    # Send the trigger signal
    win.callOnFlip(ser.write, str.encode(trigger_code))
    
    # Record the start time of the trigger pulse
    stimulus_pulse_start_time = globalClock.getTime()
    
    # Mark that the trigger pulse has started
    stimulus_pulse_started = True

if stimulus_pulse_started and not stimulus_pulse_ended:
    if globalClock.getTime() - stimulus_pulse_start_time >= 5:
        # Send the trigger signal to end the pulse
        win.callOnFlip(ser.write, str.encode('0'))
        
        # Mark that the trigger pulse has ended
        stimulus_pulse_ended = True

Are the triggers sending?

Visual Inspection

There are a couple of ways of checking whether the trigger codes are being transmitted as expected. The first and easiest is to open an EEG recording software such as PyCorder or BrainVision Record and physically look at your data. It does not have to be recording- just view your data in real time and see if any triggers are being embedded on to the data. Below is a picture to illustrate this. The blue squares with an R4 underneath are Response Trigger Codes that are being received by BrainVision Recorder in real time.

VisualMarkerInspection

Text File Inspection

Another and more thorough way is to record brain activity using one of the softwares just mentioned. Using BrainVision Recorder, saving an EEG file will output three file types:

  1. .eeg
  2. .vhdr
  3. .vmrk

We would be interested in opening the .vmrk file, which can be done with NotePad. Additionally, this information can be read into R using the read.csv function, however, you would have to delete some of the top rows in this file to get it to work properly. This text file shows you the order and types of triggers that were received by the software and thus embedded into the EEG data. Additionally, these markers include the latency for each trigger (however to convert them into milliseconds you need to know your recording's sampling rate).

Below is an example of what a .vmrk file would look like. Notice there are stimulus (S) and response (R) triggers present. The numbers on the right side give information about the trigger. The first number represents the type of trigger code, for example, when looking at only rows with S, the values are either 1 or 2, which let us know if that stimulus was a deviant (number 1) or a standard (number 2). The numbers that follow it are the latency. To transform these values into milliseconds, they need to be multiplied by 1000 and divided by the sampling rate of the EEG system.

BrainVision Data Exchange Marker File Version 1.0

[Common Infos]
Codepage=UTF-8
DataFile=AllMarkersTest.eeg

[Marker Infos]
; Each entry: Mk<Marker number>=<Type>,<Description>,<Position in data points>,
; <Size in data points>, <Channel number (0 = marker is related to all channels)>
; Fields are delimited by commas, some fields might be omitted (empty).
; Commas in type or description text are coded as "\1".
Mk1=New Segment,,1,1,0,20240523231928094356
Mk2=Response,R  7,2200,1,0
Mk3=Stimulus,S  2,2200,1,0
Mk4=Response,R  4,2206,1,0
Mk5=Response,R  7,2418,1,0
Mk6=Stimulus,S  2,2418,1,0
Mk7=Response,R  4,2423,1,0
Mk8=Response,R  7,2668,1,0
Mk9=Stimulus,S  2,2668,1,0
Mk10=Response,R  4,2673,1,0
Mk11=Response,R  7,2918,1,0
Mk12=Stimulus,S  2,2918,1,0
Mk13=Response,R  4,2924,1,0
Mk14=Response,R  7,3168,1,0
Mk15=Stimulus,S  1,3168,1,0
Mk16=Response,R  4,3174,1,0
Mk17=Response,R  7,3418,1,0
Mk18=Stimulus,S  2,3418,1,0

Quality Control

Quality control can be done through the .vmrk file and through the .eeg recordings. The things we need to check are the inputs that we coded for initially in the creation of the MMN task. For example, we created three MMN tasks, each with standard and deviation stimuli, SOA, and trial numbers. All of these inputs must be validated in the outputs from the .eeg and .vmrk files.

Stimulus Type

⚠️ **GitHub.com Fallback** ⚠️