inital implementation of crc6

This commit is contained in:
2026-03-22 17:39:25 +01:00
parent 1106b4a82e
commit a71ac0c43e
+106 -13
View File
@@ -77,7 +77,7 @@ impl Stored_U26 {
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);
// the last bit is expetced to be 0
if raw_u32 & 0x1 != 0x0 {
@@ -86,9 +86,7 @@ impl Stored_U26 {
let value = (raw_u32 & U26_VALUE_MASK) >> 6;
let hemming = ((raw_u32 & U26_HEMMING_MASK) >> 1) as u8;
match check_hemming(value, hemming) {
Ok(()) => Ok(
Stored_U26(raw_u32)
),
Ok(()) => Ok(Stored_U26(raw_u32)),
Err(correction_data) => {
let (dc, hc) = correction_data.calc_correction_data();
if check_hemming(value ^ dc, hemming ^ hc).is_err() {
@@ -101,7 +99,7 @@ impl Stored_U26 {
} else {
Ok(Self::new(value ^ dc))
}
},
}
}
}
}
@@ -282,9 +280,9 @@ impl HemmingCorrectionValue {
/// calculate xor bit patterns to apply to the data or the stored hemming code
/// returns tuple -> (xor with data, xor with hemming code)
fn calc_correction_data(&self) -> (u32, u8) {
if [1, 2, 4, 8, 16].contains(&self.0) {
if [1, 2, 4, 8, 16].contains(&self.0) {
(0x00000000, 0x1 << self.0.ilog2())
} else {
} else {
if self.0 == 3 {
(0x1, 0x00)
} else if self.0 < 8 {
@@ -304,18 +302,70 @@ pub fn check_hemming(data: u32, hemming: u8) -> Result<(), HemmingCorrectionValu
if h == hemming {
Ok(())
} 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)]
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 super::{calc_hemming, check_hemming, Stored_U26, U26_MAX_VALUE};
#[test]
fn hemming_code_generation() {
assert_eq!(calc_hemming(0x000), 0b00000);
@@ -378,7 +428,7 @@ mod base_type_tests {
let invalid_hemming = hemming ^ bit_error;
let correction = check_hemming(data, invalid_hemming).unwrap_err();
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;
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));
}
}
}