Files
CydandClaude Fable 5 2ac3188528 Fix pedal gauges frozen under ZR mix: feed L/R from pre-mix pedal readouts
The editor's L/R gauges read Rx/Ry, but EnableZR (on in real profiles)
folds the pedals into Rz and pins Rx/Ry to center, so the bars never
moved with the physical pedals. AxisCalibrator now exposes
LeftPedalOutput/RightPedalOutput (the calibrated pre-mix positions) and
RioRuntime.AxesUpdated carries an AxisReadout (the six virtual axes +
both pedals); the strip draws L/R from the pedal readouts, Z/Rz/X/Y
from the axes. With ZR off the readouts equal Rx/Ry, so nothing
changes there. Verified by screenshot: Rx/Ry centered while L=75% and
R=25% render correctly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:22:05 -05:00

139 lines
4.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using RioJoy.Core.Calibration;
using RioJoy.Core.Protocol;
using Xunit;
namespace RioJoy.Core.Tests.Calibration;
public class AxisCalibratorTests
{
// AnalogReport(throttle, leftPedal, rightPedal, joystickY, joystickX)
private static AnalogReport Report(
short throttle = 0, short left = 0, short right = 0, short y = 0, short x = 0) =>
new(throttle, left, right, y, x);
// --- Throttle (Z) --------------------------------------------------------
[Fact]
public void Throttle_BelowStart_IsProportional()
{
var cal = new AxisCalibrator();
cal.ResetThrottle();
// At-rest after reset: throttle equals its tracked start ⇒ centered at 0.
Assert.Equal(0, cal.Update(Report(throttle: 0)).Z);
// Pulled back 400 of 800 → half range × 32 = 16000.
Assert.Equal(16000, cal.Update(Report(throttle: -400)).Z);
// Full back (800) → 32000.
Assert.Equal(32000, cal.Update(Report(throttle: -800)).Z);
}
[Fact]
public void Throttle_OutOfRange_Saturates()
{
Assert.Equal(0, new AxisCalibrator().Update(Report(throttle: 900)).Z); // > +range
Assert.Equal(32000, new AxisCalibrator().Update(Report(throttle: -900)).Z); // < -range
}
[Fact]
public void Throttle_Invert_Flips()
{
var cal = new AxisCalibrator(new AxisCalibrationConfig { InvertZ = true });
// throttle=900 → raw 0 → inverted to Max.
Assert.Equal(AxisOutputs.Max, cal.Update(Report(throttle: 900)).Z);
}
// --- Pedals (Rx / Ry / Rz) ----------------------------------------------
[Fact]
public void Pedals_EnableZR_CentersRxRy_AndMixesRudder()
{
var cal = new AxisCalibrator(); // EnableZR default true
AxisOutputs o = cal.Update(Report(left: 0, right: 0));
Assert.Equal(AxisOutputs.Center, o.Rx);
Assert.Equal(AxisOutputs.Center, o.Ry);
Assert.Equal(AxisOutputs.Center, o.Rz); // rudder with both pedals at rest
}
[Fact]
public void Pedals_DisableZR_DriveRxDirectly_AndCenterRz()
{
var cal = new AxisCalibrator(new AxisCalibrationConfig { EnableZR = false });
// First sample sets the auto-range start; second (less depressed) produces a value.
cal.Update(Report(left: -100));
AxisOutputs o = cal.Update(Report(left: 0));
// lP = (-100) - 0 = -100 → ×1000/500 = -200 → |·|×32 = 6400.
Assert.Equal(6400, o.Rx);
Assert.Equal(AxisOutputs.Center, o.Rz); // Rz centered when ZR disabled
// The pedal readouts mirror Rx/Ry when there is no mix.
Assert.Equal(o.Rx, cal.LeftPedalOutput);
Assert.Equal(o.Ry, cal.RightPedalOutput);
}
[Fact]
public void PedalOutputs_TrackPedals_EvenWithZrMix()
{
var cal = new AxisCalibrator(); // EnableZR default true
cal.Update(Report(left: -100, right: -100));
AxisOutputs o = cal.Update(Report(left: 0, right: 0));
// The mix centers Rx/Ry, but the pedal readouts still track the pedals
// (same math as the ZR-off Rx/Ry: 6400 for this deflection).
Assert.Equal(AxisOutputs.Center, o.Rx);
Assert.Equal(AxisOutputs.Center, o.Ry);
Assert.Equal(6400, cal.LeftPedalOutput);
Assert.Equal(6400, cal.RightPedalOutput);
}
// --- Joystick (X / Y) ----------------------------------------------------
[Fact]
public void Joystick_AtZero_IsCentered()
{
AxisOutputs o = new AxisCalibrator().Update(Report(x: 0, y: 0));
Assert.Equal(AxisOutputs.Center, o.X);
Assert.Equal(AxisOutputs.Center, o.Y);
}
[Fact]
public void JoystickX_RightAndLeft_MoveOppositeSidesOfCenter()
{
// Right (positive raw) → below center; with auto-range seeded by this sample.
Assert.Equal(52, new AxisCalibrator().Update(Report(x: 80)).X);
// Left (negative raw) → above center.
Assert.Equal(32733, new AxisCalibrator().Update(Report(x: -80)).X);
}
[Fact]
public void JoystickX_Invert_Flips()
{
var cal = new AxisCalibrator(new AxisCalibrationConfig { InvertX = true });
Assert.Equal(AxisOutputs.Max - 52, cal.Update(Report(x: 80)).X);
}
[Fact]
public void ResetHorizontalJoystick_RecentersX()
{
var cal = new AxisCalibrator();
Assert.Equal(52, cal.Update(Report(x: 80)).X);
cal.ResetHorizontalJoystick();
// After reset, X holds center until the stick next moves off zero.
Assert.Equal(AxisOutputs.Center, cal.Update(Report(x: 0)).X);
}
[Fact]
public void Outputs_AreClampedToAxisRange()
{
// 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);
}
}