From 5cd9783d3842bf7bd2b1f9ff0600b8b0c449fa4a Mon Sep 17 00:00:00 2001 From: Cyd Date: Tue, 11 Aug 2026 09:24:09 -0500 Subject: [PATCH] Ask whether the pod stalled or the pan did Two things are now ruled out with evidence rather than argument. Render interpolation is healthy. behind is computed by Time::operator-, which subtracts ticks and only then converts to float, so it carries the clock full millisecond precision. With a 20ms step that gives the fraction twenty possible values, and the measured 17 frames of 293 sitting at zero is 5.8% against the 5% a sawtooth crossing zero would produce by itself. Mean 0.474, never once pinned at 1: it is sweeping correctly. And the trace period was never a period. Every interval it reported - 0.03125, 0.125, 0.375, 1.90625 - is a multiple of 1/32s, which is the spacing of a float32 near 474196. That is this machine uptime in seconds, because GetRTC returns QueryPerformanceCounter scaled to milliseconds since boot, so (Scalar) Now() is a number near half a million and its resolution has decayed to 31ms - coarser than the physics step it is being used to time. The stall COUNT is unaffected and real, at three to five a second; the interval between them was measurement noise. Logged separately as its own defect. What is left is that the angle is measured BETWEEN the pod and the eye, so a hitch in the pan reads exactly like a hitch in the pod - and the symptom is a pod moving PAST, which is when the pan rate peaks. Measure each one on its own, by the same consecutive-frame ratio, and let the trace say which. Co-Authored-By: Claude Opus 5 (1M context) --- MUNGA_L4/L4VIDRND.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/MUNGA_L4/L4VIDRND.cpp b/MUNGA_L4/L4VIDRND.cpp index 66dcb7e..7cd0ce0 100644 --- a/MUNGA_L4/L4VIDRND.cpp +++ b/MUNGA_L4/L4VIDRND.cpp @@ -1159,11 +1159,68 @@ void static Scalar fraction_sum = 0.0f; static int pinned_high = 0; static int pinned_low = 0; + static Point3D last_pod(0.0f, 0.0f, 0.0f); + static Point3D last_eye(0.0f, 0.0f, 0.0f); + static Logical have_pod = False; + static Scalar last_pod_step = 0.0f; + static Scalar last_eye_step = 0.0f; + static Scalar eye_travel = 0.0f; + static int pod_stalls = 0; + static int eye_stalls = 0; Vector3D dir; dir.Divide(to_pod, range); ++frames; + // + // Split the pod's motion from the camera's. + // + // The angle above is measured BETWEEN the two, so a + // hitch in the pan and a hitch in the pod are the same + // reading - and the symptom is specifically a pod moving + // past, which is when the pan rate peaks. Measuring each + // on its own says which one to go and fix. + // + // Same ratio test as the angle, and for the same reason: + // consecutive frames of smooth motion are near equal + // whatever the speed. + // + Point3D pod_now; + pod_now.x = renderToWorld(3,0); + pod_now.y = renderToWorld(3,1); + pod_now.z = renderToWorld(3,2); + + if (have_pod) + { + Vector3D pod_moved; + pod_moved.Subtract(pod_now, last_pod); + + Vector3D eye_moved; + eye_moved.x = gEyeWorld.x - last_eye.x; + eye_moved.y = gEyeWorld.y - last_eye.y; + eye_moved.z = gEyeWorld.z - last_eye.z; + + Scalar pod_step = pod_moved.Length(); + Scalar eye_step = eye_moved.Length(); + + if (last_pod_step > 0.0001f && pod_step < last_pod_step * 0.4f) + { + ++pod_stalls; + } + if (last_eye_step > 0.0001f && eye_step < last_eye_step * 0.4f) + { + ++eye_stalls; + } + last_pod_step = pod_step; + last_eye_step = eye_step; + eye_travel += eye_step; + } + last_pod = pod_now; + last_eye.x = gEyeWorld.x; + last_eye.y = gEyeWorld.y; + last_eye.z = gEyeWorld.z; + have_pod = True; + // // How far through the physics step this frame is being // drawn. This is the number the interpolation actually @@ -1265,6 +1322,17 @@ void ? "simulation is behind, interpolation degenerate" : "sweeping normally") << ")\n" << std::flush; + + DEBUG_STREAM << "CamLog: motion split - pod " + << pod_stalls << " stall(s), eye " + << eye_stalls << " stall(s), eye travelled " + << eye_travel << "m, verdict " + << ((pod_stalls > eye_stalls * 2) + ? "the pod" + : ((eye_stalls > pod_stalls * 2) + ? "the camera" + : "both, or neither")) + << "\n" << std::flush; } next_say = ((Scalar) Now()) + 5.0f; frames = 0; @@ -1277,6 +1345,9 @@ void fraction_sum = 0.0f; pinned_high = 0; pinned_low = 0; + pod_stalls = 0; + eye_stalls = 0; + eye_travel = 0.0f; } } }