calibrator: exact center in must be exact center out

The legacy joystick math left the output unchanged when the raw sample was
exactly 0 - a case a jittering pot never produces, so on real hardware the hold
was invisible. vRIO's pad deadzone produces it constantly: every release of the
bench gamepad shapes the residual to exactly 0.0 and holds it there, one sample
per 55 ms poll. The calibrator held the last in-motion output against that
stream indefinitely, so the virtual pad froze off-center at whatever the stick
commanded the instant before release.

Flown, that read as a Descent 3 pod drifting on yaw and pitch with the stick
centered, stopping only when the release was slow enough to land an
intermediate sample inside the +/-5 band. It survived a full day of suspects -
the game's deadzone, its axis map, its focus handling, com0com, the serial
protocol - because every layer below the calibrator was correct: the freeze was
visible in joy.cpl itself, and the game's own control trace showed heading
latched at -0.38 while the wire carried perfect zeros.

Same divergence from the legacy port, same reasoning, as the throttle detent
fix above it: zero means centered, not "no information". Both axes; regression
tests pin the fast-release case and center stability across repeated zero
polls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-01 20:49:25 -05:00
co-authored by Claude Opus 5
parent bdd30678e3
commit 870a8a257d
2 changed files with 46 additions and 2 deletions
+19 -2
View File
@@ -200,7 +200,19 @@ public sealed class AxisCalibrator
// 16838 (not 16383) and a +2 nudge are deliberate legacy anti-snap tweaks.
_joystickXLast = lJx > 0 ? 16838 - ((lJx + 2) * sRightRate) : AxisOutputs.Center;
}
// lJx == 0 leaves _joystickXLast unchanged (legacy behavior).
else
{
// Deliberate divergence from the legacy port, same reasoning as the
// throttle detent above: the legacy held the previous output on a raw
// of exactly 0. A real pot jitters and never rests at exact 0, so the
// hold was invisible on hardware - but vRIO's pad deadzone emits
// sustained exact zeros on release, and the hold latched the last
// in-motion output indefinitely: the ship kept turning at whatever
// rate the stick commanded the instant before release (bench
// 2026-08-01, Descent 3 drifting on yaw/pitch with the stick
// centered). Exact center in must be exact center out.
_joystickXLast = AxisOutputs.Center;
}
return _config.InvertX ? AxisOutputs.Max - _joystickXLast : _joystickXLast;
}
@@ -223,7 +235,12 @@ public sealed class AxisCalibrator
lJy -= DeadzoneJoystick;
_joystickYLast = lJy > 0 ? AxisOutputs.Center - (lJy * sDownRate) : AxisOutputs.Center;
}
// lJy == 0 leaves _joystickYLast unchanged (legacy behavior).
else
{
// Exact 0 centers rather than holding the previous output - see the
// matching branch in JoystickX for the full story.
_joystickYLast = AxisOutputs.Center;
}
return _config.InvertY ? AxisOutputs.Max - _joystickYLast : _joystickYLast;
}