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}";
}