Asynchonous API

Device Discovery

class pupil_labs.realtime_api.discovery.Network[source]

Bases: object

async close()[source]
Return type:

None

property devices: Tuple[DiscoveredDeviceInfo, ...]
Return type:

Tuple[DiscoveredDeviceInfo, …]

async wait_for_new_device(timeout_seconds=None)[source]
Return type:

Optional[DiscoveredDeviceInfo]

async pupil_labs.realtime_api.discovery.discover_devices(timeout_seconds=None)[source]

Use Bonjour to find devices in the local network that serve the Realtime API.

Parameters:

timeout_seconds (Optional[float]) – Stop after timeout_seconds. If None, run discovery forever.

Return type:

AsyncIterator[DiscoveredDeviceInfo]

pupil_labs.realtime_api.discovery.is_valid_service_name(name)[source]
Return type:

bool

Remote Control

class pupil_labs.realtime_api.device.Device(*args, **kwargs)[source]

Bases: DeviceBase

async close()[source]
async get_calibration()[source]
Raises:

pupil_labs.realtime_api.device.DeviceError – if the request fails

Return type:

ndarray

async get_status()[source]
Raises:

pupil_labs.realtime_api.device.DeviceError – if the request fails

Return type:

Status

async recording_cancel()[source]
Raises:

pupil_labs.realtime_api.device.DeviceError – if the recording could not be started Possible reasons include - Recording not running

async recording_start()[source]
Raises:

pupil_labs.realtime_api.device.DeviceError – if the recording could not be started. Possible reasons include - Recording already running - Template has required fields - Low battery - Low storage - No wearer selected - No workspace selected - Setup bottom sheets not completed

Return type:

str

async recording_stop_and_save()[source]
Raises:

pupil_labs.realtime_api.device.DeviceError – if the recording could not be started Possible reasons include - Recording not running - template has required fields

async send_event(event_name, event_timestamp_unix_ns=None)[source]
Raises:

pupil_labs.realtime_api.device.DeviceError – if sending the event fails

Return type:

Event

async status_updates()[source]
Return type:

AsyncIterator[Union[Phone, Hardware, Sensor, Recording, NetworkDevice]]

exception pupil_labs.realtime_api.device.DeviceError[source]

Bases: Exception

class pupil_labs.realtime_api.device.StatusUpdateNotifier(device, callbacks)[source]

Bases: object

async receive_updates_start()[source]
Return type:

None

async receive_updates_stop()[source]
pupil_labs.realtime_api.device.UpdateCallback

Type annotation for synchronous and asynchronous callbacks

Alias of Union[Callable[[Component], None], Callable[[Component], Awaitable[None]]]

pupil_labs.realtime_api.device.UpdateCallbackAsync

Type annotation for asynchronous update callbacks

Alias of Callable[[Component], Awaitable[None]]

pupil_labs.realtime_api.device.UpdateCallbackSync

Type annotation for synchronous update callbacks

Alias of Callable[[Component], None]

Streaming

Gaze Data

namedtuple pupil_labs.realtime_api.streaming.gaze.EyestateGazeData(x, y, worn, pupil_diameter_left, eyeball_center_left_x, eyeball_center_left_y, eyeball_center_left_z, optical_axis_left_x, optical_axis_left_y, optical_axis_left_z, pupil_diameter_right, eyeball_center_right_x, eyeball_center_right_y, eyeball_center_right_z, optical_axis_right_x, optical_axis_right_y, optical_axis_right_z, timestamp_unix_seconds)[source]

Bases: NamedTuple

EyestateGazeData(x, y, worn, pupil_diameter_left, eyeball_center_left_x, eyeball_center_left_y, eyeball_center_left_z, optical_axis_left_x, optical_axis_left_y, optical_axis_left_z, pupil_diameter_right, eyeball_center_right_x, eyeball_center_right_y, eyeball_center_right_z, optical_axis_right_x, optical_axis_right_y, optical_axis_right_z, timestamp_unix_seconds)

Fields:
  1.  x (float) – Alias for field number 0

  2.  y (float) – Alias for field number 1

  3.  worn (bool) – Alias for field number 2

  4.  pupil_diameter_left (float) – Alias for field number 3

  5.  eyeball_center_left_x (float) – Alias for field number 4

  6.  eyeball_center_left_y (float) – Alias for field number 5

  7.  eyeball_center_left_z (float) – Alias for field number 6

  8.  optical_axis_left_x (float) – Alias for field number 7

  9.  optical_axis_left_y (float) – Alias for field number 8

  10.  optical_axis_left_z (float) – Alias for field number 9

  11.  pupil_diameter_right (float) – Alias for field number 10

  12.  eyeball_center_right_x (float) – Alias for field number 11

  13.  eyeball_center_right_y (float) – Alias for field number 12

  14.  eyeball_center_right_z (float) – Alias for field number 13

  15.  optical_axis_right_x (float) – Alias for field number 14

  16.  optical_axis_right_y (float) – Alias for field number 15

  17.  optical_axis_right_z (float) – Alias for field number 16

  18.  timestamp_unix_seconds (float) – Alias for field number 17

property datetime
classmethod from_raw(data)[source]
Return type:

EyestateGazeData

property timestamp_unix_ns
namedtuple pupil_labs.realtime_api.streaming.gaze.GazeData(x, y, worn, timestamp_unix_seconds)[source]

Bases: NamedTuple

GazeData(x, y, worn, timestamp_unix_seconds)

Fields:
  1.  x (float) – Alias for field number 0

  2.  y (float) – Alias for field number 1

  3.  worn (bool) – Alias for field number 2

  4.  timestamp_unix_seconds (float) – Alias for field number 3

property datetime
classmethod from_raw(data)[source]
Return type:

GazeData

property timestamp_unix_ns
namedtuple pupil_labs.realtime_api.streaming.gaze.Point(x, y)[source]

Bases: NamedTuple

Point(x, y)

Fields:
  1.  x (float) – Alias for field number 0

  2.  y (float) – Alias for field number 1

class pupil_labs.realtime_api.streaming.gaze.RTSPGazeStreamer(*args, **kwargs)[source]

Bases: RTSPRawStreamer

async receive()[source]
Return type:

AsyncIterator[Union[GazeData, DualMonocularGazeData, EyestateGazeData]]

async pupil_labs.realtime_api.streaming.gaze.receive_gaze_data(url, *args, **kwargs)[source]
Return type:

AsyncIterator[Union[GazeData, DualMonocularGazeData, EyestateGazeData]]

IMU Data

namedtuple pupil_labs.realtime_api.streaming.imu.Data3D(x, y, z)[source]

Bases: NamedTuple

Data3D(x, y, z)

Fields:
  1.  x (float) – Alias for field number 0

  2.  y (float) – Alias for field number 1

  3.  z (float) – Alias for field number 2

namedtuple pupil_labs.realtime_api.streaming.imu.IMUData(gyro_data, accel_data, quaternion, timestamp_unix_seconds)[source]

Bases: NamedTuple

IMUData(gyro_data, accel_data, quaternion, timestamp_unix_seconds)

Fields:
  1.  gyro_data (Data3D) – Alias for field number 0

  2.  accel_data (Data3D) – Alias for field number 1

  3.  quaternion (Quaternion) – Alias for field number 2

  4.  timestamp_unix_seconds (float) – Alias for field number 3

property datetime
property timestamp_unix_nanoseconds
property timestamp_unix_ns
pupil_labs.realtime_api.streaming.imu.IMUPacket_to_IMUData(imu_packet)[source]
Return type:

IMUData

namedtuple pupil_labs.realtime_api.streaming.imu.Quaternion(x, y, z, w)[source]

Bases: NamedTuple

Quaternion(x, y, z, w)

Fields:
  1.  x (float) – Alias for field number 0

  2.  y (float) – Alias for field number 1

  3.  z (float) – Alias for field number 2

  4.  w (float) – Alias for field number 3

class pupil_labs.realtime_api.streaming.imu.RTSPImuStreamer(*args, **kwargs)[source]

Bases: RTSPRawStreamer

async receive()[source]
Return type:

AsyncIterator[IMUData]

async pupil_labs.realtime_api.streaming.imu.receive_imu_data(url, *args, **kwargs)[source]
Return type:

AsyncIterator[IMUData]

Scene Video

pupil_labs.realtime_api.streaming.video.BGRBuffer

Type annotation for raw BGR image buffers of the scene camera

class pupil_labs.realtime_api.streaming.video.RTSPVideoFrameStreamer(*args, **kwargs)[source]

Bases: RTSPRawStreamer

async receive()[source]
Return type:

AsyncIterator[VideoFrame]

property sprop_parameter_set_payloads: List[ByteString] | None
Raises:

pupil_labs.realtime_api.streaming.base.SDPDataNotAvailableError

Return type:

Optional[List[ByteString]]

namedtuple pupil_labs.realtime_api.streaming.video.VideoFrame(av_frame, timestamp_unix_seconds)[source]

Bases: NamedTuple

VideoFrame(av_frame, timestamp_unix_seconds)

Fields:
  1.  av_frame (VideoFrame) – Alias for field number 0

  2.  timestamp_unix_seconds (float) – Alias for field number 1

bgr_buffer()[source]
Return type:

ndarray[Any, dtype[uint8]]

property datetime
property timestamp_unix_ns
to_ndarray(*args, **kwargs)[source]
async pupil_labs.realtime_api.streaming.video.receive_video_frames(url, *args, **kwargs)[source]
Return type:

AsyncIterator[VideoFrame]

pupil_labs.realtime_api.streaming.nal_unit.extract_payload_from_nal_unit(unit)[source]
  • prepend NAL unit start code to payload if necessary

  • handle fragmented units (of type FU-A)

Inspired by https://github.com/runtheops/rtsp-rtp/blob/master/transport/primitives/nal_unit.py Rewritten due to license incompatibility.

Return type:

ByteString

Raw RTSP Data

namedtuple pupil_labs.realtime_api.streaming.base.RTSPData(raw, timestamp_unix_seconds)[source]

Bases: NamedTuple

RTSPData(raw, timestamp_unix_seconds)

Fields:
  1.  raw (ByteString) – Alias for field number 0

  2.  timestamp_unix_seconds (float) – Alias for field number 1

_asdict()

Return a new dict which maps field names to their values.

_field_defaults = {}

Type:    dict

_fields = ('raw', 'timestamp_unix_seconds')

Type:    tuple

classmethod _make(iterable)

Make a new RTSPData object from a sequence or iterable

_replace(**kwds)

Return a new RTSPData object replacing specified fields with new values

property datetime
property timestamp_unix_ns
class pupil_labs.realtime_api.streaming.base.RTSPRawStreamer(*args, **kwargs)[source]

Bases: object

Forwards all arguments to aiortsp.rtsp.reader.RTSPReader

property encoding
Raises:

pupil_labs.realtime_api.streaming.base.SDPDataNotAvailableError

property reader
async receive()[source]
Return type:

AsyncIterator[RTSPData]

exception pupil_labs.realtime_api.streaming.base.SDPDataNotAvailableError[source]

Bases: Exception

exception pupil_labs.realtime_api.streaming.base._UnknownClockoffsetError[source]

Bases: Exception

class pupil_labs.realtime_api.streaming.base._WallclockRTSPReader(*args, **kwargs)[source]

Bases: RTSPReader

_calculate_clock_offset(sr_pkt)[source]
absolute_timestamp_from_packet(packet)[source]

Returns the Unix epoch timestamp for the input packet

Uses the cached clock offset between the NTP and relative timestamp provided by the RTCP packets.

handle_rtcp(rtcp)[source]

Handle an incoming RTP packet

relative_timestamp_from_packet(packet)[source]
async pupil_labs.realtime_api.streaming.base.receive_raw_rtsp_data(url, *args, **kwargs)[source]
Return type:

AsyncIterator[RTSPData]

Time Echo Protocol

Manual time offset estimation via the Pupil Labs Time Echo protocol

The Realtime Network API host device timestamps its data with nanoseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC). This clock is kept in sync by the operating system through NTP (Network Time Protocol). For some use cases, this sync is not good enough. For more accurate time syncs, the Time Echo protocol allows the estimation of the direct offset between the host’s and the client’s clocks.

The Time Echo protocol works in the following way:

  1. The API host (Pupil Invisible Companion app) opens a TCP server at a specific port

  2. The client connects to the host address and port

  3. The client sends its current time (t1) in milliseconds as an uint64 in network byte order to the host

  4. The host responds with the time echo, two uint64 values in network byte order 1. The first value is equal to the sent client time (t1) 2. The second value corresponds to the host’s time in milliseconds (tH)

  5. The client calculates the duration of steps 3 and 4 (roundtrip time) by measuring the client time before sending the request (t1) and after receiving the echo (t2)

  6. The protocol assumes that the transport duration is symmetric. It will assume that tH was measured at the same time as the midpoint betwee t1 and t2.

  7. To calculate the offset between the host’s and client’s clock, we subtract tH from the client’s midpoint (t1 + t2) / 2:

    offset_ms = ((t1 + t2) / 2) - tH
    
  8. This measurement can be repeated multiple times to make the time offset estimation more robust.

To convert client to host time, subtract the offset:

host_time_ms = client_time_ms() - offset_ms

This is particularly helpful to accurately timestamp local events, e.g. a stimulus presentation.

To convert host to client time, add the offset:

client_time_ms = host_time_ms() + offset_ms

This is particularly helpful to convert the received data into the client’s time domain.


class pupil_labs.realtime_api.time_echo.Estimate(measurements)[source]

Bases: object

Provides easy access to statistics over a collection of measurements

property mean: float
Return type:

float

property median: float
Return type:

float

property std: float
Return type:

float

namedtuple pupil_labs.realtime_api.time_echo.TimeEcho(roundtrip_duration_ms, time_offset_ms)[source]

Bases: NamedTuple

Measurement of a single time echo

Fields:
  1.  roundtrip_duration_ms (int) – Round trip duration of the time echo, in milliseconds

  2.  time_offset_ms (int) – Time offset between host and client, in milliseconds

namedtuple pupil_labs.realtime_api.time_echo.TimeEchoEstimates(roundtrip_duration_ms, time_offset_ms)[source]

Bases: NamedTuple

Provides estimates for the roundtrip duration and time offsets

Fields:
  1.  roundtrip_duration_ms (Estimate) – Alias for field number 0

  2.  time_offset_ms (Estimate) – Alias for field number 1

pupil_labs.realtime_api.time_echo.TimeFunction

Returns time in milliseconds

Alias of Callable[[], int]

class pupil_labs.realtime_api.time_echo.TimeOffsetEstimator(address, port)[source]

Bases: object

async estimate(number_of_measurements=100, sleep_between_measurements_seconds=None, time_fn_ms=<function 'time_ms'>)[source]
Return type:

Optional[TimeEchoEstimates]

async static request_time_echo(time_fn_ms, reader, writer)[source]

Request a time echo, measure the roundtrip time, and estimate the time offset

Return type:

TimeEcho

pupil_labs.realtime_api.time_echo.time_ms()[source]

Return milliseconds since Unix epoch (January 1, 1970, 00:00:00 UTC)