Skip to main content

osdp/packet/
crc.rs

1//! CRC-16/KERMIT (CCITT) used as the strong frame trailer.
2//!
3//! # Spec: Annex C
4//!
5//! Polynomial `0x1021`, initial value `0x1D0F`, no input/output reflect, no
6//! final XOR. Transmitted little-endian.
7
8/// Pre-computed table for CRC-16/KERMIT with seed `0x1D0F`.
9const TABLE: [u16; 256] = {
10    let mut table = [0u16; 256];
11    let mut i = 0u16;
12    while i < 256 {
13        let mut crc = i << 8;
14        let mut bit = 0;
15        while bit < 8 {
16            if (crc & 0x8000) != 0 {
17                crc = (crc << 1) ^ 0x1021;
18            } else {
19                crc <<= 1;
20            }
21            bit += 1;
22        }
23        table[i as usize] = crc;
24        i += 1;
25    }
26    table
27};
28
29/// Compute the CRC-16/KERMIT over `bytes`.
30///
31/// The OSDP variant uses initial value `0x1D0F`. The result is transmitted
32/// little-endian after the payload.
33#[inline]
34pub fn crc16(bytes: &[u8]) -> u16 {
35    let mut crc: u16 = 0x1D0F;
36    for &b in bytes {
37        let idx = ((crc >> 8) ^ u16::from(b)) as u8 as usize;
38        crc = (crc << 8) ^ TABLE[idx];
39    }
40    crc
41}
42
43/// Compute the CRC-16/KERMIT and return it as little-endian bytes.
44#[inline]
45pub fn crc16_le(bytes: &[u8]) -> [u8; 2] {
46    crc16(bytes).to_le_bytes()
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    /// Annex E example: `osdp_COMSET` to broadcast.
54    #[test]
55    fn comset_broadcast_vector() {
56        let buf = [
57            0x53, 0x7F, 0x0D, 0x00, 0x04, 0x6E, 0x00, 0x80, 0x25, 0x00, 0x00,
58        ];
59        assert_eq!(crc16_le(&buf), [0x6E, 0x38]);
60    }
61
62    /// Annex E example: `osdp_ID` request.
63    #[test]
64    fn id_vector() {
65        let buf = [0x53, 0x00, 0x09, 0x00, 0x04, 0x61, 0x00];
66        assert_eq!(crc16_le(&buf), [0xC0, 0x66]);
67    }
68}