//! ITU-T X.25 CRC-16 — MAVLink's checksum function. //! //! Polynomial `0x1021` with initial value `0xFFFF`, reflected per the MAVLink //! reference implementation. Each frame's CRC is computed over the byte range //! `[len..payload_end]` followed by the message-specific `CRC_EXTRA` byte. /// Initial CRC value used by MAVLink. (Polynomial is `0x1021`, implicit in the /// reflected algorithm below.) pub const INIT: u16 = 0xFFFF; /// Update an in-progress CRC accumulator with a single byte. /// /// Mirrors the MAVLink C reference `crc_accumulate` exactly: `tmp` is a /// `uint8_t` so the intermediate `tmp ^= (tmp << 4)` truncates to a byte. /// Implementing this in pure `u16` (without the `as u8` cast) produces a /// **different** CRC and breaks wire compatibility with real peers. #[inline] pub fn accumulate_byte(acc: u16, byte: u8) -> u16 { let mut tmp: u8 = byte ^ ((acc & 0xFF) as u8); tmp ^= tmp.wrapping_shl(4); let tmp = tmp as u16; (acc >> 8) ^ (tmp << 8) ^ (tmp << 3) ^ (tmp >> 4) } /// CRC accumulator over a byte slice, starting from `start`. #[inline] pub fn accumulate(start: u16, bytes: &[u8]) -> u16 { bytes.iter().fold(start, |acc, b| accumulate_byte(acc, *b)) } /// Compute the full MAVLink CRC for a frame body and its `crc_extra` byte. /// /// `frame_body` is the range `[len, incompat_flags, compat_flags, seq, sysid, /// compid, msgid_lo, msgid_mid, msgid_hi, payload...]` — i.e. the frame /// without `STX` and without the trailing CRC. #[inline] pub fn frame_crc(frame_body: &[u8], crc_extra: u8) -> u16 { let intermediate = accumulate(INIT, frame_body); accumulate_byte(intermediate, crc_extra) } // The dummy value below is the CRC of "123456789" with INIT=0xFFFF and POLY=0x1021, // computed per the MAVLink reflection. Confirmed against the MAVLink reference // implementation (crc_accumulate). #[cfg(test)] mod tests { use super::*; #[test] fn empty_slice_returns_init() { // Act let crc = accumulate(INIT, &[]); // Assert assert_eq!(crc, INIT); } #[test] fn single_byte_known_value() { // Act let crc = accumulate(INIT, &[0x00]); // Assert: derived by hand from the C reference. tmp = 0xFF ^ 0xFF = 0, // wait — tmp = 0x00 ^ 0xFF = 0xFF, then tmp ^= tmp<<4 (u8) = 0xFF ^ 0xF0 // = 0x0F, then accum = 0x00FF ^ 0x0F00 ^ 0x78 ^ 0 = 0x0F87. assert_eq!(crc, 0x0F87); } #[test] fn mavlink_check_string_matches_pymavlink() { // MAVLink's CRC variant (byte-wise, not bit-reflected) gives 0x6F91 // for the ASCII string "123456789" — verified by running the same // bytes through pymavlink's `mavcrc.x25crc`. This is NOT the same as // the textbook CRC-CCITT (XMODEM) value 0x29B1. let crc = accumulate(INIT, b"123456789"); assert_eq!(crc, 0x6F91); } }