rfid_debug/
lib.rs

1use bit_field::BitField;
2
3/// Structure of a Wiegand format. Wiegand encoding can vary depending on the
4/// manufacturer or implementation of access control in a facility.
5pub struct WiegandFormat {
6    /// Position of the even parity bit
7    pub parity_even: usize,
8    /// Position of the odd parity bit
9    pub parity_odd: usize,
10    /// Facility code bit range denoted by: inclusive lower bound and non-inclusive upper bound
11    pub facility_code: (usize, usize),
12    /// Card number/identifier bit range denoted by: inclusive lower bound and non-inclusive upper bound
13    pub card_number: (usize, usize),
14}
15
16/// Encoding issues
17#[derive(Debug)]
18pub enum WiegandError {
19    /// Parity bit was wrong, possible bad read
20    InvalidParity,
21    /// Attempting to access a bit range [start, end) where start > end
22    InvalidRange,
23}
24
25impl WiegandFormat {
26    /// Decodes a (facility_code, card_number) tuple from an integer sourced from an RFID scan
27    pub fn decode(self, i: u32) -> Result<(u8, u16), WiegandError> {
28        let facility_code = i.get_bits(self.facility_code.0..self.facility_code.1) as u8;
29        let card_number = i.get_bits(self.card_number.0..self.card_number.1) as u16;
30
31        Ok((facility_code, card_number))
32    }
33
34    /// assumes `i` is not padded
35    pub fn to_string(self, i: u32) -> String {
36        let parity_bit_odd = parity_sum(i, self.card_number) % 2 == 0;
37        let parity_bit_even = parity_sum(i, self.facility_code) % 2 == 1;
38
39        format!(
40            "{:b}{:b}{:b}",
41            parity_bit_even as u8, i, parity_bit_odd as u8
42        )
43    }
44}
45
46/// sum of bits set in integer, i
47fn parity_sum(i: u32, range: (usize, usize)) -> u8 {
48    let mut sum = 0;
49
50    for bit_index in range.0..=range.1 {
51        let bit_set = i & (1 << bit_index) != 0;
52        sum += if bit_set { 1 } else { 0 };
53    }
54
55    sum % 2
56}
57
58#[cfg(test)]
59mod tests {
60    use super::WiegandFormat;
61
62    #[test]
63    fn standard_format() {
64        let standard_wiegand = WiegandFormat {
65            parity_even: 0,
66            parity_odd: 25,
67            card_number: (0, 16),
68            facility_code: (16, 24),
69        };
70
71        let rfid_payload = 5666862u32;
72
73        assert_eq!(standard_wiegand.decode(rfid_payload).unwrap(), (86u8, 30766u16));
74    }
75}