namespace RioJoy.Core.Protocol; /// /// The five raw analog axes decoded from an /// 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 /// AnalogEvent / CombinePair (riovjoy2.cpp#L1116); see /// docs/PROTOCOL.md §4. /// 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; } /// /// Decode a 14-bit signed axis value from a (low, high) byte pair, each /// carrying 7 data bits. Port of CombinePair (riovjoy2.cpp#L1116): /// combine the 7-bit halves then sign-extend bit 13. /// 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; } /// /// Try to decode an payload (10 bytes: /// 5 axes × low,high in the order throttle, left pedal, right pedal, /// joystick Y, joystick X). Returns if any byte equals /// 0xFE (), which the RIO uses as an /// "invalid sample" sentinel — the legacy code ignores such replies. /// 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}"; }