Skip to content

τ must be measured more than once: a single reading can be one period wrong with nothing to detect it #347

Description

@mkovero

spec

type: bug

depends on: #340 (same layer, different mechanism — that one is the window ceiling, this one survives fixing it)

problem statement

Round-trip latency for a fixed path on this stack varies by exactly one period between client starts, with no configuration change of any kind. Measured on the rig 2026-08-18 (work/rig/rig-243-343-results.md, PR #345), jack_iodelay on an unchanged leg:

4262.064 frames
5286.064      ← +1024.000
5286.063
5286.064

The fractional part survives the jump untouched, which identifies it as an integer number of periods added in software rather than anything analogue. It is per client, not per graph: at one point jack_iodelay measured its own clients at +1024 while ac-daemon's workers, minutes apart on the same machine, sat at the unshifted value.

Why this layer cannot see it

TauConditions (shared/calibration.rs:128-138) keys on device, backend, sample rate, period size, output port and input port. Two τ readings a period apart share all six. Nothing changed about the configuration, so there is no mismatch for the refuse-on-mismatch logic to catch, and #340's fix does not help: a measure_tau that refuses a pinned peak and admits a 50 ms round trip will still happily store a value that is 10.67 ms wrong at 96 kHz.

#281's premise — "τ is a property of (device, backend, sample rate, period size, port pair)" — is not quite true here. It is also a property of which client measured it, and when.

Within-run consistency proves nothing. Each state is internally stable to 0.001 frames. A measure_tau that sampled its own peak a hundred times in one worker would get the same answer every time and still be a period out.

acceptance criteria

  • τ is measured more than once, in separate client lifetimes, and the calibration refuses rather than storing when the readings disagree. One reading is not a measurement of this quantity.
  • The period quantum is used as the prior it is: a disagreement of exactly period_size samples is reported as this — a graph-buffering shift — and not as noise or as hardware drift. A disagreement that is not a multiple of the period is a different fault and should say so.
  • The stored entry records how many readings agreed, so a reader can tell a corroborated τ from a single-shot one. An entry from before this lands must not be indistinguishable from one after.
  • Test: two synthetic readings one period_size apart are refused, with the period named in the message. A test that only checks "readings differ" would pass on noise and miss the point.
  • Whatever ac calibrate prints says which route produced τ and whether it was corroborated, per Interface latency (τ) captured during ac calibrate, with scoped validity and history #281's existing requirement that a reader a year later can tell.

stretch

  • If a majority of readings agree and one is a period out, that is recoverable rather than fatal — but only with the disagreement recorded. Do not silently take the mode.

out of scope

  • Fixing PipeWire. The instrument has to be correct on the stack it runs on.
  • transfer_stream, which is immune and shows why: both legs live in one client for one session, so an added period is common-mode and cancels. It held 378 samples across three sessions and 378/378/379/379/379 across five more while this was happening. Any τ scheme that composes two separately-measured absolutes inherits the problem; one that measures a difference within a single client does not — worth weighing before more is built on the absolute.

files likely affected

  • ac-rs/crates/ac-daemon/src/handlers/calibrate.rsmeasure_tau
  • ac-rs/crates/ac-core/src/shared/calibration.rs — the stored entry, if corroboration count joins it

needs architect review yes — it questions whether τ should be an absolute at all, which is #281's shape.

estimated complexity medium

Found while running #243. Related: #276 (settled decision 3 rests on the same false premise), #340.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triageTriage agent acted on itbugSomething isn't workingmeasurement-accuracyH1 estimator / THD floor / windowing / calibration correctnesssoftwareac Python/Rust codebasetier-1ac-core/measurement/ — QA runs the standards check

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions