Repository navigation
egocap, sensing: align camera, glove and controller time; show latency and fps - #1437
Open
gareth-morgan-nv wants to merge 57 commits into
Open
gareth-morgan-nv wants to merge 57 commits into
gareth-morgan-nv wants to merge 57 commits into
Conversation
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
gareth-morgan-nv
had a problem deploying
to
dev
November 5, 2026 22:31 — with
GitHub Actions
Failure
gareth-morgan-nv
force-pushed
the
gmorgan/Argus
branch
from
November 6, 2026 01:42
71b6bbf to
2c38aba
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/TimestampAlignment
branch
from
November 6, 2026 01:42
ba2d1d7 to
b7d8f99
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/Argus
branch
from
November 6, 2026 05:39
2c38aba to
27dad9c
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/TimestampAlignment
branch
from
November 6, 2026 05:39
b7d8f99 to
be24085
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/Argus
branch
from
November 6, 2026 05:42
27dad9c to
2602fd7
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/TimestampAlignment
branch
from
November 6, 2026 05:42
be24085 to
fbdc7e3
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/Argus
branch
from
November 6, 2026 18:04
2602fd7 to
74384ee
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/TimestampAlignment
branch
from
November 6, 2026 18:04
c6f0635 to
d9d408f
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/Argus
branch
from
November 6, 2026 20:31
74384ee to
32272f2
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/TimestampAlignment
branch
from
November 6, 2026 20:31
d9d408f to
0dc1c08
Compare
UnixClockMap keeps the bracket uncertainty of its pair and flags a change in the offset between measurements that exceeds the larger of 5 ms and four times that uncertainty. The recording feed then fails the take instead of recording frames with a guessed mapping; frames already mapped are never remapped. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…sh delay), shared by all tracks Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…hetic camera/tracking timeline Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
The rate is the change in the browser's cumulative presented-frame count over a one-second window, not a tally of callbacks; a hidden tab shows paused, a stalled stream 0 FPS, and a browser without frame callbacks 'FPS unavailable'. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Each camera stamp is truth + offset + elapsed_s * drift_ppm over a 60 s span, and the recorded residual is asserted per frame and to increase. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…mp and a four-timestamp clock mapping The preview now asks livekit-client for the per-frame packet-trailer metadata (worker option), runs a four-timestamp exchange with the publisher over the room's data channel (viewer tokens may publish data, never media), and each tile matches the displayed frame's RTP timestamp to the publisher's capture stamp, maps the frame's expected display time onto the publisher's clock, and shows the window's median, 95th percentile and the mapping's uncertainty. A frame with no stamp, a stream whose basis is not a capture event on the publisher's monotonic clock, a stale mapping, or a window spanning a mapping discontinuity has no figure. Tests follow. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…e recording tests to the four-timestamp path A mapping is stale when no exchange has arrived within the limit, not when the best exchange in the window is old, which made a healthy jittery link go stale (Python and browser estimators). The recording, source, app, receiver, loader and emulator tests move to the monotonic capture basis and the clock mapping, UnixClockMap's tests are removed, and the synthetic timeline is one shared event timeline for a camera and a glove-like source, each with its own clock and delays, mapped by the real estimator. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
The tile's two labels are the displayed FPS (frames presented over the one-second window) and the mean capture-to-display latency of the window in ms; '– FPS' and '– ms' stand in when a number is not available. The uncertainty and 95th percentile move into the tooltip. Tests for the newest-exchange staleness fix are included; the tile tests follow. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…rowser latency path Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
… clock mapping The displayed rate now divides the change in presented frames by the presentation times of the two frames that bound the window, and a counter reset becomes the new baseline. The browser clock sync assumes a conservative 1 ms timer resolution instead of spinning the UI thread to measure it. Both estimators pick the exchange with the smallest uncertainty now, half its round trip plus the drift its age allows, and report that uncertainty, so a low-round-trip exchange stops being preferred once it is old. Tests follow. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…nd the tracking host-poll boundary Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
The LiveKit session parses four-timestamp requests (topic isaaccapture.clock-sync, 13 bytes) in onUserPacketReceived, stamps t2 first on CLOCK_MONOTONIC, and a small responder thread sends the 29-byte reply to the requester only, unreliable, with t3 written just before the send. The queue is bounded; anything else is ignored. The wire format is in clock_sync.hpp. The frames still declare processing time. Syntax-checked against the pinned SDK v1.12.4 headers here; not built or run on a Jetson. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
… the Python side Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…utes The recording service takes timing evidence for the active episode (set_episode_timing, in memory, written under 'timing' in the kept episode's attributes; a take recovered after a crash has none). The coordinator gathers it from the LiveKit source before the room is left: the method, the clock names, each stream's timestamp basis, the mappings adopted (epoch, offset, round trip, uncertainty, time accepted; the newest 64), whether a discontinuity occurred, rejected exchanges, and that tracking time is the host poll time. The estimator's history is bounded. Tests follow. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…th tests The estimator re-reports one exchange with a growing age allowance while no newer exchange arrives; the take's evidence now keeps the first report of each exchange. Adds tests for set_episode_timing, timing_evidence(), the coordinator wiring and the bounded estimator history. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…e the encoder's returned stamps --timestamp-basis=capture_monotonic_us converts the backend's sensor timestamp (SIPL capture counter as generic-counter ticks, Argus sensor timestamp as CLOCK_MONOTONIC_RAW ns) through a low-rate anchor pair and fails the stream on an implausible conversion; the default stays processing time. The units are assumptions checked per frame, not verified on hardware. SENSING_ENCODER_TIMESTAMP_PROBE=1 logs returned unit stamps beside the last submitted one. Host tests cover the mapping. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
EGOCAP_CLOCK_UNCERTAINTY_BUDGET_MS (default 5 ms, a policy choice) bounds the mapping uncertainty a take may be recorded with; the budget is saved in the take's timing evidence. Docs describe the native capture-time option and the remaining open items. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…racking times mean ClockEstimator.mapping(now) grows the uncertainty with the age of the exchange, so the recording budget cannot pass on a figure from before a reply gap. The take's timing evidence names what each tracking source's time is (host poll; controller pose-evaluation time; Manus host time at push) and that physical alignment is unavailable. The native capture_monotonic_us option is described as unverified. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…and aging tests The browser estimator recomputes the mapping's uncertainty from the exchange's age, as the PC estimator does. New tests: a camera, glove-like and controller-like source with independent clocks and delays through real video and numeric ingestion into a saved take, with drop and clock-jump cases; a feed that fails once aging pushes the mapping over budget. Existing estimator tests now expect the aged figure. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Each SDK callback reads CLOCK_MONOTONIC on entry; the skeleton and raw-sensor samples keep that receipt time with the cached data and are injected or pushed with it, once. The SDK publishTime stays unused: its sampling event is not specified by the vendor. Not built or run here (Manus SDK and OpenXR runtime needed). Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…ime" This reverts commit 5dac9eb. Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
… default The native sender publishes its monotonic frame-ready time (monotonic_us) by default; recording accepts that basis alongside the sensor-capture basis, the take's timing evidence names which, and the page computes latency from either. The agent-only plan notes move to the git-ignored agentPlans/ folder. Tests extend the saved-artifact timeline fixture over both bases. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
… as the code now has them Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
… one docstring line The ingress and readiness tests built streams on a basis that is no longer recordable, so no frame counted toward the budget. Use the sensor-capture monotonic basis, which is. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
… no longer exists FrameMeta lost stamp_us when the application timestamp moved to unix_us; the frame's own timestamp is the unit's. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
The effect runs once per track but the clock arrives after it, so it closed over a null clock and never showed latency. It now reads both through refs. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
…pping Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Signed-off-by: Gareth Morgan <[email protected]>
gareth-morgan-nv
force-pushed
the
gmorgan/Argus
branch
from
November 6, 2026 22:37
32272f2 to
e28d3bc
Compare
gareth-morgan-nv
force-pushed
the
gmorgan/TimestampAlignment
branch
from
November 6, 2026 22:37
0dc1c08 to
30c47c3
Compare
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Puts camera video, glove and controller data on one PC timeline for recording, and shows end-to-end latency and fps on the page.
EGOCAP_CLOCK_UNCERTAINTY_BUDGET_MS, default 5 ms, a policy choice).monotonic_us(the publisher's frame-ready time, the native sender's default) orcapture_monotonic_us(sensor capture event). Tracking keeps the existing host poll timestamp. The take'stimingattributes save the mapping evidence, the budget, what each camera time means (frame_ready/sensor_capture) and what each tracking time means.camera_plugin_sensing). Answers the clock exchange;--timestamp-basisselectsmonotonic_us(default),capture_monotonic_us(converts the SIPL counter or Argus sensor timestamp through a 1 Hz anchor, failing the stream on an implausible value) orprocessing_unix_us.SENSING_ENCODER_TIMESTAMP_PROBE=1logs encoder returned stamps.agentPlans/is git-ignored).Limits
publishTime) are not carried into recordings; that needs changes outside egocap.Validation
Run (mock stack, no hardware):
pytestovertests/backend,tests/capture_server,tests/tools,tests/recording_service: 1181 passed, 100 skipped, 1 failed (tests/capture_server/test_gmsl_ipc.py::test_pipeline_serves_each_announced_frame_once_downscaled_as_jpeg, in code this PR does not touch; not checked against a clean checkout).vitest386 passed,tsc --noEmitclean.pre-commithooks (withSKIP=check-copyright-year) pass on the touched files; touched C++ was formatted withclang-format-14.Not run: the native sender and Argus/SIPL paths were not built against the SDKs, nothing ran on an Orin, the Manus/controller paths were not exercised, and the page was not verified in a real browser by the author's tooling. The sensor-counter conversion (
capture_monotonic_us) is unverified on hardware.Stacked on #1429 (Argus), which stacks on #1428 (RearchBackend) and #1426 (FastLiveKit).
🤖 Generated with Claude Code