跳到主要内容

Timestamps & Unix time

Every message in an EGO1 recording is timestamped, but the timestamps are not Unix time by default — they run on the device's monotonic clock (nanoseconds since the device booted). This page explains the format and shows how to convert to Unix (wall-clock) time.

The two clocks

ClockWhat it isWhere you see it
Monotonic (boot)Nanoseconds since the device powered on. Never jumps.Every message timestamp, and the MCAP framing
Realtime (Unix)Seconds + nanoseconds since 1970 (wall-clock).Only inside /ego/system_health messages

A fresh recording typically starts at a monotonic value of a few hundred seconds (time since boot), not the ~1.7 billion seconds you'd expect from a Unix timestamp — that's the tell-tale sign you're looking at the monotonic clock.

Where the timestamp lives

Both the MCAP framing (log_time / publish_time) and the message payload carry the same monotonic value in nanoseconds. In the payload it's a { seconds, nanos } pair (combine as seconds * 1e9 + nanos):

TopicTimestamp fieldNotes
/ego/camera/0, 1timestampPer video frame
/ego/imu/0/quattimestampFused orientation sample
/ego/imu/0/rawfirst_sample_timeTime of the first batched IMU sample
/ego/system_healthtimestampPlus realtime — see below
Underlying resolution is microseconds

Sensor timestamps come from a microsecond clock, so the nanosecond values end in 000. Only host-generated messages carry full-nanosecond precision. This doesn't affect the conversion below.

Converting to Unix time

/ego/system_health messages (emitted at ~1 Hz) carry both clocks at once: the monotonic timestamp and the Unix-epoch realtime. The difference between them is a fixed offset that maps one clock onto the other:

offset = realtime_ns − monotonic_ns (constant across the recording)
unix_ns = any_monotonic_ns + offset

So the recipe is: read one /ego/system_health message, compute the offset once, then add it to any other message's timestamp.

from visio_schema import read_mcap

def to_ns(t): # a { seconds, nanos } timestamp -> nanoseconds
return t.seconds * 1_000_000_000 + t.nanos

# 1. Find the monotonic -> Unix offset from any system_health message.
offset_ns = None
for msg, channel in read_mcap("capture.mcap"):
if channel.topic == "/ego/system_health":
offset_ns = to_ns(msg.realtime) - to_ns(msg.timestamp)
break

# 2. Apply it to any message timestamp.
for msg, channel in read_mcap("capture.mcap"):
if channel.topic == "/ego/imu/0/quat":
mono_ns = to_ns(msg.timestamp)
unix_ns = mono_ns + offset_ns
unix_s = unix_ns / 1e9
print(unix_s) # e.g. 1782802423.004 (a Unix timestamp)
break

To turn a Unix timestamp into a human-readable UTC time:

from datetime import datetime, timezone

print(datetime.fromtimestamp(unix_s, tz=timezone.utc).isoformat())
# 2026-06-30T06:53:43.004+00:00
Average a few for sub-microsecond accuracy

The offset is stable to within a couple of microseconds. If you want to cancel that jitter, average the offset over several /ego/system_health messages instead of using just the first one.

Caveats

  • Wall-clock accuracy is only as good as the device's clock. EGO1 has no battery-backed real-time clock, so its realtime reflects whatever time it was given (or an unset clock). Absolute timestamps may be off by minutes, hours, or more if the clock was never synced — but timing within a recording (frame pacing, IMU intervals, camera-to-IMU alignment) is always accurate, because it all rides the same monotonic clock.
  • /device_info is not an anchor. Its boot_unix_seconds field is 0 when the clock wasn't synced at boot — use the /ego/system_health realtime pairs instead.
  • No system_health, no conversion. If a recording contains no /ego/system_health messages there's no in-file link to Unix time, and you can only work in boot-relative time.

For the full list of topics and schemas, see Data streams and the channel & network reference.