Latency Measurement

Last validated: 2026-05-04

Oden Control Pipeline (OCP) reports several latency-related values. Use this page to verify that the latency loop is closed, understand what the operator should see, and decide whether a fault points at networking, video metadata, the vehicle-side TCP integration, or an operator-side client.

The important distinction is that the operator-side numbers are health indicators, while the High Latency fault is raised by the Streamer-side timestamp loop.

Value Where you see it What it means

ping_latency_ms

ocp_vehicle_user_data.vehicle_feedback.<vehicle>.ping_latency_ms and the OCP operator GUI as Ping Latency

A lightweight Oden com-channel ping from operator to vehicle and back. It is useful for network health, but it is not the video latency loop.

message_latency_roundtrip

ocp_vehicle_user_data.vehicle_feedback.<vehicle>.message_latency_roundtrip and the OCP operator GUI as Operator Latency

An operator-control-message timestamp returned by the Streamer feedback message. It shows that OCP control and feedback messages are flowing. It can be null before feedback arrives and can look stale when feedback is lost.

OCP timestamp-loop latency

Vehicle-side telemetry.latency, plus receiver_fault values such as High Latency or Latency Loop Not Closed

The Streamer-side measurement used for the High Latency fault. It follows the vehicle TCP response, video metadata path, operator echo, and return control message.

How the timestamp loop works

The latency loop does not require synchronized clocks between the vehicle computer and the operator computer. OCP uses a Streamer-side elapsed-time clock and protects the timestamp with MAC values.

Streamer OCP elapsed clock
  -> OcpControlMessage over vehicle TCP
  -> vehicle integration copies ack fields into VehicleResponseMessage
  -> Streamer embeds ack fields as video frame metadata
  -> Player extracts metadata from the Remote Streamer
  -> operator-side OCP or client returns latest ack fields
  -> control message reaches Streamer
  -> Streamer validates MAC and computes elapsed_ms - returned_ack_time

The external values you must preserve are:

ack_time

A u64 elapsed-milliseconds timestamp generated by Streamer-side OCP.

ack_time_mac

A u32 MAC for ack_time. OCP randomizes the Streamer-side key on startup.

Do not edit, reinterpret, scale, round, or regenerate either field. Operator-side TCP clients and plugins should copy ack_time to ack_time_returned and ack_time_mac to ack_time_mac_returned. The OCP operator sender performs the internal return MAC calculation before sending the control message back to the Streamer.

Message paths and ports

Path Default Latency role

Vehicle-side TCP

127.0.0.1:4000

Streamer OCP sends OcpControlMessage to the vehicle integration. The integration returns VehicleResponseMessage with ack_time and ack_time_mac.

Operator-side TCP

127.0.0.1:4001

Optional external operator client receives VehicleOcpShared feedback and sends ClientOcpShared data. If used, it must return the latest timestamp fields.

Webview messages

ocp_vehicle_user_data and ocp_client_user_data

Webviews receive vehicle feedback and can send operator-side user data. The injected OdenLayoutClient automatically echoes timestamps.

Plugin shared data

vehicle_ocp and client_ocp

Custom operator-side plugins read feedback and publish client data. Plugins must copy the timestamp fields manually.

Oden com-channel messages

CONTROL_COMMAND_ID, FEEDBACK_MESSAGE_ID, PING_MESSAGE_ID, PONG_MESSAGE_ID

Internal OCP transport for control, feedback, and ping measurements.

Video metadata

Streamer frame metadata / SEI

Carries the returned vehicle-side ack_time and ack_time_mac from Streamer video output to the operator-side Remote Streamer.

Both TCP APIs use a 4-byte little-endian length prefix followed by UTF-8 JSON. Messages larger than 16 KB are dropped. By default, TCP servers bind only to localhost. Set ocp_tcp_allow_remote only when a TCP client must connect from another host, and protect that port at the network boundary.

Configure the measurement

OCP plugin parameters are passed at startup with --plugin-param.

oden-streamer --plugin-param latency_limit 750
Parameter Default Use

tcp_port

4000

Vehicle-side TCP server port on the Streamer.

ocp_player_tcp_port

4001

Operator-side TCP server port on OdenVR / Player.

ocp_tcp_allow_remote

unset

Binds OCP TCP servers to 0.0.0.0 instead of 127.0.0.1.

latency_limit

500

Threshold in milliseconds for the Streamer-side High Latency fault. Increase it only when the measured end-to-end control path is acceptable for the vehicle.

These are the OCP settings that usually matter for latency measurement. For the overall OCP model, see Oden Control Pipeline overview.

Validate a basic setup

Use this flow when OCP should handle gamepad input and no custom operator-side TCP or plugin client is involved.

  1. Enable the OCP global plugin in Oden Streamer and OdenVR / Player.

  2. Start the Streamer output and connect the Player so video is visible.

  3. Start the vehicle integration and connect it to the Streamer TCP server, normally 127.0.0.1:4000.

  4. In every vehicle response, copy ack_time and ack_time_mac from the latest OcpControlMessage.

    {
      "ack_time": 123456,
      "ack_time_mac": 987654,
      "vehicle_user_data": {
        "user_data": {}
      }
    }
  5. On the operator station, open the OCP plugin GUI or read ocp_vehicle_user_data.

  6. Confirm that ping_latency_ms is finite, message_latency_roundtrip becomes non-null, ack_time changes over time, and receiver_fault does not contain High Latency or Latency Loop Not Closed.

With no external client data source, OCP extracts the timestamp from video metadata and echoes it back internally. No JavaScript, operator plugin, or player-side TCP client is required.

Add operator-side data safely

Only one operator-side client data source may send OCP client data in a session. The first source that sends data becomes the locked source. If OCP later receives client data from another source, it logs an error, stops all client data communication, and requires a restart.

Webview

Use sendNamedUserMessage("ocp_client_user_data", payload). The injected OdenLayoutClient stores the latest ack_time and ack_time_mac from ocp_vehicle_user_data and injects them into each matching client_user_data entry. If no custom JavaScript sends OCP client data, the webview client auto-echoes timestamps with user_data: null.

Player-side TCP

Read ack_time and ack_time_mac from VehicleOcpShared.vehicle_feedback. Return them as ack_time_returned and ack_time_mac_returned in the matching vehicle entry.

Custom operator plugin

Read vehicle_ocp shared data. Publish client_ocp shared data with ack_time_returned, ack_time_mac_returned, optional user_data, and optional ocp_disable_gamepad.

For a TCP or plugin client, send the latest timestamp for the same vehicle name that received it:

{
  "active_vehicle": "vehicle_a",
  "client_user_data": {
    "vehicle_a": {
      "user_data": {
        "mode": "work"
      },
      "ocp_disable_gamepad": false,
      "ack_time_returned": 123456,
      "ack_time_mac_returned": 987654
    }
  }
}

In multi-vehicle sessions, return timestamp fields for each vehicle you include in client_user_data. Set active_vehicle to the vehicle that should receive OCP gamepad input.

Read operator feedback

A webview can display the values the operator needs with the Oden JavaScript SDK:

const client = getOrCreateOdenLayoutClient();

client.registerUserMessageCallback("ocp_vehicle_user_data", (payload) => {
  for (const [vehicleName, feedback] of Object.entries(payload.vehicle_feedback ?? {})) {
    console.log(vehicleName, {
      ping: feedback.ping_latency_ms,
      messageRoundtrip: feedback.message_latency_roundtrip,
      ackTime: feedback.ack_time,
      senderFaults: feedback.sender_fault,
      receiverFaults: feedback.receiver_fault,
    });
  }
});

For operator UIs, show both fault arrays and at least these values:

  • ping_latency_ms for network health.

  • message_latency_roundtrip for OCP control/feedback message health.

  • receiver_fault for Streamer-side faults, especially High Latency and Latency Loop Not Closed.

  • sender_fault for operator-side faults, especially Input Lost, No Feedback Data, and Vehicle Control Disabled.

  • ack_time when diagnosing whether video metadata is arriving.

Do not treat ping_latency_ms or message_latency_roundtrip as the same value that raises High Latency. The high-latency decision is made on the Streamer from the validated ack_time loop.

OCP GUI field guide

The OCP plugin GUI is available from the engineering sidebar when the plugin is enabled.

On OdenVR / Player:

Operator Latency

Round-trip time from operator to vehicle and back for OCP message feedback. It may be stale when feedback is lost.

Ping Latency

Lightweight network ping from operator to vehicle and back.

Last Timestamp

Last Streamer-side ack_time received from video metadata.

Controller

Last local controller input.

Vehicle Data

Latest vehicle-side user data repeated from feedback.

Missing Cameras

Cameras whose drop detector reports missing frames. An unnamed 2D video appears as Unknown Camera; inputs with drop detection disabled are ignored.

Faults

Active OCP faults for the operator and vehicle path.

Client Data

Operator-side data being sent into OCP and forwarded to the Streamer side.

On Oden Streamer:

Ack Time

Timestamp values OCP adds to outgoing frames for latency measurement.

Faults

Active receiver faults on the Streamer side.

Controller Commands

Last control data sent over the vehicle-side TCP connection, including fault state.

Vehicle Userdata

Last vehicle data received from the vehicle-side TCP client.

Troubleshoot symptoms

Symptom What to check

Vehicle Not Responding

No vehicle-side TCP client is connected to the Streamer OCP server. Start the vehicle integration, verify the port, and remember that the default bind address is 127.0.0.1.

No Control Messages

Streamer OCP has not received operator control messages for more than 1000 ms. Check the Player connection, Remote Streamer mapping, Fleet active vehicle state, and OCP plugin state on both sides.

No Feedback Data

Operator OCP is not receiving Streamer feedback messages. Check the video/control connection first, then check Streamer-side OCP faults and logs.

Camera Lost

Streamer OCP found a monitored camera whose valid-frame timestamp is too old. Check the camera input, the Output Alignment children, and the camera drop detector. Cameras with drop detection disabled are not reported in this list.

Streamer Slow

The Streamer frame loop is taking too long. Check CPU/GPU load, encoder load, blocking plugins, and whether the Streamer is overloaded.

Input Lost

No gamepad input is available while OCP expects to forward gamepad input. Connect a gamepad or set ocp_disable_gamepad: true from the single operator-side client that provides its own controls.

Vehicle Control Disabled

This vehicle is not the active control target. In multi-vehicle sessions, send active_vehicle. It is normal for connected but non-active vehicles to report this.

Latency Loop Not Closed

Timestamp validation failed. Usually ack_time or ack_time_mac was missing, modified, returned for the wrong vehicle, or not copied through an operator-side TCP/plugin client. Also check that a webview, TCP client, and plugin are not all sending OCP client data in the same session.

High Latency

The timestamp loop is valid, but the latest returned timestamp is older than latency_limit. Check video transport latency, network congestion, Player decode/display latency, vehicle integration delay, and how often the operator-side client returns fresh timestamps.

Operator Station Has Error

The operator side reported a sender fault to the Streamer. Read the same vehicle’s sender_fault array and fix Input Lost, No Feedback Data, or Vehicle Control Disabled first.

ack_time stays 0 or does not change

The operator is not receiving valid video metadata. Confirm the vehicle integration returns ack_time and ack_time_mac, Streamer output is running, the Player receives the Remote Streamer video, and metadata extraction is enabled for the receive path. If another plugin writes Streamer frame metadata in the same frame, OCP can fail to set its timestamp metadata and will log that failure.

ping_latency_ms is missing, huge, or unstable

The Oden com-channel ping/pong path is unhealthy. Check Remote Streamer connection state, packet loss, route, firewall, relay path, and whether the vehicle is still connected.

Client data stops after it was working

Look for an OCP GUI error saying client data was received from another source. Restart Oden after removing the duplicate source. An open webview can auto-echo timestamps and become the webview source.

Internal Error (1)

OCP’s internal control-thread queue is delayed. Check CPU load, Streamer frame time, blocking plugin work, and application logs.

Delivery checklist

Before handing over an OCP integration:

  • Vehicle TCP connects on the intended tcp_port and reconnects cleanly.

  • Every VehicleResponseMessage copies ack_time and ack_time_mac unmodified.

  • Only one operator-side client data source sends ocp_client_user_data or client_ocp.

  • Multi-vehicle operator data uses vehicle names consistently and sets active_vehicle.

  • Operator UI shows ping_latency_ms, message_latency_roundtrip, sender_fault, and receiver_fault.

  • High Latency and Latency Loop Not Closed are absent during a realistic driving or operating test.