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>
139 lines
4.8 KiB
C#
139 lines
4.8 KiB
C#
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);
|
||
}
|
||
}
|