{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Pupil Core device timing\n", "========================\n", "\n", "`PupilCore().light_stamper(...)` marks the onset of a light stimulus by sending an annotation containing the timestamp of the first frame where the light becomes visible to the\n", "World camera. This timestamp is ultimately what gets used to extract pupil data and as a reference for calculating time-critical measures, such as constriction latency and time-to-peak constriction. The validity of any time-related measures therefore depends on Pupil Capture's ability to synchronise the clocks of the cameras on a Pupil Core headset. How well does it handle this task? We were able to test camera clock synchronisation by putting the Pupil Core headset inside our integrating sphere and repeatedly flashing a bright orange light containing enough near infrared to afford detection by the Eye cameras as well as the World camera. Prior to each flash, concurrent `.light_stamper(...)`’s were instantiated, giving us the timestamp of the frame where the luminance change was detected independently for each camera. This protocol allowed us to get some insight into how well the data streams are synchronised. \n", "\n", "We ran [the testing protocol](05e_pupil_core_camera_sync.ipynb) on Mac and Windows (Pupil Capture v3.2-20) for *n* = 100 near-IR light flashes. For each run of the protocol, Eye camera resolution was kept at (192, 192) with Absolute Exposure Time of 25, and the World camera had (640, 480) and 60. Auto Exposure Mode was set to 'manual mode' for all cameras, and Auto Exposure Priority was disabled for the World camera. Whilst all of these camera settings may have the potential to impact the precision of the `.light_stamper(...)`, Frame rate is the most obvious. So we performed separate trials on Mac and Windows with a Frame rate of 60 and 120.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import os\n", "import os.path as op\n", "import glob\n", "\n", "import numpy as np\n", "import pandas as pd\n", "import seaborn as sns" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Calculate differences from annotation timestamps\n", "------------------------------------------------" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", " | Comparison | \n", "Timestamp difference (ms) | \n", "OS | \n", "FPS | \n", "
---|---|---|---|---|
0 | \n", "eye0 - world | \n", "58.807 | \n", "Windows | \n", "120 | \n", "
1 | \n", "eye0 - world | \n", "55.010 | \n", "Windows | \n", "120 | \n", "
2 | \n", "eye0 - world | \n", "58.925 | \n", "Windows | \n", "120 | \n", "
3 | \n", "eye0 - world | \n", "57.622 | \n", "Windows | \n", "120 | \n", "
4 | \n", "eye0 - world | \n", "58.371 | \n", "Windows | \n", "120 | \n", "
... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "
295 | \n", "eye0 - eye1 | \n", "-3.959 | \n", "macOS | \n", "60 | \n", "
296 | \n", "eye0 - eye1 | \n", "-3.968 | \n", "macOS | \n", "60 | \n", "
297 | \n", "eye0 - eye1 | \n", "12.266 | \n", "macOS | \n", "60 | \n", "
298 | \n", "eye0 - eye1 | \n", "-3.984 | \n", "macOS | \n", "60 | \n", "
299 | \n", "eye0 - eye1 | \n", "-3.991 | \n", "macOS | \n", "60 | \n", "
1200 rows × 4 columns
\n", "