diff --git a/src/VRio.App/MainForm.cs b/src/VRio.App/MainForm.cs
index 8f2b967..a3c52a8 100644
--- a/src/VRio.App/MainForm.cs
+++ b/src/VRio.App/MainForm.cs
@@ -148,6 +148,7 @@ internal sealed class MainForm : Form
// Canvas ↔ device wiring.
_canvas.LampProvider = _device.GetLamp;
_canvas.AxisProvider = _device.GetAxis;
+ _canvas.WireAxisProvider = _device.GetWireAxis;
_canvas.AddressPressed += _device.PressAddress;
_canvas.AddressReleased += _device.ReleaseAddress;
_canvas.AxisMoved += (axis, value) => _device.SetAxis(axis, value);
@@ -192,7 +193,11 @@ internal sealed class MainForm : Form
if (!_kbInput.Checked)
_router.ReleaseAllKeys();
};
- _invertY.CheckedChanged += (_, _) => _device.InvertJoystickY = _invertY.Checked;
+ _invertY.CheckedChanged += (_, _) =>
+ {
+ _device.InvertJoystickY = _invertY.Checked;
+ _canvas.Invalidate(); // the wire readout flips even though the axes didn't move
+ };
_device.InvertJoystickY = _invertY.Checked; // the field initializer raises no event
_kbLights.CheckedChanged += (_, _) =>
{
diff --git a/src/VRio.App/PanelCanvas.cs b/src/VRio.App/PanelCanvas.cs
index 5c4fdbf..b17a99b 100644
--- a/src/VRio.App/PanelCanvas.cs
+++ b/src/VRio.App/PanelCanvas.cs
@@ -73,6 +73,12 @@ internal sealed class PanelCanvas : Control
/// Current raw value of an axis (from the device).
public Func? AxisProvider { get; set; }
+ ///
+ /// Axis value as transmitted to the host (joystick Y sign flip applied).
+ /// The text readout shows this; the dot and gauges track physical state.
+ ///
+ public Func? WireAxisProvider { get; set; }
+
/// The user pressed a panel control.
public event Action? AddressPressed;
@@ -299,9 +305,10 @@ internal sealed class PanelCanvas : Control
new Size(statusRect.Width, 0), TextFormatFlags.WordBreak).Height + 2;
}
+ short WireAxis(RioAxis a) => WireAxisProvider?.Invoke(a) ?? Axis(a);
string readout =
- $"Z {Axis(RioAxis.Throttle),4} L {Axis(RioAxis.LeftPedal),3} R {Axis(RioAxis.RightPedal),3} " +
- $"X {Axis(RioAxis.JoystickX),3} Y {Axis(RioAxis.JoystickY),3}";
+ $"Z {WireAxis(RioAxis.Throttle),4} L {WireAxis(RioAxis.LeftPedal),3} R {WireAxis(RioAxis.RightPedal),3} " +
+ $"X {WireAxis(RioAxis.JoystickX),3} Y {WireAxis(RioAxis.JoystickY),3}";
TextRenderer.DrawText(g, readout, statusFont,
new Rectangle(statusRect.X, readoutTop, statusRect.Width, statusRect.Bottom - readoutTop), green,
TextFormatFlags.Left | TextFormatFlags.Top | TextFormatFlags.SingleLine);
diff --git a/src/VRio.Core/Device/VRioDevice.cs b/src/VRio.Core/Device/VRioDevice.cs
index f169a19..c9994a6 100644
--- a/src/VRio.Core/Device/VRioDevice.cs
+++ b/src/VRio.Core/Device/VRioDevice.cs
@@ -108,6 +108,20 @@ public sealed class VRioDevice
lock (_gate) return _axes[(int)axis];
}
+ ///
+ /// Axis value as the next AnalogReply will carry it — same as
+ /// except for the flip.
+ ///
+ public short GetWireAxis(RioAxis axis)
+ {
+ short value = GetAxis(axis);
+ return axis == RioAxis.JoystickY ? WireY(value) : value;
+ }
+
+ private short WireY(short y) => InvertJoystickY
+ ? (short)Math.Min(AnalogCodec.Max, -y) // clamp: -Min (8192) is one past the 14-bit Max
+ : y;
+
///
/// Move an axis. Values are clamped to the 14-bit signed range the wire
/// can carry; the new value is returned by the next AnalogReply.
@@ -281,9 +295,7 @@ public sealed class VRioDevice
y = _axes[(int)RioAxis.JoystickY];
x = _axes[(int)RioAxis.JoystickX];
}
- if (InvertJoystickY) // clamp: -Min (8192) is one past the 14-bit Max
- y = (short)Math.Min(AnalogCodec.Max, -y);
- Send(PacketBuilder.AnalogReply(t, l, r, y, x));
+ Send(PacketBuilder.AnalogReply(t, l, r, WireY(y), x));
break;
case RioCommand.ResetRequest:
diff --git a/tests/VRio.Core.Tests/VRioDeviceTests.cs b/tests/VRio.Core.Tests/VRioDeviceTests.cs
index 38a17c9..c810c12 100644
--- a/tests/VRio.Core.Tests/VRioDeviceTests.cs
+++ b/tests/VRio.Core.Tests/VRioDeviceTests.cs
@@ -77,7 +77,8 @@ public class VRioDeviceTests
byte[] p = Assert.Single(wire.Packets).Payload;
Assert.Equal(-3000, AnalogCodec.Combine(p[6], p[7]));
- Assert.Equal(3000, device.GetAxis(RioAxis.JoystickY)); // the panel's view is unflipped
+ Assert.Equal(3000, device.GetAxis(RioAxis.JoystickY)); // the panel's dot is unflipped
+ Assert.Equal(-3000, device.GetWireAxis(RioAxis.JoystickY)); // the panel's readout matches the wire
// Full negative deflection: -Min is one past Max, so it clamps rather than throws.
device.SetAxis(RioAxis.JoystickY, AnalogCodec.Min);