Interface control document
1. Identification
| Item | Value |
|---|---|
| Document | ICD-CMSMARTEYE-001 rev 1 |
| Product | SmartEye module (SmartEye Module in the module registry) |
| Product versions covered | f44f1ac |
| Peer | SmartEye eye-tracking software, sending its UDP output packet type 4 |
| Host | HEAT Capture, HEAT.Capture.ModuleSDK |
| Settings section | HEAT.SmartEyeModule |
| Data model | SimulationFrame carrying one Entity with GazeComponent, PupillometryDataComponent, SpatialComponent and BiometricSensorComponent |
| Date | 2026-09-02 |
| Owner | VRAI Simulation |
2. Scope
This document defines the UDP datagram envelope the module accepts, the packet-type and data-identifier values it decodes, the byte order and per-identifier wire sizes it enforces, and the mapping from those identifiers to the fields and readings published to HEAT Capture.
The interface is receive-only. The module opens a listening socket and never sends anything to SmartEye: no subscription request, no handshake, no acknowledgement, no configuration. SmartEye must be configured independently to send its output to the workstation and port the module is listening on.
3. Interface inventory
| ID | Name | Direction | Transport | Peer |
|---|---|---|---|---|
| IF-CMSMARTEYE-01 | SmartEye output packet stream | SmartEye to module | UDP datagrams to a bound local port, default 5003 | SmartEye software |
| IF-CMSMARTEYE-02 | Frame publication | Module to host | Module SDK publication path | HEAT Capture |
4. IF-CMSMARTEYE-01 SmartEye output packet stream
4.1 Transport and addressing
| Item | Value |
|---|---|
| Socket | UdpClient bound to IPAddress.Any on the configured port |
| Port | UdpPort setting, default 5003 |
| Local address | All interfaces. No interface selection setting exists. |
| Sender address | Not checked. The remote endpoint is read into a discarded variable and never validated or filtered. |
| Multicast | No multicast group is joined, so only unicast and broadcast traffic addressed to the bound port is received. |
| Socket receive buffer | Set to 65507 bytes, one maximum-size datagram |
| Maximum datagram | 65507 bytes. Larger datagrams are not representable in UDP. |
| Read model | Blocking Receive on a dedicated background thread named SmartEye-UDP |
Because the socket receive buffer holds roughly one maximum datagram, a burst that arrives while the receive thread is parsing or publishing can be discarded by the operating system before the module sees it. There is no counter or log for that loss.
4.2 Connection lifecycle
There is no connection. UDP is connectionless and the module is a passive listener, so there is no handshake, no keepalive, no disconnect detection and no reconnect logic.
StartCapture logs SmartEye Module starting capture on UDP port {Port}, constructs the receiver
with the port value read at that moment, subscribes to its packet event, and starts the thread, which
logs SmartEye UDP receiver listening on port {Port}. StartCapture is bounded by the SDK
StartupTimeout of 5 seconds; the module does not override it. If the port is already in use the
socket constructor throws inside StartCapture, the SDK logs
SmartEye Module StartCapture failed. and the module does not start.
EndCapture logs “SmartEye Module ending capture”, unsubscribes the packet event, stops the receiver and
disposes it. Stopping clears the running flag and closes the socket, which unblocks the pending Receive;
the thread then logs “SmartEye UDP receiver stopped”. EndCapture does not wait for the thread to exit,
and it does not clear the retained snapshot. See 5.6.
Silence is indistinguishable from absence. If SmartEye is not running, is not configured to send, or is sending to a different port, the module reports nothing: the listening log line appears exactly as it would with a healthy sender. See Troubleshooting.
4.3 Authentication
None. There is no credential, token, shared secret or source-address check. Any process that can send a UDP datagram to the bound port, on any interface of the workstation, can inject a packet that the module will accept as a measurement.
4.4 Packet envelope
All integers and all floating-point values are big-endian. The module decodes them with
BinaryPrimitives.ReadUInt16BigEndian, ReadUInt32BigEndian and ReadDoubleBigEndian, so no host
byte-order dependency exists.
A datagram is a 4-byte header followed by a sequence of length-prefixed entries:
Datagram
+0 packetType u16 big-endian, must equal 4
+2 length u16 big-endian, payload byte count following the header
+4 payload length bytes
Payload: zero or more entries, packed with no padding
+0 dataId u16 big-endian, see 4.5
+2 dataLength u16 big-endian, byte count of this entry's data
+4 data dataLength bytesEnvelope acceptance rules, in the order the parser applies them:
- A datagram shorter than 4 bytes is rejected.
- A
packetTypeother than4is rejected. SmartEye’s other packet types are not decoded. - A datagram shorter than
4 + lengthis rejected. - Only the first
lengthbytes after the header are treated as payload. Any bytes beyond that are ignored. - Entries are read until fewer than 4 bytes remain in the payload.
Rejection means the whole datagram is discarded and no event is raised. Nothing is logged for a discarded datagram at any log level.
Data element wire sizes are fixed: 1 byte for a boolean, 4 for an unsigned integer, 8 for a
double-precision float, 24 for a three-component vector of doubles, 32 for a four-component vector of
doubles. Vector components are packed in X, Y, Z order, and the quaternion in X, Y, Z, W
order.
4.5 Data identifiers decoded
Each entry is matched on dataId together with an exact dataLength. Both must match for the value to
be taken. “Destination” states where the value ends up: a published field or reading name, or the point
at which it is dropped.
Frame information:
| Data id | Size | Type | Destination |
|---|---|---|---|
0x0001 | 4 | u32 | Retained as FrameNumber, never published |
0x0003 | 8 | f64 | Retained as TimeStamp, never published. The SmartEye timestamp is not used as the frame time; see 6. |
0x0005 | 8 | f64 | Frame rate. Decoded, then dropped when the packet is copied to the retained snapshot. |
Head pose:
| Data id | Size | Type | Destination |
|---|---|---|---|
0x0010 | 24 | vec3 f64 | SpatialComponent.pose.position |
0x0012 | 24 | vec3 f64 | Rodrigues rotation vector, converted to SpatialComponent.pose.orientation only when 0x001d is absent |
0x001d | 32 | vec4 f64 | Quaternion, SpatialComponent.pose.orientation |
0x0013 | 24 | vec3 f64 | Nose direction. Retained, never published. |
0x0014 | 24 | vec3 f64 | Up direction. Retained, never published. |
0x0015 | 24 | vec3 f64 | Left-ear direction. Decoded, then dropped at the snapshot copy. |
0x0016 | 8 | f64 | Head heading. Retained, never published. |
0x0017 | 8 | f64 | Head pitch. Retained, never published. |
0x0018 | 8 | f64 | Head roll. Retained, never published. |
Gaze, raw and filtered. Where two identifiers feed one published field, the filtered value is used when present and the raw value only when the filtered one is absent in that packet:
| Data id | Size | Type | Destination |
|---|---|---|---|
0x001a | 24 | vec3 f64 | GazeComponent.gaze_origin, fallback for 0x0500 |
0x0500 | 24 | vec3 f64 | GazeComponent.gaze_origin, preferred |
0x001b | 24 | vec3 f64 | GazeComponent.left_gaze_origin, fallback for 0x0501 |
0x0501 | 24 | vec3 f64 | GazeComponent.left_gaze_origin, preferred |
0x001c | 24 | vec3 f64 | GazeComponent.right_gaze_origin, fallback for 0x0502 |
0x0502 | 24 | vec3 f64 | GazeComponent.right_gaze_origin, preferred |
0x0020 | 24 | vec3 f64 | GazeComponent.combined_eye_position |
0x0023 | 24 | vec3 f64 | GazeComponent.left_eye_position |
0x0026 | 24 | vec3 f64 | GazeComponent.right_eye_position |
0x0021 | 24 | vec3 f64 | GazeComponent.gaze_direction, fallback for 0x0030 |
0x0030 | 24 | vec3 f64 | GazeComponent.gaze_direction, preferred |
0x0022 | 8 | f64 | GazeComponent.gaze_direction_quality, fallback for 0x0031 |
0x0031 | 8 | f64 | GazeComponent.gaze_direction_quality, preferred |
0x0024 | 24 | vec3 f64 | GazeComponent.left_gaze_direction, fallback for 0x0032 |
0x0032 | 24 | vec3 f64 | GazeComponent.left_gaze_direction, preferred |
0x0025 | 8 | f64 | GazeComponent.left_gaze_direction_quality, fallback for 0x0033 |
0x0033 | 8 | f64 | GazeComponent.left_gaze_direction_quality, preferred |
0x0027 | 24 | vec3 f64 | GazeComponent.right_gaze_direction, fallback for 0x0034 |
0x0034 | 24 | vec3 f64 | GazeComponent.right_gaze_direction, preferred |
0x0028 | 8 | f64 | GazeComponent.right_gaze_direction_quality, fallback for 0x0035 |
0x0035 | 8 | f64 | GazeComponent.right_gaze_direction_quality, preferred |
Filtered and raw are selected per field, not per packet. A single published frame can carry a filtered combined direction and a raw left direction if that is what the packet contained. The values are never averaged or blended.
Eyelid and pupil:
| Data id | Size | Type | Destination |
|---|---|---|---|
0x0050 | 8 | f64 | PupillometryDataComponent.eyelid_openness |
0x0051 | 8 | f64 | PupillometryDataComponent.eyelid_openness_quality |
0x0052 | 8 | f64 | PupillometryDataComponent.left_eyelid_openness |
0x0053 | 8 | f64 | PupillometryDataComponent.left_eyelid_openness_quality |
0x0054 | 8 | f64 | PupillometryDataComponent.right_eyelid_openness |
0x0055 | 8 | f64 | PupillometryDataComponent.right_eyelid_openness_quality |
0x0060 | 8 | f64 | pupil_diameter, fallback for 0x0066 |
0x0066 | 8 | f64 | pupil_diameter, preferred |
0x0061 | 8 | f64 | pupil_diameter_quality, fallback for 0x0067 |
0x0067 | 8 | f64 | pupil_diameter_quality, preferred |
0x0062 | 8 | f64 | left_eye_pupil_diameter, fallback for 0x0068 |
0x0068 | 8 | f64 | left_eye_pupil_diameter, preferred |
0x0063 | 8 | f64 | left_eye_pupil_diameter_quality, fallback for 0x0069 |
0x0069 | 8 | f64 | left_eye_pupil_diameter_quality, preferred |
0x0064 | 8 | f64 | right_eye_pupil_diameter, fallback for 0x006a |
0x006a | 8 | f64 | right_eye_pupil_diameter, preferred |
0x0065 | 8 | f64 | right_eye_pupil_diameter_quality, fallback for 0x006b |
0x006b | 8 | f64 | right_eye_pupil_diameter_quality, preferred |
Eyelid openness has no filtered identifier in this parser, so 0x0050 to 0x0055 are used as sent.
Analysis, status and occlusion:
| Data id | Size | Type | Destination |
|---|---|---|---|
0x003f | 1 | bool | GazeComponent.blink |
0x003d | 1 | bool | Saccade flag. Retained, never published. |
0x003e | 1 | bool | Fixation flag. Retained, never published. |
0x00c0 | 4 | u32 | Tracking-state code. Retained, never published. |
0x0600 | 1 | bool | GazeComponent.has_glasses |
0x0601 | 8 | f64 | Glasses-detection quality. Retained, never published. |
0x0602 | 1 | bool | Face-mask flag. Retained, never published. |
0x0604 | 1 | bool | GazeComponent.left_eye_occluded |
0x0605 | 8 | f64 | Left-eye occlusion quality. Retained, never published. |
0x0606 | 1 | bool | GazeComponent.right_eye_occluded |
0x0607 | 8 | f64 | Right-eye occlusion quality. Retained, never published. |
Occlusion is published as a bare boolean while its quality score is discarded, so a consumer cannot tell a confident occlusion from a low-confidence guess.
Drowsiness, emotion and activity, all published as named readings on BiometricSensorComponent:
| Data id | Size | Type | Reading name |
|---|---|---|---|
0x03f5 | 4 | u32 | Drowsiness9Level |
0x03f6 | 8 | f64 | Drowsiness9LevelQ |
0x03f7 | 4 | u32 | Drowsiness9LevelStatus |
0x03fa | 8 | f64 | Anger |
0x03fb | 8 | f64 | AngerQ |
0x03fc | 8 | f64 | Disgust |
0x03fd | 8 | f64 | DisgustQ |
0x03fe | 8 | f64 | Happiness |
0x03ff | 8 | f64 | HappinessQ |
0x0503 | 8 | f64 | Neutral |
0x0504 | 8 | f64 | NeutralQ |
0x0505 | 8 | f64 | Sadness |
0x0506 | 8 | f64 | SadnessQ |
0x0507 | 8 | f64 | Surprise |
0x0508 | 8 | f64 | SurpriseQ |
0x0509 | 8 | f64 | Valence |
0x050a | 8 | f64 | ValenceQ |
0x050b | 8 | f64 | Mood |
0x050c | 8 | f64 | MoodQ |
0x050d | 4 | u32 | DominantEmotion |
0x050e | 8 | f64 | DominantEmotionQ |
0x03f0 | 1 | bool | Speaking, published as 1.0 or 0.0 |
0x03f1 | 8 | f64 | SpeakingQ |
Profile identity, decoded and then dropped when the packet is copied to the retained snapshot:
| Data id | Size | Type | Destination |
|---|---|---|---|
0x03f2 | 4 | u32 | Profile id. Dropped at the snapshot copy. |
0x03f3 | 8 | f64 | Profile id quality. Dropped at the snapshot copy. |
Identifiers named in the module’s identifier table but not decoded by the parser at this revision, so they
are skipped as if unknown: eyeglasses status 0x00c1, per-eye blink closing and opening mid-time,
amplitude and speed 0x00e0 to 0x00eb, left and right eyelid state 0x0390 and 0x0391, keypoints
found 0x0510, camera-drift heading, pitch and roll 0x0512 to 0x0514.
4.6 Sample validity handling
There is no sample-validity concept beyond structural checks. Concretely:
- Presence is per identifier, per packet. Any identifier SmartEye omits leaves the corresponding value unset for that packet.
- An entry whose
dataLengthdoes not exactly equal the expected size for its identifier is skipped. It is treated identically to an unknown identifier: no value is taken, nothing is logged. - Quality values are carried through as numbers. The module never thresholds, gates or rejects a
measurement based on its quality value, on the tracking-state code
0x00c0, or on the occlusion flags. A gaze direction with a quality of zero is published exactly like one with a quality of one. - Value ranges are not validated. A decoded double is cast to
floatfor publication with no range or finiteness check, so a non-finite or out-of-range value from the sender is published as such. - A packet is accepted whole or not at all at the envelope level, but partially at the entry level: see the truncated-entry row in 4.7.
4.7 Error behaviour
| Condition | Behaviour |
|---|---|
| Datagram shorter than 4 bytes | Datagram discarded, no event raised, nothing logged |
packetType not equal to 4 | Datagram discarded, no event raised, nothing logged |
Datagram shorter than 4 + length | Datagram discarded, no event raised, nothing logged |
Unknown dataId | Entry skipped, parsing continues with the next entry, nothing logged |
Known dataId with an unexpected dataLength | Entry skipped, parsing continues with the next entry, nothing logged |
Entry dataLength exceeding the remaining payload | Entry loop stops. The packet is still delivered with whatever was decoded before the truncation. Nothing is logged. |
| Fewer than 4 bytes left in the payload | Entry loop ends, remaining bytes ignored |
| Type-4 datagram containing no decodable entry | An all-unset packet is delivered and replaces the retained snapshot. Nothing is logged. This is not distinguished from a real measurement in which SmartEye happened to send nothing. |
| Socket closed during shutdown | The SocketException is swallowed while the running flag is clear, the loop exits, “SmartEye UDP receiver stopped” is logged |
| Any other exception in the receive loop | Logged as a warning “SmartEye UDP receive error”, the loop continues |
| Port already bound at capture start | The socket constructor throws inside StartCapture, the SDK logs “SmartEye Module StartCapture failed.” and the module does not start |
| Exception thrown while building or publishing a frame on the fixed-rate path | The SDK logs “Error in module” and the capture loop continues |
| Exception thrown while building or publishing a frame on the native-rate path | Caught by the receive loop, logged as “SmartEye UDP receive error”, the loop continues |
No condition in this table produces a retry, a counter, or a state change visible to HEAT Capture. A malformed or hostile stream degrades silently.
4.8 Timing and cadence
The module imposes no rate on the sender. The publication cadence depends on SendAtNativeRate:
SendAtNativeRate | Cadence | Path |
|---|---|---|
true, the default | One frame per accepted type-4 datagram, at whatever rate SmartEye sends | Frame is built and published on the SmartEye-UDP receive thread. HandleCapture returns null every cycle, so the SDK interval publishes nothing. |
false | One frame per SDK Interval, 1000 ms by default | HandleCapture builds a frame from the retained snapshot. Datagrams arriving between cycles update the snapshot and are otherwise discarded. |
On the native-rate path the frame build and the publish run inside the receive loop, between two
Receive calls, so publication cost is back-pressure on datagram reception rather than on the capture
loop.
Neither path uses the SmartEye frame number 0x0001 or timestamp 0x0003 for ordering or
de-duplication. On the native-rate path a duplicate or out-of-order datagram is published as a new frame.
5. IF-CMSMARTEYE-02 Frame publication and data model reference
5.1 Entity
| Item | Value |
|---|---|
| Entity name | SmartEye_Data |
| Entity id | The hash code of a freshly generated GUID, masked to the low 31 bits. Computed once per module instance, so it is arbitrary and not stable across restarts of HEAT Capture. It is not derived from the entity name and carries nothing SmartEye reported. |
| Components | GazeComponent, PupillometryDataComponent, SpatialComponent, BiometricSensorComponent, all four attached on every frame |
| Capture time | UTC Unix milliseconds, taken when the frame is built |
| Entities per frame | Exactly one |
All four components are attached unconditionally, whether or not the snapshot contained anything for
them. An empty SpatialComponent or a BiometricSensorComponent with no readings is still present in
the frame.
5.2 Head pose
SpatialComponent.pose is emitted only when head position 0x0010 was present. Orientation is resolved
in this order:
- The quaternion
0x001d, used directly. - Otherwise the Rodrigues vector
0x0012, converted to a unit quaternion by the module. A vector whose magnitude is below1e-10yields the identity quaternion. - Otherwise the identity quaternion
(0, 0, 0, 1).
Case 3 means a frame can carry an authoritative-looking neutral head orientation that reflects only the absence of rotation data. There is no published flag distinguishing it from a measured neutral pose.
5.3 Fields never populated
GazeComponent.IPD, GazeComponent.gaze_point and GazeComponent.gaze_screen_point are never set by
this module: no SmartEye identifier is mapped to them. PupillometryDataComponent.other_pupil_data is
never populated; the drowsiness, emotion and activity values go to BiometricSensorComponent instead.
SpatialComponent fields other than pose are never set.
No EntityEvent is ever published. Saccade 0x003d and fixation 0x003e are decoded and retained but
are not turned into events or published fields.
5.4 Absence semantics per field type
Whether a consumer can distinguish “SmartEye did not send this” from “SmartEye sent zero” depends on the proto field type, not on anything the module does:
| Published field group | Absence is distinguishable |
|---|---|
Float3 gaze origins, directions and eye positions, and SpatialComponent.pose | Yes. The message is left unset. |
optional quality floats, left_eye_occluded, right_eye_occluded, has_glasses | Yes. The field is left unset. |
blink | No. It is a plain bool and reads false when 0x003f was absent. |
eyelid_openness, left_eyelid_openness, right_eyelid_openness, pupil_diameter, left_eye_pupil_diameter, right_eye_pupil_diameter | No. They are plain floats and read 0 when the identifier was absent. |
BiometricSensorComponent readings | Yes. A reading is added only when its identifier was present, so an absent value has no entry. |
5.5 Publication before a current packet
The retained snapshot is created empty when the module instance is constructed, is not cleared by
StartCapture, and is not cleared by EndCapture. On the fixed-rate path a frame is built from it on
every cycle regardless. This is audit finding SE-01, and it is a property of this revision, not of
the SmartEye protocol.
Two consequences follow. First, before any packet arrives, the seed snapshot has every field unset and the module publishes this:
| Published item | Seed value |
|---|---|
All Float3 gaze origins, directions and eye positions | Unset |
| All quality values | Unset |
left_eye_occluded, right_eye_occluded, has_glasses | Unset |
blink | false |
eyelid_openness, left_eyelid_openness, right_eyelid_openness | 0 |
pupil_diameter, left_eye_pupil_diameter, right_eye_pupil_diameter | 0 |
SpatialComponent.pose | Unset, because head position is unset |
BiometricSensorComponent | Present with zero readings |
A consumer therefore cannot distinguish “no SmartEye data yet” from a real measurement of fully closed eyelids and zero pupil diameter with no blink.
Second, and more consequential, the snapshot outlives a capture session. Because the module instance is initialised once and reused across starts and stops, the last packet of one session remains in the snapshot and is published as the first frames of the next session, timestamped with the new session’s capture time. An earlier subject’s gaze, pupil, head-pose, drowsiness and emotion values can be attributed to a new subject.
Do not treat an early fixed-rate frame as evidence of a live tracker. Correlate with a changing value, or run the module on its default native-rate path, where a frame is only built from an immediately received packet. Native rate does not fix SE-01; it avoids the code path on which SE-01 manifests.
6. Units and conventions
| Concept | Convention |
|---|---|
| Positions and origins | As reported by SmartEye, in SmartEye’s own world coordinate system and units. The module applies no transform, scaling or axis remapping. |
| Directions | As reported by SmartEye. Not renormalised. |
| Head orientation | Unit quaternion in X, Y, Z, W order. Either SmartEye’s own quaternion, or a module conversion of its Rodrigues vector, in which magnitude is the rotation angle in radians and direction is the rotation axis. |
| Pupil diameter and eyelid openness | As reported by SmartEye. No unit conversion. |
| Quality values | As reported by SmartEye. The proto comments describe quality as a 0..1 measure, but the module neither checks nor rescales the value. |
| Enumerated codes | DominantEmotion, Drowsiness9Level, Drowsiness9LevelStatus and the tracking-state code are numeric codes published unmapped. The module does not decode which number means what. |
| Booleans | Any non-zero byte is true. |
| Numeric precision | SmartEye sends 64-bit doubles. Every value is cast to 32-bit float for publication, so precision is reduced. |
| Time base | Host wall clock, UTC Unix milliseconds, applied when the frame is built and again by the SDK when the packet is wrapped. The SmartEye timestamp 0x0003 and frame number 0x0001 are decoded but never published, so frames cannot be aligned to SmartEye’s own clock. |
The authority for coordinate-system handedness, origin, axis directions and units is SmartEye’s own documentation. The module does not define them and does not assert them.
7. Configuration affecting the interface
| Setting | Default | Interface effect |
|---|---|---|
UdpPort | 5003 | The local port the socket binds to. Read once at capture start, so a change takes effect only on the next capture session. |
SendAtNativeRate | true | Selects the publication path in 4.8. Read on every packet and on every capture cycle, so a change takes effect immediately, mid-session. |
Enabled | true | Whether the module runs at all |
Interval | 1000 ms | Publication cadence on the fixed-rate path only. Has no effect while SendAtNativeRate is true. |
OverrideNodeId, NodeId | false, 0 | Node attribution of the published packet |
There is no setting for the listening interface, for a sender allow-list, for which identifier groups to publish, or for a quality threshold. Emotion and drowsiness readings cannot be turned off independently of gaze.
See Manage capture modules for the generic settings and the Module SDK ICD for the lifecycle contract.
8. Compatibility and versioning
There is no version negotiation and no version field in the envelope. Compatibility rests on three
assumptions: that the sender uses packet type 4, that identifier numbers keep their meaning, and that
each identifier keeps its wire size.
A SmartEye release that changes an identifier’s wire size causes that field to be dropped silently, because the size guard fails and the entry is treated as unknown. A release that reassigns an identifier number to a different quantity causes that quantity to be published under the old field or reading name, with no diagnostic. A release that adds identifiers is tolerated: unknown identifiers are skipped and the rest of the packet still parses.
The identifier subset listed as undecoded at the end of 4.5 is a property of this revision. Enabling those outputs in SmartEye does not make them appear in HEAT Capture.
9. Security considerations
The listening socket is unauthenticated and accepts datagrams from any source address on any interface of the workstation, so any host that can route a UDP packet to the bound port can inject a measurement, and any local process can do the same. Nothing in the module distinguishes an injected packet from a genuine one. Restrict the port at the host firewall to the address of the machine running SmartEye.
The stream itself is unencrypted. Any device on the path can read it, and the module cannot detect tampering.
The payload is personal data. Gaze direction and origin, eye position, pupil diameter, eyelid openness, blink, head pose, glasses and face-mask detection, drowsiness level, facial-emotion estimates and a speaking flag are all measurements of an identifiable person’s body and inferred internal state. The profile identifier SmartEye can send is decoded, though this revision discards it before publication. Gaze and pupil data support inferences beyond the stated purpose of a session, including attention, cognitive load and, in combination with the emotion readings, affective state. Treat captured SmartEye sessions accordingly: restrict who can read them, apply the deployment’s retention limits, and obtain the consent the deployment’s jurisdiction requires before recording. There is no setting that reduces the published set, so consent must cover everything in 4.5.
10. Conformance
- Configure SmartEye to send its output to the workstation’s address on the configured port. Start a capture session and confirm the log records “SmartEye Module starting capture on UDP port 5003” and “SmartEye UDP receiver listening on port 5003”.
- Confirm one entity named
SmartEye_Dataappears in the captured data, carrying all four components. - With a subject tracked, confirm
gaze_directionandpupil_diameterchange across successive frames. - Confirm the filtered-over-raw preference. Disable SmartEye’s filtered gaze output and confirm
gaze_directionstill populates from the raw identifier0x0021. - Confirm the byte order end to end by checking that a published pupil diameter is a plausible millimetre-scale number rather than a value of extreme magnitude, which is what a byte-order mismatch would produce.
- Verify packet-type filtering. Confirm that enabling another SmartEye packet type produces no additional data and no log entry.
- Verify SE-01 on the fixed-rate path. Set
SendAtNativeRatetofalse, start a session with SmartEye stopped, and confirm the frames carry the seed values in 5.5, in particularpupil_diameterof0andblinkoffalsewith no pose. - Verify the cross-session case. With
SendAtNativeRateset tofalse, run a session with a tracked subject, stop it, stop SmartEye, then start a second session and confirm whether the first frames repeat the previous subject’s last measurement. Until SE-01 is fixed, discard the frames preceding the first packet of each session. - Stop SmartEye mid-session and confirm the module logs nothing and publishes no error. On the native-rate path, confirm frame publication stops.
- Confirm the entity id is not relied on across restarts: restart HEAT Capture and confirm the id for
SmartEye_Datadiffers while the name does not.
11. Revision history
| Rev | Date | Product version | Change |
|---|---|---|---|
| 1 | 2026-09-02 | f44f1ac | Initial issue. |