inital implementation of crc6
This commit is contained in:
+104
-11
@@ -77,7 +77,7 @@ impl Stored_U26 {
|
|||||||
self.0.to_be_bytes()
|
self.0.to_be_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_be_bytes(bytes: [u8;4]) -> Result<Self, U26Error> {
|
pub fn from_be_bytes(bytes: [u8; 4]) -> Result<Self, U26Error> {
|
||||||
let raw_u32 = u32::from_be_bytes(bytes);
|
let raw_u32 = u32::from_be_bytes(bytes);
|
||||||
// the last bit is expetced to be 0
|
// the last bit is expetced to be 0
|
||||||
if raw_u32 & 0x1 != 0x0 {
|
if raw_u32 & 0x1 != 0x0 {
|
||||||
@@ -86,9 +86,7 @@ impl Stored_U26 {
|
|||||||
let value = (raw_u32 & U26_VALUE_MASK) >> 6;
|
let value = (raw_u32 & U26_VALUE_MASK) >> 6;
|
||||||
let hemming = ((raw_u32 & U26_HEMMING_MASK) >> 1) as u8;
|
let hemming = ((raw_u32 & U26_HEMMING_MASK) >> 1) as u8;
|
||||||
match check_hemming(value, hemming) {
|
match check_hemming(value, hemming) {
|
||||||
Ok(()) => Ok(
|
Ok(()) => Ok(Stored_U26(raw_u32)),
|
||||||
Stored_U26(raw_u32)
|
|
||||||
),
|
|
||||||
Err(correction_data) => {
|
Err(correction_data) => {
|
||||||
let (dc, hc) = correction_data.calc_correction_data();
|
let (dc, hc) = correction_data.calc_correction_data();
|
||||||
if check_hemming(value ^ dc, hemming ^ hc).is_err() {
|
if check_hemming(value ^ dc, hemming ^ hc).is_err() {
|
||||||
@@ -101,7 +99,7 @@ impl Stored_U26 {
|
|||||||
} else {
|
} else {
|
||||||
Ok(Self::new(value ^ dc))
|
Ok(Self::new(value ^ dc))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,18 +302,70 @@ pub fn check_hemming(data: u32, hemming: u8) -> Result<(), HemmingCorrectionValu
|
|||||||
if h == hemming {
|
if h == hemming {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(HemmingCorrectionValue::new(h^hemming))
|
Err(HemmingCorrectionValue::new(h ^ hemming))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// CRC-6 calculation using polynomial 0x21 (MSB first)
|
||||||
|
///
|
||||||
|
/// This function calculates a 6-bit CRC for 26 bits of data.
|
||||||
|
/// The polynomial used is 0x21 in the crc world polynoms
|
||||||
|
/// are named after the bit pattern with implicit assumption
|
||||||
|
/// of the trailing 1, thus 0x21 become 0x43 referring to
|
||||||
|
/// the 0b1000011 pattern. (Don't ask why just accept it …)
|
||||||
|
///
|
||||||
|
/// # Algorithm
|
||||||
|
///
|
||||||
|
/// The CRC is calculated using the standard CRC algorithm with MSB first bit ordering,
|
||||||
|
/// the inital value is equal to 0x00 and no additional transformat at the end is performed.
|
||||||
|
///
|
||||||
|
/// # Error Detection
|
||||||
|
/// This CRC provides a Hamming distance of 3, meaning it can detect:
|
||||||
|
/// - All single-bit errors
|
||||||
|
/// - All double-bit errors
|
||||||
|
/// - Random errors with a probability of 2^-6 = 0.015625 of going undetected
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn calc_crc6(data: u32) -> u8 {
|
||||||
|
let mut working_data: u32 = data << 6;
|
||||||
|
let mut polynom: u32 = 0x43 << 25;
|
||||||
|
let mut mask: u32 = 0x40 << 25;
|
||||||
|
while working_data >= 0x40 {
|
||||||
|
if working_data & mask != 0 {
|
||||||
|
working_data ^= polynom;
|
||||||
|
} else {
|
||||||
|
polynom >>= 1;
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(working_data & 0x3f) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CRC-6 checking
|
||||||
|
///
|
||||||
|
/// This function is the mirror piece to the crc6 calculation, it checks a crc value against
|
||||||
|
/// the data to determine if the crc is valid.
|
||||||
|
pub fn check_crc6(data: u32, crc: u8) -> bool {
|
||||||
|
let mut working_data: u32 = data << 6 | crc as u32;
|
||||||
|
let mut polynom: u32 = 0x43 << 25;
|
||||||
|
let mut mask: u32 = 0x40 << 25;
|
||||||
|
while working_data >= 0x40 {
|
||||||
|
if working_data & mask != 0 {
|
||||||
|
working_data ^= polynom;
|
||||||
|
} else {
|
||||||
|
polynom >>= 1;
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
working_data == 0x00000000
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod base_type_tests {
|
mod base_type_tests {
|
||||||
use std::println;
|
use super::{
|
||||||
|
Stored_U26, Transit_U26, U26_MAX_VALUE, calc_crc6, calc_hemming, check_crc6, check_hemming,
|
||||||
|
};
|
||||||
use crate::types::U26Error;
|
use crate::types::U26Error;
|
||||||
|
|
||||||
use super::{calc_hemming, check_hemming, Stored_U26, U26_MAX_VALUE};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hemming_code_generation() {
|
fn hemming_code_generation() {
|
||||||
assert_eq!(calc_hemming(0x000), 0b00000);
|
assert_eq!(calc_hemming(0x000), 0b00000);
|
||||||
@@ -378,7 +428,7 @@ mod base_type_tests {
|
|||||||
let invalid_hemming = hemming ^ bit_error;
|
let invalid_hemming = hemming ^ bit_error;
|
||||||
let correction = check_hemming(data, invalid_hemming).unwrap_err();
|
let correction = check_hemming(data, invalid_hemming).unwrap_err();
|
||||||
let (_, hc) = correction.calc_correction_data();
|
let (_, hc) = correction.calc_correction_data();
|
||||||
assert_eq!(invalid_hemming^hc, hemming);
|
assert_eq!(invalid_hemming ^ hc, hemming);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,4 +441,47 @@ mod base_type_tests {
|
|||||||
bytes[2] = bytes[2] ^ 0x01;
|
bytes[2] = bytes[2] ^ 0x01;
|
||||||
assert_eq!(Stored_U26::from_be_bytes(bytes).unwrap(), data);
|
assert_eq!(Stored_U26::from_be_bytes(bytes).unwrap(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crc6_simple_data() {
|
||||||
|
assert_eq!(calc_crc6(0x0000000), 0x00);
|
||||||
|
assert_eq!(calc_crc6(0x0000001), 0x03);
|
||||||
|
assert_eq!(calc_crc6(0x0000043), 0x00);
|
||||||
|
assert_eq!(calc_crc6(0x3ffffff), 0x06);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crc6_consistency() {
|
||||||
|
// Test that same input always produces same output
|
||||||
|
let data = 0x123456;
|
||||||
|
let crc = calc_crc6(data);
|
||||||
|
assert!(check_crc6(data, crc))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crc6_different_inputs_different_outputs() {
|
||||||
|
// Test that different inputs produce different CRCs (with high probability)
|
||||||
|
let data1 = 0x123456;
|
||||||
|
let data2 = 0x123457;
|
||||||
|
let crc1 = calc_crc6(data1);
|
||||||
|
let crc2 = calc_crc6(data2);
|
||||||
|
assert_ne!(crc1, crc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crc6_bit_error_detection() {
|
||||||
|
// Test that single bit errors are detected
|
||||||
|
let original_data = 0x123456;
|
||||||
|
let original_crc = calc_crc6(original_data);
|
||||||
|
|
||||||
|
// Flip each bit and verify CRC changes
|
||||||
|
for bit_pos in 0..26 {
|
||||||
|
let mut corrupted_data = original_data;
|
||||||
|
let mask = 1 << (25 - bit_pos); // Flip specific bit
|
||||||
|
corrupted_data ^= mask;
|
||||||
|
|
||||||
|
// With high probability, CRC should change
|
||||||
|
assert!(!check_crc6(corrupted_data, original_crc));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user