//! CRC-16/CCITT-FALSE โ€” the wire-level integrity check used by DongLoRa Protocol. //! //! Polynomial `0x1021`, initial value `0xFFFF`, no reflection, XOR-out `0x0000`. //! This is the same variant also known as CRC-16/AUTOSAR or //! CRC-16/IBM-3740. It is **NOT** the same as CRC-16/KERMIT, //! CRC-16/XMODEM, or plain "CRC-16/CCITT" โ€” those use different initial //! values or reflect their inputs. //! //! Check value: `crc16("123456789") == 0x29B1`. A module-level test pins //! this, and `tests/vectors.rs` anchors every Appendix C wire example. /// CRC-16/CCITT-FALSE of `data`. pub fn crc16(data: &[u8]) -> u16 { let mut crc: u16 = 0xFFFF; for &byte in data { crc ^= (byte as u16) << 8; for _ in 0..8 { if crc & 0x8000 != 0 { crc = (crc << 1) ^ 0x1021; } else { crc <<= 1; } } } crc } #[cfg(test)] mod tests { use super::*; #[test] fn check_value_matches_spec() { // PROTOCOL.md ยง2.2: CRC-16/CCITT-FALSE of ASCII "123456789" is 0x29B1. assert_eq!(crc16(b"123456789"), 0x29B1); } #[test] fn empty_input_is_initial_value() { // No reflection, no XOR-out: empty input leaves the init value untouched. assert_eq!(crc16(&[]), 0xFFFF); } #[test] fn single_zero_byte() { // 0xFFFF XOR 0x0000 = 0xFFFF, then 8 shift-and-maybe-xor rounds. // Worked out by hand against the reference impl in Appendix B. assert_eq!(crc16(&[0x00]), 0xE1F0); } #[test] fn differs_from_kermit_variant() { // CRC-16/KERMIT of "123456789" is 0x2189 โ€” if we ever accidentally // swap init or reflection, this vector catches it. assert_ne!(crc16(b"123456789"), 0x2189); } }