EyeTrackingWithEprime - Meg Wiki

Revision 27 as of 2012-10-22 13:56:44

Clear message
location: EyeTrackingWithEprime

SMI now has a toolkit which makes using their eye trackers with E-Prime much easier, so the following is outdated

Ask Maarten van Casteren for details if you need to know more

Running an Eye tracking experiment with E-Prime

When using E-Prime with a SMI eye tracker, E-Prime should communicate with iView X, the eye tracking software, to start and stop the recording, indicate the start of each trial, specify the stimulus used and finally save the data. The way SMI prefer to do this is by using an ethernet connection and the UDP protocol, but E-Prime cannot handle this. For this reason SMI have developed a toolkit that can take care of the communication with the eye tracker. The toolkit consists of two parts: first a dll file that contains the actual executable code, and second an E-Prime package file that makes sure the functionality in the dll is available within an E-Prime script in a user friendly way.

To be able to use an eye tracker with this toolkit you need to do the following:

1 - Add the package to your E-Prime script. In the 'structure' window double-click on 'Experiment' and then select the 'Packages' tab. Press 'Add' and select the 'iView_X_SDK_CBU' package. If this is not available you'll have to install it: the file 'iViewXAPI_CBU.epk2' should be in 'Program Files\PST\E-Prime 2.0\Program\Packages'. If the Packages folder doesn't exist, it should be created.

2 - At the start of your experiment, when you want the eye tracker to start recording, add these lines, in an inline object:

' Connect to the Eye Tracker over the ethernet. The IP adresses are the
' ones we usually use, so this should work at the CBU without modification

if not iView_X_SDK_CBU_Connect("192.168.1.1", 4444, "192.168.1.2", 5555) then
        Mouse.ShowCursor True
        MsgBox "Connect failed: " & iView_X_SDK_CBU_GetErrorString
        iView_X_SDK_CBU_Abort
        End
end if

3 - At the start of each trial, or just before your main stimulus is being presented, add a 'remark line', preferably with your stimulus name as an argument (NB if you are presenting images, sending the name of the image file you are presenting will greatly facilitate analysis using the BeGaze software, as the associations between the trial and image will then be made automatically by the software. For more info, see BeGaze):

Dim strData As String
strData = "ET_REM " & c.GetAttrib("stim") & Chr(13) & Chr(10)
Serial.WriteString strData

4 - Finally, at the end of th experiment put these lines in an inline object:

' Stop eye tracking recording

strData = "ET_STP" & Chr(13) & Chr(10)
Serial.WriteString strData

' Save eyetracking data in a file with a subject, session etc.
dim filename as string
fileName = c.GetAttrib("Subject") & "_" & c.GetAttrib("Session") & ".idf"
strData = "ET_SAV C:\\" & fileName & Chr(13) & Chr(10)
Serial.WriteString strData

Obviously, the filename can be changed to whatever is best for you. Note that iView X cannot save to a directory that doesn't exist, so make sure to either create the correct directory, or write to root.

Running the script

When running the script you should go through these steps:

1 - Switch on the eye tracking hardware and install your subject.

2 - Start the iView X program on the eye tracking computer.

3 - Make sure the eye picture looks good and the eye gaze is correctly tracked. Verify this by asking the subject to look at all 4 corners of the screen.

4 - Start the 'WinCal' program on the stimulus presentation computer.

5 - Click on the calibration button in iView X. The stimulus presentation machine should now present a screen with a calibration dot. Press space on the eye tracking machine to start calibration. The dot should move around the screen.

6 - After succesful calibration, close down WinCal on the stimulus presentation machine.

7 - Start your E-Prime script. iView X should indicate that it is recording.

Calibration from within E-Prime

The calibration can also be performed by E-Prime. To be able to do this you need to incorporate a bit of inline code in your E-Prime script. This code will communicate with the eye tracker software and put targets on the screen in response to requests from iView X.

All of the calibration code is in a single inline object, and the only other thing that needs to be done is to define a serial port device in the 'Devices' tab of the 'Experimental Object Properties' dialogue. Make sure to give this the name 'Serial', which is actually the default name that E-Prime will suggest.

The first bit of inline code should be inserted in the E-Prime script at the point where the calibration is to be performed. To start the recording, mark the trials, and to close and save the recording the same bits of code should be inserted as used in the previous section.

The calibration uses the settings specified in iView X, which means that you will have to specify 'auto accept' or other settings there. The only thing that the script sets is the number of calibration points used. This is set to 13, but can easily be changed to another value.

One warning: this code hasn't been tested much yet, so might not always work as expected. Be careful and please report any problems to Maarten van Casteren.

' Flush serial connection

Serial.FlushOutBuffer

Const strENDL as String = Chr$(13) & Chr$(10)

' Clear eye tracker recording buffer

Dim strData as String

' Start the calibration

Const NumPoints as Integer = 13
Const ScreenWidth as integer = 1280
Const ScreenHeight as integer = 1024
Const CalWidth as integer = 640
Const CalHeight as integer = 512

' Wait for valid = 0 is no; 1 is yes
strData = "ET_CPA " & "0 " & "1"  & strENDL
Serial.WriteString strData
' Randomised point order = 0 is no; 1 is yes
strData = "ET_CPA " & "1 " & "1"  & strENDL
Serial.WriteString strData
' Accept points automatically = 0 is no; 1 is yes
strData = "ET_CPA " & "2 " & "1"  & strENDL
Serial.WriteString strData
' Check level of fixation = from 0 to 3 (how stringent it is with "wait for valid")
strData = "ET_LEV " & "2"  & strENDL
Serial.WriteString strData

' Set calibration area (i.e. screen resolution)
strData = "ET_CSZ " & str(ScreenWidth) & " " & str(ScreenHeight)  & strENDL
Serial.WriteString strData

'' Default points in eye tracker are 13. We can tweak

dim standardPoints(2, 13) as integer
dim shiftedPoints(2, 13) as integer

standardPoints(1,1) = 640
standardPoints(2,1) = 512
standardPoints(1,2) = 64
standardPoints(2,2) = 51
standardPoints(1,3) = 1216
standardPoints(2,3) = 51
standardPoints(1,4) = 64
standardPoints(2,4) = 973
standardPoints(1,5) = 1216
standardPoints(2,5) = 973
standardPoints(1,6) = 64
standardPoints(2,6) = 512
standardPoints(1,7) = 640
standardPoints(2,7) = 51
standardPoints(1,8) = 1216
standardPoints(2,8) = 512
standardPoints(1,9) = 640
standardPoints(2,9) = 973
standardPoints(1,10) = 352
standardPoints(2,10) = 282
standardPoints(1,11) = 928
standardPoints(2,11) = 282
standardPoints(1,12) = 352
standardPoints(2,12) = 743
standardPoints(1,13) = 928
standardPoints(2,13) = 743

'Scale up/down to match calibration area

Dim b as integer
For b  = 1 To 13
        shiftedPoints(1,b) = standardPoints(1,b) / screenWidth * CalWidth + (screenWidth - CalWidth)/2
        shiftedPoints(2,b) = standardPoints(2,b) / screenHeight * CalHeight + (screenHeight - CalHeight)/2

        strData = "ET_PNT " & str(b) & " " & str(shiftedPoints(1,b)) & " " & str(shiftedPoints(2,b)) & strENDL
        Serial.WriteString strData
Next

strData = "ET_CAL " & NumPoints & strENDL
Serial.WriteString strData

' Get the current canvas, set to gray and clear it

Dim Cvs as Canvas
Set Cvs = Display.Canvas

Cvs.FillColor = CColor("gray")  ' Yes, 'gray'
Cvs.Clear

Cvs.PenWidth = 8
Cvs.PenColor = CColor("0,0,0")

Keyboard.History.RemoveAll

' Process iView X commands

Dim points(NumPoints) as String   ' Array for the calibration points
Dim bReady as Boolean
bReady = False

Dim strRead as String
Dim strBuf as String
Dim strComLine as String
Dim strCom as String
Dim strPnt as String
Dim nRead as Long
Dim bWait as Boolean
bWait = false

Dim bPointOnShow as Boolean
bPointOnShow = false

Dim nPos as Long

Dim nLimit as Integer
nLimit = 0

While Not bReady
        
        nLimit = nLimit + 1

        if nLimit > 500 then
                MsgBox("Communication failure, time-out after 500 attempts")
                Stop
        end if

        ' Check the keyboard for presses on space, to force the current
        ' point to be accepted. This might be needed, sometimes

        if bPointOnShow and KeyBoard.History.Count > 0 then

                Dim nIndex As Long
                Dim theResponseData As ResponseData

                For nIndex = 1 To Keyboard.History.Count
                        ' Retrieve a single ResponseData object from the
                        ' collection of ResponseData objects stored in the
                        ' InputHistoryManager
                        Set theResponseData = Keyboard.History(nIndex)  
                        If Not theResponseData Is Nothing Then
                                If theResponseData.RESP = "{SPACE}" Then
                                        strData = "ET_ACC" & strENDL
                                        Serial.WriteString strData
                                End If
                        End If
                Next

                Keyboard.History.RemoveAll

        end if

        nRead = Serial.ReadString(strRead)

        if nRead = 0 and bWait then
                ' Nothing read from port and nothing in buffer to process
                Sleep(50)
        else
                strBuf = strBuf & strRead       ' Add newly read input to buffer

                nPos = InStr(strBuf, strENDL)

                if nPos = 0 then
                        bWait = true
                else
                        bWait = false
                        strComLine = Left(strBuf, nPos - 1)     ' Extract command and arguments
                        strBuf = Mid(strBuf, nPos + 2)          ' Remove from input buffer

                        Debug.print strComLine

                        strCom = Item$(strComLine, 1, 1, " ")   ' Extract command itself

                        'if strCom = "ET_CSZ" then
                                ' Screen size verification
                                'if CInt(Item$(strComLine, 2, 2, " \t")) <> Display.xres then
                                        'MsgBox("X Resolution error, iView X assumes:\n" & strComLine)
                                        'Stop
                                'e'nd if

                                'if CInt(Item$(strComLine, 3, 3, " \t")) <> Display.yres then
                                        'MsgBox("Y Resolution error, iView X assumes:\n" & strComLine)
                                        'Stop
                                'end if
                        if  strCom = "ET_PNT" then
                                ' Calibration point definition
                                points(CInt(Item$(strComLine, 2, 2, " \t"))) = Item$(strComLine, 3, 4, " \t")
                        elseif  strCom = "ET_CHG" then
                                ' Change calibration point
                                bPointOnShow = true
                                nLimit = 0      ' Create some extra time
                                strPnt = points(CInt(Item$(strComLine, 2, 2, " \t")))
                                Cvs.Clear
                                Cvs.Circle Cint(Item$(strPnt, 1, 1, " \t")), Cint(Item$(strPnt, 2, 2, " \t")), 8
                        elseif  strCom = "ET_FIN" then
                                ' Calibration finished
                                bReady = True
                        elseif strCom <> "ET_CPA" and strCom <> "ET_ACC" and strCom <> "ET_BRK" and strCom <> "ET_CLR" and strCom <> "ET_REC" and strCom <> "ET_CAL" and strCom <> "ET_LEV" and strCom <> "ET_CSZ" then
                                MsgBox("Error in communications with iView X, received:\n" & strComLine)
                                Stop
                        end if
                end if
        end if

Wend

Cvs.Clear
Set Cvs = Nothing

Validation

You can also validate (check) your calibration using the following script:

'' Variables already declared in Calibration AVG & JC
' Start validation

strData = "ET_VLS" & strENDL
Serial.WriteString strData

' Set randomise point order to ON

'strData = "ET_CPA 1 1" & strENDL
'Serial.WriteString strData

' Turn Auto accept ON

'strData = "ET_CPA 2 1" & strENDL
'Serial.WriteString strData

' Get the current canvas, set to gray and clear it

Set Cvs = Display.Canvas

Cvs.FillColor = CColor("gray")  ' Yes, 'gray'
Cvs.Clear

Cvs.PenWidth = 8
Cvs.PenColor = CColor("0,0,0")

Keyboard.History.RemoveAll

' Process iView X commands

bReady = False
bWait = false
bPointOnShow = false
nLimit = 0

While Not bReady
        
        nLimit = nLimit + 1

        if nLimit > 500 then
                MsgBox("Communication failure, time-out after 500 attempts")
                Stop
        end if

        ' Check the keyboard for presses on space, to force the current
        ' point to be accepted. This might be needed, sometimes

        if bPointOnShow and KeyBoard.History.Count > 0 then

                For nIndex = 1 To Keyboard.History.Count
                        ' Retrieve a single ResponseData object from the
                        ' collection of ResponseData objects stored in the
                        ' InputHistoryManager
                        Set theResponseData = Keyboard.History(nIndex)  
                        If Not theResponseData Is Nothing Then
                                If theResponseData.RESP = "{SPACE}" Then
                                        strData = "ET_ACC" & strENDL
                                        Serial.WriteString strData
                                End If
                        End If
                Next

                Keyboard.History.RemoveAll

        end if

        nRead = Serial.ReadString(strRead)

        if nRead = 0 and bWait then
                ' Nothing read from port and nothing in buffer to process
                Sleep(50)
        else
                strBuf = strBuf & strRead       ' Add newly read input to buffer

                nPos = InStr(strBuf, strENDL)

                if nPos = 0 then
                        bWait = true
                else
                        bWait = false
                        strComLine = Left(strBuf, nPos - 1)     ' Extract command and arguments
                        strBuf = Mid(strBuf, nPos + 2)          ' Remove from input buffer

                        Debug.print strComLine

                        strCom = Item$(strComLine, 1, 1, " ")   ' Extract command itself

                        if strCom = "ET_CSZ" then
                                ' Screen size verification
                                if CInt(Item$(strComLine, 2, 2, " \t")) <> Display.xres then
                                        MsgBox("X Resolution error, iView X assumes:\n" & strComLine)
                                        Stop
                                end if

                                if CInt(Item$(strComLine, 3, 3, " \t")) <> Display.yres then
                                        MsgBox("Y Resolution error, iView X assumes:\n" & strComLine)
                                        Stop
                                end if
                        elseif  strCom = "ET_PNT" then
                                ' Calibration point definition
                                points(CInt(Item$(strComLine, 2, 2, " \t"))) = Item$(strComLine, 3, 4, " \t")
                        elseif  strCom = "ET_CHG" then
                                ' Change calibration point
                                bPointOnShow = true
                                nLimit = 0      ' Create some extra time
                                strPnt = points(CInt(Item$(strComLine, 2, 2, " \t")))
                                Cvs.Clear
                                Cvs.Circle Cint(Item$(strPnt, 1, 1, " \t")), Cint(Item$(strPnt, 2, 2, " \t")), 8
                        elseif  strCom = "ET_FIN" then
                                ' Calibration finished
                                bReady = True
                        elseif strCom <> "ET_CPA" and strCom <> "ET_ACC" and strCom <> "ET_BRK" and strCom <> "ET_CLR" and strCom <> "ET_REC" and strCom <> "ET_CAL" then
                                MsgBox("Error in communications with iView X, received:\n" & strComLine)
                                Stop
                        end if
                end if
        end if

Wend

Cvs.Clear
Set Cvs = Nothing

Drift correction

And you can do drift correction using this script:

' Start drift correction
strData = "ET_RCL" & strENDL
Serial.WriteString strData

' Get the current canvas, set to gray and clear it
Set Cvs = Display.Canvas

Cvs.FillColor = CColor("gray")  ' Yes, 'gray'
Cvs.Clear

Cvs.PenWidth = 8
Cvs.PenColor = CColor("0,0,0")

' Process iView X commands

bReady = False
bWait = false
nLimit = 0

' Put calibration point
bPointOnShow = true
'strPnt = points(CInt(Item$(strComLine, 2, 2, " \t")))
strPnt = points(CInt(Item$("ET_CHG 1", 2, 2, " \t")))
Cvs.Clear
Cvs.Circle Cint(Item$(strPnt, 1, 1, " \t")), Cint(Item$(strPnt, 2, 2, " \t")), 8

While Not bReady
        
        nLimit = nLimit + 1

        if nLimit > 500 then
                MsgBox("Communication failure, time-out after 500 attempts")
                Stop
        end if

        ' Check the keyboard for presses on space, to force the current
        ' point to be accepted. This might be needed, sometimes

        if bPointOnShow and KeyBoard.History.Count > 0 then

                For nIndex = 1 To Keyboard.History.Count
                        ' Retrieve a single ResponseData object from the
                        ' collection of ResponseData objects stored in the
                        ' InputHistoryManager
                        Set theResponseData = Keyboard.History(nIndex)  
                        If Not theResponseData Is Nothing Then
                                If theResponseData.RESP = "{SPACE}" Then
                                        strData = "ET_ACC" & strENDL
                                        Serial.WriteString strData
                                End If
                        End If
                Next

                Keyboard.History.RemoveAll

        end if

        nRead = Serial.ReadString(strRead)

        if nRead = 0 and bWait then
                ' Nothing read from port and nothing in buffer to process
                Sleep(50)
        else
                strBuf = strBuf & strRead       ' Add newly read input to buffer

                nPos = InStr(strBuf, strENDL)

                if nPos = 0 then
                        bWait = true
                else
                        bWait = false
                        strComLine = Left(strBuf, nPos - 1)     ' Extract command and arguments
                        strBuf = Mid(strBuf, nPos + 2)          ' Remove from input buffer

                        Debug.print strComLine

                        strCom = Item$(strComLine, 1, 1, " ")   ' Extract command itself

                        if  strCom = "ET_FIN" then
                                ' Calibration finished
                                bReady = True
                        end if
                end if
        end if
Wend

The main advantage of letting E-Prime take care of the calibration is that you don't have to use WinCal any more. Combine these E-Prime scripts to start the recording, do the calibration, validation, drift correction, mark the trials, stop the recording and save the results to disk.