Files
riojoy/tests/RioJoy.Core.Tests/Calibration/AxisCalibratorTests.cs
T
CydandClaude Opus 4.8 24a1b64519 Phase 4: axis calibration + plasma display (RioJoy.Core)
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>
2026-06-26 15:24:45 -05:00

119 lines
4.1 KiB
C#
Raw 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
}
// --- 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);
}
}