:tocdepth: 3

**************************
Using gaze data from Cloud
**************************

The lsl stream will contain gaze data with a resolution of ~66 Hz.
You can get a higher sampling rate when you're using the gaze data downloaded from
Pupil Cloud. In order to do this, you'll need to align the timestamps collected during
the lsl streaming to the timestamps recorded in Cloud.

We have build a command line interface tool that allows you to perform a linear model fit,
mapping time recorded with lsl to time in Pupil Cloud, or vice versa.

The mapping uses events generated by the lsl relay.

Setup
=====

#. Install the lsl relay, including the extra dependencies via::

      pip install lsl-relay[pupil_cloud_alignment]

#. Start the lsl relay of your Pupil device.

#. In your lsl recording software (e.g. LabRecorder), select both the gaze data stream and the event stream.

#. Start the lsl recording through your software of choice.

#. In your Pupil Labs Companion App, tap the red "record" button and make sure the recording is running.

#. Run your experiment/data collection.

#. Stop the recording in the Pupil Labs Companion App.

#. Stop the lsl recording.

#. Wait till the gaze data was uploaded to Pupil Cloud and the 200 Hz gaze data was computed.

#. Export the gaze data from Pupil Cloud by right-clicking on the recording and selecting Downloads -> Download Recording.

#. Unzip the export from Pupil Cloud, place all files (xdf files and cloud exports) in the directory where you want them to be and run
   ``lsl_relay_time_alignment`` from your terminal.

.. important::
   If your recording is short (less than 1 minute), you should increase the frequency at which ``lsl.timesync`` events
   are being generated. As a rule of thumb you should aim for at least 3 events being sent throughout the recording, including
   ``recording.begin`` and ``recording.end`` events, which are generated by starting and ending the recording in the Pupil
   Labs Companion App.

   You can change the frequency at which ``lsl.timesync`` events are being sent by setting the ``--time_sync_interval``
   argument.


.. _lsl_relay_time_alignment:

Start the post-hoc time alignment
=================================
The time alignment is started by executing::

   lsl_relay_time_alignment <path_to_xdf> <paths_to_exports>

It requires the following positional arguments:

- ``--path_to_xdf`` is the path to the xdf file with your lsl recording. The xdf file must contain event streams
  for each recorded device.

- ``--paths_to_export`` is one or more paths pointing to the directory of raw data exports from pupil cloud.
  The raw data exports can be made for entire projects, or for each recording individually.
  The export must contain the events.csv file.


Output of the post-hoc time alignment
=====================================
The post-hoc time alignment outputs a json file 'time_alignment_parameters.json' in the same directory
where the events.csv file from the pupil cloud export is located. The json file contains three fields:

- ``cloud_to_lsl`` contains an intercept and a slope to map from cloud time to lsl time
- ``lsl_to_cloud`` also contains an intercept and a slope, but for mapping from lsl time to cloud time
- ``info`` contains the type of the model that was fitted and the version of the time alignment.

Example:

.. literalinclude:: ../../examples/cloud_recordings/time_alignment_parameters.json
  :language: json
  :linenos:

Use the parameters from the linear model to map between cloud time and lsl time
===============================================================================
Import the installed dependencies before running the example code below:

.. literalinclude:: ../../examples/time_mapping_from_parameters.py
   :language: python
   :lines: 2,4
   :linenos:

Load Pupil-Cloud-exported gaze data and convert nanoseconds to seconds

.. literalinclude:: ../../examples/time_mapping_from_parameters.py
   :language: python
   :lines: 6,9,12
   :linenos:

Import the parameters from the json file:

.. literalinclude:: ../../examples/time_mapping_from_parameters.py
  :language: python
  :lines: 15,16
  :linenos:

Define a simple linear model:

.. literalinclude:: ../../examples/time_mapping_from_parameters.py
  :language: python
  :lines: 20,21
  :linenos:

Apply the linear model to transform timestamps from Pupil Cloud to LSL time domain:

.. literalinclude:: ../../examples/time_mapping_from_parameters.py
  :language: python
  :lines: 25-27
  :linenos:

Save the time-aligned data as CSV file:

.. literalinclude:: ../../examples/time_mapping_from_parameters.py
  :language: python
  :lines: 29
  :linenos:

The new column ``'lsl time [s]'`` contains the lsl-compatible time stamps.


Time alignment under the hood
=============================
The post-hoc time alignment works by matching events from the lsl stream to events recorded by the companion device
(available through pupil cloud).

Each time a relay is started, it automatically sends events to the companion device. Each event has a unique name
containing a session id which is generated when starting the relay, and an event counter.
As soon as the companion device registers these events, they are timestamped and streamed back to lsl.

In the time alignment, we look for the unique session id in the stream header of the lsl-generated xdf file,
and in the event names available in cloud.
When we find a matching pair, we compute a linear regression(sklearn's
`linear_model.LinearRegression <https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html>`_)
between the time stamps of these events in both directions.
