目录
前言
数据校验概念
CRC校验算法
CRC计算原理
CRC算法种类
代码实现CRC算法
python实现算法①
python实现算法②
总结
前言
在二次开发eCan上位机应用时,遇到了采用CRC(全称是循环冗余校验)32算法校验文件传输完整性的场景,浅浅地记录一下使用心得.
数据校验概念
数据在传输的过程中,会受到各种干扰的影响,如脉冲干扰,随机噪声干扰和人为干扰等,这会使数据产生差错。为了能够控制传输过程的差错,通信系统必须采用有效的检错方案。因此产生了数据校验。
数据校验是为保证数据的完整性进行的一种验证操作。通常用一种指定的算法对原始数据计算出的一个校验值,接收方用同样的算法计算一次校验值,如果两次计算得到的检验值相同,则说明数据是完整的。
CRC校验算法
CRC计算原理
被校验的数据 除以 多项式,得到的余数就是CRC数值。不过这里的除法是模2除法,也就是异或。多项式是通信中双方约定数,可以自己定义,不过目前有各个领域定义好的,可直接用。
CRC算法种类
{ //CRC算法名称宽度 多项式 初始值 结果异或值 输入反转 输出反转 {"CRC4_ITU", 4, 0x03, 0x00, 0x00, E_TRUE, E_TRUE}, {"CRC5_EPC", 5, 0x09, 0x09, 0x00, E_FALSE, E_FALSE}, {"CRC5_ITU", 5, 0x15, 0x00, 0x00, E_TRUE, E_TRUE}, {"CRC5_USB", 5, 0x05, 0x1F, 0x1F, E_TRUE, E_TRUE}, {"CRC6_ITU", 6, 0x03, 0x00, 0x00, E_TRUE, E_TRUE}, {"CRC7_MMC", 7, 0x09, 0x00, 0x00, E_FALSE, E_FALSE}, {"CRC8", 8, 0x07, 0x00, 0x00, E_FALSE, E_FALSE}, {"CRC8_ITU", 8, 0x07, 0x00, 0x55, E_FALSE, E_FALSE}, {"CRC8_ROHC", 8, 0x07, 0xFF, 0x00, E_TRUE, E_TRUE}, {"CRC8_MAXIM", 8, 0x31, 0x00, 0x00, E_TRUE, E_TRUE}, {"CRC16_IBM", 16, 0x8005, 0x0000, 0x0000, E_TRUE, E_TRUE}, {"CRC16_MAXIM", 16, 0x8005, 0x0000, 0xFFFF, E_TRUE, E_TRUE}, {"CRC16_USB", 16, 0x8005, 0xFFFF, 0xFFFF, E_TRUE, E_TRUE}, {"CRC16_MODBUS",16, 0x8005, 0xFFFF, 0x0000, E_TRUE, E_TRUE}, {"CRC16_CCITT", 16, 0x1021, 0x0000, 0x0000, E_TRUE, E_TRUE}, {"CRC16_CCITT_FALSE",16, 0x1021, 0xFFFF, 0x0000, E_FALSE, E_FALSE}, {"CRC16_X25", 16, 0x1021, 0xFFFF, 0xFFFF, E_TRUE, E_TRUE}, {"CRC16_XMODEM", 16, 0x1021, 0x0000, 0x0000, E_FALSE, E_FALSE}, {"CRC16_DNP", 16, 0x3D65, 0x0000, 0xFFFF, E_TRUE, E_TRUE}, {"CRC32", 32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, E_TRUE, E_TRUE}, {"CRC32_MPEG2", 32, 0x04C11DB7, 0xFFFFFFFF, 0x00000000, E_FALSE, E_FALSE}};
代码实现CRC算法
要求:在这里,在IEEE 802.3 帧中采用的是CRC-32校验码,输出4个字节的校验码.
x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
多项式按正常写法是0x04C11DB7 ,翻转则为 0xEDB88320
通常的CRC算法在计算一个数据段的CRC值时,其CRC值是由求解每个数值的CRC值的和对CRC寄存器的值反复更新而得到的。
python实现算法①
binascii刚好有现成的可以直接调用.输入要求的是二进制,那就用binascii.a2b_hex将16进制的字符串转换成二进制.
import binasciidef crc2hex(crc): """ crc : str 从hex文件或bin文件中获取的有效数据 func a2b_hex 16进制字符串 -> 二进制 func crc32 二进制 计算得到crc值 -> int 最后和 0xffffffff 相乘得正值 return str 4个字节 """ # return '%08x' % (binascii.crc32(binascii.a2b_hex(crc)) & 0xffffffff) # 增加一个 取反 return '%08x' % (binascii.crc32(binascii.a2b_hex(crc)) & 0xffffffff ^ 0xffffffff)
python实现算法②
CRC- 32的算法的多项式是0x04C11DB7 , 初始值默认是0xFFFFFFFF.
有多项式和初始值,就可以事先构造表.这里用查表法来减少计算量,
import re# 定义一个256个元素的全0数组custom_crc32_table = [0 for x in range(0, 256)]# 定义一个256个元素的全0数组reversal_crc32_table = [0 for x in range(0, 256)]# 一个8位数据加到16位累加器中去,只有累加器的高8位或低8位与数据相作用,# 其结果仅有256种可能的组合值。def generate_crc32_table(): for i in range(256): c = i << 24 for j in range(8): if (c & 0x80000000): c = (c << 1) ^ 0x04C11DB7 else: c = c << 1 custom_crc32_table[i] = c & 0xffffffffdef get_crc32_val(bytes_arr): length = len(bytes_arr) # print(f"data length {length}") if bytes_arr != None: crc = 0xffffffff for i in range(0, length): crc = (crc <> 24)) & 0xff] else: crc = 0xffffffff # - 返回计算的CRC值 crc = getReverse(crc ^ 0xffffffff, 32) return crc# 反转def getReverse(tempData, byte_length): reverseData = 0 for i in range(0, byte_length): reverseData += ((tempData >> i) & 1) <> 1) ^ 0xEDB88320 else: c = c >> 1 reversal_crc32_table[i] = c & 0xffffffffdef reversal_getCrc32(bytes_arr): length = len(bytes_arr) if bytes_arr != None: crc = 0xffffffff for i in range(0, length): crc = (crc >> 8) ^ reversal_crc32_table[(bytes_arr[i] ^ crc) & 0xff] else: crc = 0xffffffff crc = crc ^ 0xffffffff return crcif __name__ == '__main__': import struct import zlib import binascii s = struct.pack('>i', 400) print('当前CRC输入初始值:', (s, type(s))) test = binascii.crc32(s) & 0xffffffff print('算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % test))) test = zlib.crc32(s) & 0xffffffff print('算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % test))) buf_s = [0x00, 0x00, 0x01, 0x90] generate_crc32_table() crc_stm = get_crc32_val(bytearray(buf_s)) & 0xffffffff print('算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % crc_stm))) reversal_init_crc32_table() crc_stm = reversal_getCrc32(bytearray(buf_s)) & 0xffffffff print('反转算出来的CRC值:', '0x' + "{:0>8s}".format(str('%x' % crc_stm))) """ 当前CRC输入初始值: (b'\x00\x00\x01\x90', ) 算出来的CRC值: 0xc8507d19 算出来的CRC值: 0xc8507d19 算出来的CRC值: 0xc8507d19 反转算出来的CRC值: 0xc8507d19 """
总结
在数据传输前,CRC校验得到一个4字节的校验码.接收方对获取的有效数据进行同样的校验,若校验码一致,说明传输的数据是完整的.
CRC算法的种类多样,在算法上的差别在于多项式和初始值,这两个也可以自行定义.