The deadline belongs on the sender's clock
The prediction test answered clearly, though not through the verdict label - that compared two noise floors with no absolute threshold and so cried TIMING over errors of a millimetre. Read the magnitudes instead: extrapolating the sender's own position and velocity across the gap between two of the sender's own timestamps lands within 0.0005 to 0.011m. Constant velocity holds to MILLIMETRES over one interval. Against corrections of 0.25 to 0.66m that is a factor of five hundred, so the two cannot be the same quantity. The corrections are not prediction failure at all - they are the latency offset, which is what a dead reckoner is supposed to carry. That leaves the target, and the fault is mine. The dead reckoner projects to updateOrigin + velocity * (nextUpdate - lastUpdate), so that difference becomes a DISTANCE once multiplied by speed. lastUpdate is the sampling moment RP412NETCLOCK computes, on the sender's clock. The median predictor I added set nextUpdate from Now(), ours - so the subtraction spanned two different timelines and yielded the interval plus however late that particular packet ran. At 52 m/s each millisecond of that is 52mm. Fifteen milliseconds of ordinary jitter is three quarters of a metre of target error, enough to collapse a one metre step to a third, and only on the packets that ran late. An intermittent tick, worst when a pod is close and fast - which is the symptom as it was reported. Anchor nextUpdate to lastUpdate and the difference is the predicted interval exactly. The target then depends on what the sender said and how fast it is going, and not at all on the route the packet took. NetClock confirmed live in the log, offset 52735ms, which is these two machines' launch times differing now that the clock counts from launch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+37
-2
@@ -1148,12 +1148,20 @@ void
|
||||
//
|
||||
nextUpdate = Now();
|
||||
Scalar diff = nextUpdate - lastUpdate;
|
||||
Scalar anchorInterval = (Scalar) 0;
|
||||
if (diff < 10.0f)
|
||||
{
|
||||
if (UseMedianPrediction())
|
||||
{
|
||||
Scalar predicted = PredictUpdateInterval(diff);
|
||||
|
||||
//
|
||||
// Anchor the projection to the SENDER's timeline, below,
|
||||
// once Entity::ReadUpdateRecord has moved lastUpdate to
|
||||
// the sampling moment RP412NETCLOCK worked out.
|
||||
//
|
||||
anchorInterval = predicted;
|
||||
|
||||
//
|
||||
// Score the previous prediction against the gap that
|
||||
// actually just elapsed - a true one-step-ahead error,
|
||||
@@ -1173,8 +1181,6 @@ void
|
||||
if (diff > widestGap) { widestGap = diff; }
|
||||
if (diff > kLongGapThreshold) { longGapCount++; }
|
||||
if (diff < kQueuedGapThreshold) { queuedGapCount++; }
|
||||
|
||||
nextUpdate += predicted;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1299,6 +1305,35 @@ void
|
||||
//
|
||||
Entity::ReadUpdateRecord(record);
|
||||
|
||||
//
|
||||
// Put the projection deadline on the SENDER's timeline.
|
||||
//
|
||||
// The dead reckoner projects to updateOrigin + velocity *
|
||||
// (nextUpdate - lastUpdate), so that difference is a DISTANCE
|
||||
// once multiplied by speed - and a pod at 52 m/s turns every
|
||||
// millisecond in it into 52mm of target.
|
||||
//
|
||||
// lastUpdate is the sampling moment RP412NETCLOCK computed, on
|
||||
// the sender's clock. Setting nextUpdate from Now() measured the
|
||||
// gap between two different timelines, so it came out as the
|
||||
// interval PLUS however late this particular packet happened to
|
||||
// be. Fifteen milliseconds of ordinary jitter became three
|
||||
// quarters of a metre of target error, which is enough to
|
||||
// collapse a one metre step to a third - and only on the packets
|
||||
// that ran late, which is exactly the intermittent tick that was
|
||||
// reported.
|
||||
//
|
||||
// Anchored to lastUpdate the difference is the predicted
|
||||
// interval exactly, so the target depends on what the sender
|
||||
// said and how fast it is going, and not at all on the route the
|
||||
// packet took to reach us.
|
||||
//
|
||||
if (anchorInterval > (Scalar) 0)
|
||||
{
|
||||
nextUpdate = lastUpdate;
|
||||
nextUpdate += anchorInterval;
|
||||
}
|
||||
|
||||
//
|
||||
//-----------------------
|
||||
// Update the motion data
|
||||
|
||||
Reference in New Issue
Block a user