A running mean cannot keep up with a pod that is closing

The period trace came back with no period - intervals scattered from 0 to
2.5s - so the twenty-step renormalisation is not the culprit and neither
is any other fixed cadence. That is a real answer, and it also exposed a
fault in the instrument that produced it.

The angular step varies six hundredfold across the samples, 0.12 mrad
with the pod at 119m against 77 mrad at 5.4m. Judging each frame against
a long running mean therefore reports the mean own lag as a lurch
whenever the pod is closing, which is exactly what the window matching
the reported symptom was doing: 26 lurches at 5.4m range, almost all of
them measurement artifact.

Compare each frame against the frame before it instead. Consecutive
frames of a smooth pass are nearly equal however fast the sweep, so the
ratio is immune to range, and a tick is specifically one frame that
barely moves followed by one that catches up - so time the stalls, not
the lurches.

Also read renderStepFraction directly, per drawn frame, which should have
been the first measurement taken. It is the number the interpolation
actually uses. Sweeping smoothly from 0 to 1 means interpolation is
working; pinned at 1 means the simulation is behind and every frame is
drawing the same latest step, which is stepping at the physics rate no
matter how clean the packets were. The camera station rasters the map and
the gauges as well as the world, so falling behind is entirely plausible
and would show here and nowhere else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-11 09:09:07 -05:00
co-authored by Claude Opus 5
parent 8d372ec067
commit 9c886efd8d
+63 -6
View File
@@ -1153,11 +1153,34 @@ void
static int interval_count = 0;
static int frames = 0;
static int lurches = 0;
static int stalls = 0;
static Scalar last_angle = 0.0f;
static Logical have_angle = False;
static Scalar fraction_sum = 0.0f;
static int pinned_high = 0;
static int pinned_low = 0;
Vector3D dir;
dir.Divide(to_pod, range);
++frames;
//
// How far through the physics step this frame is being
// drawn. This is the number the interpolation actually
// uses, so it is worth reading directly rather than
// inferring from the picture. It should sweep smoothly
// from 0 to 1 and wrap. Pinned at 1 means the
// simulation is behind and interpolation has degenerated
// to drawing the latest step over and over - which IS
// stepping, at the physics rate, however smooth the
// packets were.
//
Scalar fraction = myEntity->renderStepFraction;
fraction_sum += fraction;
if (fraction > 0.99f) { ++pinned_high; }
if (fraction < 0.01f) { ++pinned_low; }
if (have_last)
{
Scalar dot =
@@ -1169,15 +1192,31 @@ void
Scalar angle = (Scalar) acos((double) dot);
//
// Only judge while the pod is actually crossing the
// view. A pod held near the centre subtends almost
// no angle and every ratio against it is noise.
// Compare each frame against the frame BEFORE it,
// not against a running mean. The angular step
// varies six hundredfold between a pod at 119m and
// the same pod at 5m, so a long mean cannot keep up
// during a pass and reports its own lag as a lurch -
// which is precisely what the previous version of
// this trace did, in the one window that mattered.
//
if (mean_angle > 0.0002f)
// Consecutive frames of a smooth pass are nearly
// equal however fast the sweep, so their ratio is
// immune to range. A tick is one frame that barely
// moves followed by one that catches up, so the
// stall is the event worth timing.
//
if (have_angle && last_angle > 0.00002f)
{
if (angle > mean_angle * 2.5f)
Scalar ratio = angle / last_angle;
if (ratio > 2.5f)
{
++lurches;
}
else if (ratio < 0.4f)
{
++stalls;
Scalar now = (Scalar) Now();
@@ -1193,6 +1232,9 @@ void
last_event = now;
}
}
last_angle = angle;
have_angle = True;
mean_angle = (mean_angle > 0.0f)
? (mean_angle * 0.95f + angle * 0.05f)
: angle;
@@ -1205,21 +1247,36 @@ void
if (next_say > 0.0f)
{
DEBUG_STREAM << "CamLog: on-screen motion - " << frames
<< " frames, " << lurches << " lurch(es), period "
<< " frames, " << stalls << " stall(s), "
<< lurches << " lurch(es), period "
<< ((interval_count > 0)
? (interval_sum / interval_count)
: 0.0f)
<< "s (" << interval_min << ".." << interval_max
<< "), mean step " << (mean_angle * 1000.0f)
<< " mrad, range " << range << "m\n" << std::flush;
DEBUG_STREAM << "CamLog: render fraction - mean "
<< ((frames > 0) ? (fraction_sum / frames) : 0.0f)
<< ", " << pinned_high << " pinned at 1, "
<< pinned_low << " at 0, of " << frames
<< " frames ("
<< ((pinned_high * 4 > frames)
? "simulation is behind, interpolation degenerate"
: "sweeping normally")
<< ")\n" << std::flush;
}
next_say = ((Scalar) Now()) + 5.0f;
frames = 0;
lurches = 0;
stalls = 0;
interval_sum = 0.0f;
interval_min = 1000.0f;
interval_max = 0.0f;
interval_count = 0;
fraction_sum = 0.0f;
pinned_high = 0;
pinned_low = 0;
}
}
}