use std::fmt;
#[derive(Debug)]
pub struct SbusPacket {
pub analog_channels: Vec<u16>,
pub channel_17: bool,
pub channel_18: bool,
}
impl SbusPacket {
pub fn try_from_bytes(bytes: &[u8; 25]) -> Result<SbusPacket, ParsingError> {
if bytes[0] != 0xF0 {
return Err(ParsingError::new(ErrorKind::NotSbus));
}
if (bytes[23] & 16) > 0 {
return Err(ParsingError::new(ErrorKind::FailsafeEngaged));
}
if (bytes[23] & 32) > 0 {
return Err(ParsingError::new(ErrorKind::FrameLost));
}
let mut bit_counter = 7;
let mut output = Vec::<u16>::new();
let mut current = 0u16;
for i in 0..176 {
current |=
(((bytes[(i / 8) + 1] & 2u8.pow(bit_counter)) >> bit_counter) as u16) << (i % 11);
if i % 11 == 10 {
output.push(current);
current = 0u16;
}
if bit_counter == 0 {
bit_counter = 7;
} else {
bit_counter -= 1;
}
}
let channel_17 = (bytes[23] & 128) > 0;
let channel_18 = (bytes[23] & 64) > 0;
Ok(SbusPacket {
analog_channels: output,
channel_17,
channel_18,
})
}
}
pub struct ParsingError {
kind: ErrorKind,
}
impl ParsingError {
pub fn kind(&self) -> ErrorKind {
self.kind.to_owned()
}
}
impl fmt::Debug for ParsingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = match self.kind {
ErrorKind::NotSbus => "Received non-SBUS packet",
ErrorKind::FailsafeEngaged => "The failsafe has been engaged, this packet is unusable",
ErrorKind::FrameLost => "A frame has been lost, this packet is unusable",
};
write!(f, "{}", error)
}
}
impl ParsingError {
fn new(kind: ErrorKind) -> ParsingError {
ParsingError { kind }
}
}
#[derive(Clone)]
pub enum ErrorKind {
NotSbus,
FailsafeEngaged,
FrameLost,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn analog() {
let data = [
0xF0, 128, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
];
let parsed = SbusPacket::try_from_bytes(&data).unwrap();
let correct_analog: [u16; 16] = [1025, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1024];
let mut works = true;
for i in 0..15 {
if parsed.analog_channels[i] != correct_analog[i] {
works = false;
break;
}
}
assert!(works);
}
#[test]
fn rejects_non_sbus() {
let data = [
0x0A, 128, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
];
let packet_attempt = SbusPacket::try_from_bytes(&data);
let error_kind = packet_attempt.unwrap_err().kind();
let rejected_correctly = match error_kind {
ErrorKind::NotSbus => true,
_ => false,
};
assert!(rejected_correctly);
}
}