inital implementation of crc6

This commit is contained in:
2026-03-22 17:39:25 +01:00
parent 1106b4a82e
commit a71ac0c43e
+104 -11
View File
@@ -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));
}
}
} }