Port the analog calibration math and the plasma/VFD command set: - Calibration/: AxisCalibrator ports UpdateThrottle/UpdatePadal/UpdateJoystick (riovjoy2.cpp#L1504+) — throttle deadzone + ratchet field, pedal deadzones, joystick X/Y auto-ranging from observed min/max, rudder mixing (enableZR), and the per-axis invert flags. Stateful (start positions, last outputs, observed extremes) like the legacy globals, with the RIOcmd axis resets. Final outputs clamp to the documented 0..32766 range (also guards a legacy compounding quirk). AxisOutputs carries the six values; IJoystickSink gains SetAxis(JoyAxis,value) so calibrated axes reach the HID feeder. - Plasma/: PlasmaCommands builds the CPlasma ESC sequences (clear/cursor/font/ attr/box draw+fill/text) + GetFontSize + the PlasmaPosText auto-fit/centering; PlasmaDisplay writes them over the secondary COM transport. - tests: 21 new xUnit tests (105 total) for throttle proportional/saturation/ invert, pedal ZR mix vs. direct, joystick centering/direction/invert/reset, the output clamp, and plasma byte sequences/font sizes/auto-fit positioning. Hardware verification of axis feel + plasma output remains; the game-specific PlasmaScoreDraw layout is deferred to profile content (Phase 5/7). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
119 lines
4.1 KiB
C#
119 lines
4.1 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
|
||
}
|
||
|
||
// --- 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);
|
||
}
|
||
}
|