calibration: throttle reads rest (0), not full, at the detent
Faithful port of a legacy quirk made harmful by axis routing: _throttleLast initialized to Center and the lT==0 hold path meant the first poll at rest compounded 16383*32 -> clamped 32766 = FULL throttle until the lever moved past the deadzone (under the old routing this held LeftTrigger at 255 - fire-secondary - from power-on). lT==0 now zeroes like the deadzone branch and _throttleLast initializes to 0, the sanctioned rest value ResetAll/ ResetThrottle already used. Three regression tests replace the one that asserted the old behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,12 @@ namespace RioJoy.Core.Calibration;
|
||||
/// across <see cref="Update"/> calls exactly as the legacy globals did.
|
||||
///
|
||||
/// <para>Final outputs are clamped to <c>0..<see cref="AxisOutputs.Max"/></c> — the
|
||||
/// documented axis range — which also guards a legacy quirk where a value pinned
|
||||
/// at its observed extreme could compound across polls (see ⚠️ below).</para>
|
||||
/// documented axis range. One legacy quirk is deliberately fixed rather than
|
||||
/// ported: with the lever exactly at the tracked start (the detent), the legacy
|
||||
/// held the previous throttle value, which the ×32 rescale compounded into a
|
||||
/// full-throttle pin — on the very first poll at rest, and for a poll when a
|
||||
/// release landed exactly on the start. The detent now zeroes like the rest of
|
||||
/// the deadzone (see the note in <see cref="Throttle"/>).</para>
|
||||
/// </summary>
|
||||
public sealed class AxisCalibrator
|
||||
{
|
||||
@@ -28,8 +32,11 @@ public sealed class AxisCalibrator
|
||||
private int _leftPedalStart = int.MaxValue;
|
||||
private int _rightPedalStart = int.MaxValue;
|
||||
|
||||
// Last computed (pre-clamp) outputs — persist across calls like the legacy globals.
|
||||
private int _throttleLast = AxisOutputs.Center;
|
||||
// Last computed (pre-clamp) outputs — persist across calls like the legacy
|
||||
// globals. The throttle starts at 0, its calibrated rest value (the same value
|
||||
// ResetThrottle/ResetAll restore): the legacy Center init fed the detent-hold
|
||||
// quirk (see Throttle) and pinned Z at full on the first poll at rest.
|
||||
private int _throttleLast;
|
||||
private int _leftPedalLast = AxisOutputs.Center;
|
||||
private int _rightPedalLast = AxisOutputs.Center;
|
||||
private int _joystickXLast = AxisOutputs.Center;
|
||||
@@ -98,17 +105,20 @@ public sealed class AxisCalibrator
|
||||
if (lT > 800)
|
||||
lT = 800;
|
||||
|
||||
if (lT != 0)
|
||||
{
|
||||
lT = lT * 1000 / 800;
|
||||
if (lT is > -DeadzoneThrottle and < DeadzoneThrottle)
|
||||
_throttleLast = 0;
|
||||
else if (_throttleResult > 0) // back
|
||||
_throttleLast = 900 + (lT * _throttleResult / 10);
|
||||
else // front
|
||||
_throttleLast = lT * _throttleResult;
|
||||
}
|
||||
// lT == 0 leaves _throttleLast unchanged (legacy behavior).
|
||||
// Deliberate divergence from the legacy port: lT == 0 (lever exactly at
|
||||
// the tracked start, i.e. the detent — including samples that raise the
|
||||
// running max) zeroes the output like the rest of the ±50 deadzone.
|
||||
// The legacy left _throttleLast unchanged here, and the ×32 rescale
|
||||
// below then compounded the held value — pinning Z at full throttle on
|
||||
// the first poll at rest and spiking full for a poll when a release
|
||||
// landed exactly on the start.
|
||||
lT = lT * 1000 / 800;
|
||||
if (lT is > -DeadzoneThrottle and < DeadzoneThrottle)
|
||||
_throttleLast = 0;
|
||||
else if (_throttleResult > 0) // back
|
||||
_throttleLast = 900 + (lT * _throttleResult / 10);
|
||||
else // front
|
||||
_throttleLast = lT * _throttleResult;
|
||||
}
|
||||
|
||||
_throttleLast = Math.Abs(_throttleLast * 32);
|
||||
|
||||
@@ -128,11 +128,39 @@ public class AxisCalibratorTests
|
||||
Assert.Equal(AxisOutputs.Center, cal.Update(Report(x: 0)).X);
|
||||
}
|
||||
|
||||
// --- Detent regression (legacy quirk deliberately fixed) -----------------
|
||||
|
||||
[Fact]
|
||||
public void Outputs_AreClampedToAxisRange()
|
||||
public void Throttle_FirstPollAtRest_IsZero_NotPinned()
|
||||
{
|
||||
// The init-state throttle quirk yields a value far over range on the first
|
||||
// poll at rest; the documented 0..Max clamp guards it.
|
||||
Assert.Equal(AxisOutputs.Max, new AxisCalibrator().Update(Report(throttle: 0)).Z);
|
||||
// Regression: the legacy Center init + detent-hold compounded through the
|
||||
// ×32 rescale and pinned Z at Max on the very first poll at rest — full
|
||||
// reverse on a Centered thumb route, full thrust on a UnipolarPositive one
|
||||
// (Descent). Rest must read 0 with no reset and stay there.
|
||||
var cal = new AxisCalibrator();
|
||||
Assert.Equal(0, cal.Update(Report(throttle: 0)).Z);
|
||||
Assert.Equal(0, cal.Update(Report(throttle: 0)).Z);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throttle_ReleaseExactlyOntoStart_ReturnsToZero_NoSpike()
|
||||
{
|
||||
// Regression: a release landing exactly on the tracked start (lT == 0)
|
||||
// used to hold the last moving value, which the ×32 rescale turned into a
|
||||
// one-poll full-throttle spike until jitter broke it.
|
||||
var cal = new AxisCalibrator();
|
||||
Assert.Equal(0, cal.Update(Report(throttle: 0)).Z); // seed start at rest
|
||||
Assert.Equal(16000, cal.Update(Report(throttle: -400)).Z); // pushed halfway
|
||||
Assert.Equal(0, cal.Update(Report(throttle: 0)).Z); // release onto start
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throttle_SampleAboveRunningMax_ReadsAsRest()
|
||||
{
|
||||
// Samples above the tracked start re-base the auto-range (lT == 0 again)
|
||||
// and must read as rest, not re-enter the legacy hold path.
|
||||
var cal = new AxisCalibrator();
|
||||
cal.Update(Report(throttle: -400)); // power-on with lever pushed: becomes the start
|
||||
Assert.Equal(0, cal.Update(Report(throttle: -100)).Z); // above max → re-based, rest
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user