Files
riojoy/src/RioJoy.Core/Protocol/AnalogReport.cs
T
CydandClaude Fable 5 3b2af7b79a Phase 8A (1/2): de-Span the serial layer, swap JSON to Newtonsoft
Prepares RioJoy.Core for the net40 (Windows XP) target, which has no
System.Memory, ValueTask, or System.Text.Json:
- IRioTransport and the whole protocol/framing layer now use byte[] +
  Task (RioPacket.Payload, PacketParser/Builder, RioChecksum, replies,
  AnalogReport, RioHidReport). At 9600 baud Span bought nothing; the
  SerialPortTransport bridge copies disappear entirely.
- ConfigStore/OverlayTemplateStore switch to Newtonsoft 13 with the
  same conventions (indented, PascalCase, string enums, null-skipping);
  verified against the real STJ-written config.json and regions.json
  (load + round-trip). System.Memory and System.Text.Json packages
  dropped.

275 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:36:19 -05:00

76 lines
2.9 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.
namespace RioJoy.Core.Protocol;
/// <summary>
/// The five raw analog axes decoded from an <see cref="RioCommand.AnalogReply"/>
/// payload. Values are the 14-bit signed raw counts straight off the RIO, before
/// any calibration/deadzone mapping (that math is Phase 4). Port of
/// <c>AnalogEvent</c> / <c>CombinePair</c> (riovjoy2.cpp#L1116); see
/// docs/PROTOCOL.md §4.
/// </summary>
public readonly struct AnalogReport
{
public short Throttle { get; }
public short LeftPedal { get; }
public short RightPedal { get; }
public short JoystickY { get; }
public short JoystickX { get; }
public AnalogReport(short throttle, short leftPedal, short rightPedal, short joystickY, short joystickX)
{
Throttle = throttle;
LeftPedal = leftPedal;
RightPedal = rightPedal;
JoystickY = joystickY;
JoystickX = joystickX;
}
/// <summary>
/// Decode a 14-bit signed axis value from a (low, high) byte pair, each
/// carrying 7 data bits. Port of <c>CombinePair</c> (riovjoy2.cpp#L1116):
/// combine the 7-bit halves then sign-extend bit 13.
/// </summary>
public static short CombinePair(byte low, byte high)
{
int raw = (low & 0x7F) | (high << 7); // 14 bits
if ((raw & 0x2000) != 0) // bit 13 set → negative
raw |= ~0x3FFF; // sign-extend into the high bits
return (short)raw;
}
/// <summary>
/// Try to decode an <see cref="RioCommand.AnalogReply"/> payload (10 bytes:
/// 5 axes × low,high in the order throttle, left pedal, right pedal,
/// joystick Y, joystick X). Returns <see langword="false"/> if any byte equals
/// <c>0xFE</c> (<see cref="RioControl.Restart"/>), which the RIO uses as an
/// "invalid sample" sentinel — the legacy code ignores such replies.
/// </summary>
public static bool TryParse(byte[] payload, out AnalogReport report)
{
if (payload is null) throw new ArgumentNullException(nameof(payload));
if (payload.Length != RioCommandTable.PayloadLength(RioCommand.AnalogReply))
throw new ArgumentException(
$"AnalogReply payload must be {RioCommandTable.PayloadLength(RioCommand.AnalogReply)} bytes.",
nameof(payload));
foreach (byte b in payload)
{
if (b == (byte)RioControl.Restart)
{
report = default;
return false;
}
}
report = new AnalogReport(
throttle: CombinePair(payload[0], payload[1]),
leftPedal: CombinePair(payload[2], payload[3]),
rightPedal: CombinePair(payload[4], payload[5]),
joystickY: CombinePair(payload[6], payload[7]),
joystickX: CombinePair(payload[8], payload[9]));
return true;
}
public override string ToString() =>
$"T:{Throttle} L:{LeftPedal} R:{RightPedal} Y:{JoystickY} X:{JoystickX}";
}