-
2016-11-06 23:48:55
由于元件故障和噪声干扰等因素常常导致计算机在处理信息的过程中出现错误。为了防止信息在传输过程的错误,将信号采用专门的逻辑电路进行编码以检测错误,甚至校正错误。
通常的方法是在每个字上添加一些校验位,用来确定字中出现错误的位置。
在计算机中有三种常见的检验码,分别是:奇偶校验码,海明校验码,循环冗余码
<1>奇偶校验码
这是最简单的校验方式,在信息编码的时候,将字的最高位作为校验位。需要说明的事奇偶校验也有两种校验方式:奇校验和偶校验。
奇校验:在最高位添加0或1,使字编码中的“1”的个数为奇数。
偶校验:在最高位添加0或1,使字编码中的“1”的个数为偶数。
校验特点:一次能校验更多的数据,效率较高,系统实现也比较简单,检测可靠性有所提高,但仍然不能检测出所有的错误。
<2> 海明校验码
海明校验是一种多重校验, 将有效信息按某种规律分成若干组,每组安排一个校验位,做奇偶测试,就能提供多位检错信息,以指出最大可能是哪位出错。假设k个数据位设置r个校验位,则应满足:
^r>=k+r+1
校验位分布2^0,2^1,2^2...2^n位上,如下所示(以4位数据位为例):
校验的位置为:
由此可以看出,校验位置的数据分别校验:
接下来采用异或运算得出具体的R的值:
R1=1,R2=0,R3=0;
再将值分别填入信息位即可。
<3>循环冗余码
奇偶校验码作为一种检错码虽然简单,但是漏检率太高。在计算机网络和数据通信中用E得最广泛的检错码,是一种漏检率低得多也便于实现的循环冗余码CRC (Cyclic Redundancy .Code),CRC码又称为多项式码。
任何一个由二进制数位串组成的代码,都可以惟一地与一个只含有0和1两个系数的多项式建立一一对应的关系。例如,代码1010111对应的多项式为X6+X4+X2+X+1,同样.多项式X5+X3+X2+X+1对应的代码为101111。
CRC码在发送端编码和接收端校验时,都可以利用事先约定的生成多项式G(X)来得到。 k位要发送的信息位可对应于一个(k-1)次多项式K(X),r位冗余位则对应于一个(r-1)次多项式R(X),由k位信息位后面加上r位冗余位组成的 n=k+r位码字则对应于一个(n-1)次多项式T(X)=Xr·K(X)+R(X)。例如
信息位:1011001→K(X)=X6+X4+X3+1
冗余位:1010→R(X)=X3+X
码字:10110011010→T(X)=X4·K(X)+R(X)
=X10+X8+X7+X4+X3+X
由信息位产生冗余位的编码过程,就是已知K(X)求R(X)的过程。在CRC码中可以通过找到一个特定的r次多项式G (X)(其最高项Xr的系数恒为1),然后用Xr·K (X)去除以G(X),得到的余式就是R(X)。特别要强调的是,这些多项式中的"+"都是模2加(也即异或运算);此外,这里的除法用的也是模2除法, 即除法过程中用到的减法是模2减法,它和模2加法的运算规则一样,都是异或运算,这是一种不考虑加法进位和减法借位的运算,即
0+O=0,0+1=1,1+0=1,1+1=0
0-0=0,0-1=1,1-0=1,1-1=0
在进行基于模2运算的多项式除法时,只要部分余数首位为1,便可上商1,否则上商0。然后按模2减法求得余数,该余数不计最高位。当被除数逐位除完时,最后得到比除数少一位的余数。此余数即为冗余位,将其添加在信息位后便构成CRC码字。
仍以上例中K(X)=X6+X4+X3+1为例(即信息位为1011001),若G(X)=X4+X3+1
(对应代码11001),取r=4,则X4·K(X)=X10+X8+X7+X4(对应代码为0110010000),其由模2除法求余式R(X)的过程所示如下:
得到的最后余数为1010,这就是冗余位,对应R(X)=X3+X。
由于R(X)是Xr·K(X)除以G(X)的余式,那么下列关系式必然满足
Xr·K(X)=G(X)Q(X)+R(X)
其中Q(X)为商式。根据模二运算规则R(X)+R(X)=0的特点,可将上式改记为
[Xr-K(X)+R(X)]/G(X)=Q(X)
即 T(X)/G(X)=Q(X)
由此可见,信道上发送的码字多项式T(X)=Xr-K(X)+R(X)。若传输过程无错,则接收方收到的码字也对应于此多项式,也即接收到的码字多项式能被G(X)整除。因而接收端的校验过程就是将接收到的码字多项式除以G(X)的过程。若余式为零则认为传输元差错;若余式不为零则传输有差错。
例如,前述例子中若码字10110011010经传输后由于受噪声的干扰,在接收端变成为10110011100,则求余式的除法如下:
求得的余式不为零,相当于在码字上面半加上了差错模式00000000110。差错模式对应的多项式记为E(X),上例中E(X)=X2+X。有差错时,接收端收到的不再是T(X),而是T(X)与E(X)之模二加,即
[T(X)+E(X)]/G(X)=T(X)/G(X)+E(X)/G(X)
若E(X)/G(X)=0,则这种差错就能检测出来;若E(X)/G(X)=0,那么由于接收到的码字多项式仍然可被G(X)整除,错误就检测不出来,也即发生了漏检。
理论上可以证明循环冗余校验码的检错能力有以下特点:
(1)可检测出所有奇数位错。
(2)可检测出所有双比特的错。
(3)可检测出所有小于、等于校验位长度的突发错。
CRC码是由r-K(X)除以某个选定的多项式后产生的,所以该多现式称生成多项式。一般来说,生成多项式位数越多校验能力越强。但并不是任何一个r+1位的二进制数都可以做生成多项式。目前广泛使用的生成多项式主要有以下四种:
(1)CRC12=X12+X11+X3+X2+1
(2)CRC16=X16+X15+X2+1(IBM公司)
(3)CRC16=X16+X12+X5+1(CCITT)
(4)CRC32=X32+X26+X23+X22+X16+X11+X10+X8+X7+X5+X4+X2+X+1
更多相关内容 -
三种常见校验码
2021-09-11 09:21:57本文主要介绍以下三种校验码:检验位求解、校验码求解、纠错能能力 奇偶校验码 海明码(汉明码) 循环冗余码(CRC码) 1.奇偶校验 校验原理 奇偶校验码 2. 海明校验码 海明码校验码思路简介 海明码求解... -
详述CRC校验码(附代码)
2021-08-26 00:15:28关注+星标公众号,不错过精彩内容来源| 一口LinuxCRC校验应用比较广泛,通常在通信领域用的比较多,即便是自定义通信协议,也可以添加CRC校验码,使其通信更加可靠。今天就来进一步描述...关注+星标公众号,不错过精彩内容
来源 | 一口Linux
CRC校验应用比较广泛,通常在通信领域用的比较多,即便是自定义通信协议,也可以添加CRC校验码,使其通信更加可靠。
今天就来进一步描述CRC校验码。
一、CRC概念
1. 什么是CRC?
CRC(Cyclic Redundancy Checksum)是一种纠错技术,代表循环冗余校验和。
数据通信领域中最常用的一种差错校验码,其信息字段和校验字段长度可以任意指定,但要求通信双方定义的CRC标准一致。主要用来检测或校验数据传输或者保存后可能出现的错误。它的使用方式可以说明如下图所示:
在数据传输过程中,无论传输系统的设计再怎么完美,差错总会存在,这种差错可能会导致在链路上传输的一个或者多个帧被破坏(出现比特差错,0变为1,或者1变为0),从而接受方接收到错误的数据。
为尽量提高接受方收到数据的正确率,在接收方接收数据之前需要对数据进行差错检测,当且仅当检测的结果为正确时接收方才真正收下数据。检测的方式有多种,常见的有奇偶校验、因特网校验和循环冗余校验等。
2. 使用方法概述
循环冗余校验是一种用于校验通信链路上数字传输准确性的计算方法(通过某种数学运算来建立数据位和校验位的约定关系的 )。
发送方计算机使用某公式计算出被传送数据所含信息的一个值,并将此值 附在被传送数据后,接收方计算机则对同一数据进行 相同的计算,应该得到相同的结果。
如果这两个 CRC结果不一致,则说明发送中出现了差错,接收方计算机可要求发送方计算机重新发送该数据。
3. 应用广泛
在诸多检错手段中,CRC是最著名的一种。CRC的全称是循环冗余校验,其特点是:检错能力强,开销小,易于用编码器及检测电路实现。从其检错能力来看,它所不能发现的错误的几率仅为0.0047%以下。
从性能上和开销上考虑,均远远优于奇偶校验及算术和校验等方式。
因而,在数据存储和数据通讯领域,CRC无处不在:著名的通讯协议X.25的FCS(帧检错序列)采用的是CRC-CCITT,WinRAR、NERO、ARJ、LHA等压缩工具软件采用的是CRC32,磁盘驱动器的读写采用了CRC16,通用的图像存储格式GIF、TIFF等也都用CRC作为检错手段。
二、CRC名称的定义
这里需要知道几个组成部分或者说计算概念:多项式公式、多项式简记式、数据宽度、初始值、结果异或值、输入值反转、输出值反转、参数模型。
1、多项式公式
对于CRC标准除数,一般使用多项式(或二项式)公式表示,如下图中除数11011(poly值为0x1b)的二项式为G(X)=X4+X3+X+1,X的指数就代表了该bit位上的数据为1,(最低位为0)。
这里特别注意一下位数问题,除数的位数为二项式最高次幂+1(4+1=5),这个很重要。
2、多项式简记式
通过对CRC的基本了解我们知道,多项式的首尾必定为1,而这个1的位置在下一步计算一定为0,所以就把前面这个1给省略掉了,出现了一个叫简记式的东西,如上例中除数11011的简记式为1011,很多看过CRC高级语言源码的人会知道,对于CRC_16标准下G(X)=X16+X15+X2+1(16#18005)的poly值实际上是8005,这里使用的就是简记式。后面会对这个用法做一个说明。
3、数据宽度
数据宽度指的就是CRC校验码的长度(二进制位数),知道了CRC的运算概念和多项式,就可以理解这个概念了,CRC长度始终要比除数位数少1,与简记式长度是一致的。
以上三个数据就是我们经常能够用到的基本数据
4、初始值与结果异或值
在一些标准中,规定了初始值,则数据在进行上述二项式运算之前,需要先将要计算的数据与初始值的最低字节进行异或,然后再与多项式进行计算。
而在结果异或值不为零的情况下,则需要将计算得到的CRC结果值再与结果异或值进行一次异或计算,得到的最终值才是我们需要的CRC校验码。
这里可以看出,初始值与结果值的位数要求与数据宽度一致。
5、输入值反转与输出值反转
输入值反转的意思是在计算之前先将二项式反转,然后再用得到的新值和数据进行计算。如对于G(X)=X16+X15+X2+1(16#18005),其正向值为1 1000 0000 0000 0101,反转值则为1010 0000 0000 0001 1
输出值反转则是将最终得到的CRC结果反转。
通常,输入值反转后的结果值也会是反转的,所以这两个选项一般是同向的,我们只有在在线CRC计算器中会看到自由选择正反转的情况存在。
三、常见的CRC算法
虽然CRC可以任意定义二项式、数据长度等,但没有一个统一的标准的话,就会让整个计算变得非常的麻烦。但实际上,不同的厂家经常采用不同的标准算法,这里列出了一些国际常用的模型表:
名称 多项式 表示法 应用举例 CRC-8 X8+X2+X+1 0X107 CRC-12 X12+X11+X3+X2+X+1 0X180F telecom systems CRC-16 X16+X15+X2+1 0X18005 Bisync, Modbus, USB, ANSI X3.28, SIA DC-07, many others; also known as CRC-16 and CRC-16-ANSI CRC-CCITT X16+X12+X5+1 0X11021 ISO HDLC, ITU X.25, V.34/V.41/V.42, PPP-FCS CRC-32 X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X+1 0x104C11DB7 ZIP, RAR, IEEE 802 LAN/FDDI, IEEE 1394, PPP-FCS CRC-32C X32+X28+X27+X26+X25+X23+X22+X20+X19+X18+X14+X13+X11+X10+X9+X8+X6+1 0x11EDC6F41 iSCSI, SCTP, G.hn payload, SSE4.2, Btrfs, ext4, Ceph 四、CRC校验算法前置知识
在学习CRC校验算法之前,先复习一下CRC会涉及的主要几个主要的算法。
1. 异或
异或,就是不同为1,相同为0,运算符号是^。
0^0 = 0 0^1 = 1 1^1 = 0 1^0 = 1
异或运算存在如下几个规律,需要了解。
0^x = x 即0 异或任何数等于任何数 1^x = ~x 即1异或任何数等于任何数取反 x^x = 0 即任何数与自己异或,结果为0 a ^ b = b ^ a 交换律 a ^ (b ^ c) = (a ^ b) ^c 结合律
2. 模2加法
模2加法相对于普通的算术加法,主要的区别在模2加法,不做进位处理。具体结果如下。0+0 = 0 0+1 = 1 1+1 = 0 1+0 = 1 我们发现模2加法的计算结果,同异或运算结果一模一样。进一步推演,我们会发现,异或运算的5个规律,同样适合于模2加法。这里,就不在一一列举了。
3. 模2减法
模2减法相对于普通的算术减法,主要的区别在模2减法,不做借位处理。具体结果如下。0-0 = 0 0-1 = 1 1-1 = 0 1-0 = 1 我们发现模2减法的计算结果,同模2加法,以及异或的运算结果一模一样。进一步推演,我们会发现,异或运算的5个规律,同样适合于模2减法。这里,就不在一一列举了。
4. 模2除法
模2除法相对于普通的算术除法,主要的区别在模2除法,它既不向上位借位,也不比较除数和被除数的相同位数值的大小,只要以相同位数进行相除即可。
五、CRC原理
CRC原理:在K位信息码(目标发送数据)后再拼接R位校验码,使整个编码长度为N位,因此这种编码也叫(N,K)码。
通俗的说,就是在需要发送的信息后面附加一个数(即校验码),生成一个新的发送数据发送给接收端。这个数据要求能够使生成的新数据被一个特定的数整除。这里的整除需要引入模 2除法的概念。
那么,CRC校验的具体做法就是
(1)选定一个标准除数(K位二进制数据串)
(2)在要发送的数据(m位)后面加上K-1位0,然后将这个新数(M+K-1位)以模2除法的方式除以上面这个标准除数,所得到的余数也就是该数据的CRC校验码(注:余数必须比除数少且只少一位,不够就补0)
(3)将这个校验码附在原m位数据后面,构成新的M+K-1位数据,发送给接收端。
(4)接收端将接收到的数据除以标准除数,如果余数为0则认为数据正确。
注意:CRC校验中有两个关键点:
一是要预先确定一个发送端和接收端都用来作为除数的二进制比特串(或多项式);
二是把原始帧与上面选定的除进行二进制除法运算,计算出FCS。
前者可以随机选择,也可按国际上通行的标准选择,但最高位和最低位必须均为“1”
六、循环冗余的计算
实例:
由于CRC-32、CRC-16、CCITT和CRC-4的编码过程基本一致,只有位数和生成多项式不一样,下面就举例,来说明CRC校验码生成过程。
对于数据1110 0101(16#E5),以指定除数11011求它的CRC校验码,其过程如下:
使用上面计算的校验和和消息数据,可以创建要传输的码字。
有时候,我们需要填充checksum到制定的位置,这就涉及到字节序问题,建议用memcpy()进行拷贝。
七、代码实现
实现算法参考网络相关代码,进行整理并验证,可直接使用。crc.c
/* *一口Linux *2021.6.21 *version: 1.0.0 */ #include "crc.h" #include <stdio.h> typedef enum { REF_4BIT = 4, REF_5BIT = 5, REF_6BIT = 6, REF_7BIT = 7, REF_8BIT = 8, REF_16BIT = 16, REF_32BIT = 32 }REFLECTED_MODE; uint32_t ReflectedData(uint32_t data, REFLECTED_MODE mode) { data = ((data & 0xffff0000) >> 16) | ((data & 0x0000ffff) << 16); data = ((data & 0xff00ff00) >> 8) | ((data & 0x00ff00ff) << 8); data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4); data = ((data & 0xcccccccc) >> 2) | ((data & 0x33333333) << 2); data = ((data & 0xaaaaaaaa) >> 1) | ((data & 0x55555555) << 1); switch (mode) { case REF_32BIT: return data; case REF_16BIT: return (data >> 16) & 0xffff; case REF_8BIT: return (data >> 24) & 0xff; case REF_7BIT: return (data >> 25) & 0x7f; case REF_6BIT: return (data >> 26) & 0x7f; case REF_5BIT: return (data >> 27) & 0x1f; case REF_4BIT: return (data >> 28) & 0x0f; } return 0; } uint8_t CheckCrc4(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut, const uint8_t *buffer, uint32_t length) { uint8_t i; uint8_t crc; if (refIn == true) { crc = init; poly = ReflectedData(poly, REF_4BIT); while (length--) { crc ^= *buffer++; for (i = 0; i < 8; i++) { if (crc & 0x01) { crc >>= 1; crc ^= poly; } else { crc >>= 1; } } } return crc ^ xorOut; } else { crc = init << 4; poly <<= 4; while (length--) { crc ^= *buffer++; for (i = 0; i < 8; i++) { if (crc & 0x80) { crc <<= 1; crc ^= poly; } else { crc <<= 1; } } } return (crc >> 4) ^ xorOut; } } uint8_t CheckCrc5(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut, const uint8_t *buffer, uint32_t length) { uint8_t i; uint8_t crc; if (refIn == true) { crc = init; poly = ReflectedData(poly, REF_5BIT); while (length--) { crc ^= *buffer++; for (i = 0; i < 8; i++) { if (crc & 0x01) { crc >>= 1; crc ^= poly; } else { crc >>= 1; } } } return crc ^ xorOut; } else { crc = init << 3; poly <<= 3; while (length--) { crc ^= *buffer++; for (i = 0; i < 8; i++) { if (crc & 0x80) { crc <<= 1; crc ^= poly; } else { crc <<= 1; } } } return (crc >> 3) ^ xorOut; } } uint8_t CheckCrc6(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut, const uint8_t *buffer, uint32_t length) { uint8_t i; uint8_t crc; if (refIn == true) { crc = init; poly = ReflectedData(poly, REF_6BIT); while (length--) { crc ^= *buffer++; for (i = 0; i < 8; i++) { if (crc & 0x01) { crc >>= 1; crc ^= poly; } else { crc >>= 1; } } } return crc ^ xorOut; } else { crc = init << 2; poly <<= 2; while (length--) { crc ^= *buffer++; for (i = 0; i < 8; i++) { if (crc & 0x80) { crc <<= 1; crc ^= poly; } else { crc <<= 1; } } } return (crc >> 2) ^ xorOut; } } uint8_t CheckCrc7(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut, const uint8_t *buffer, uint32_t length) { uint8_t i; uint8_t crc; if (refIn == true) { crc = init; poly = ReflectedData(poly, REF_7BIT); while (length--) { crc ^= *buffer++; for (i = 0; i < 8; i++) { if (crc & 0x01) { crc >>= 1; crc ^= poly; } else { crc >>= 1; } } } return crc ^ xorOut; } else { crc = init << 1; poly <<= 1; while (length--) { crc ^= *buffer++; for (i = 0; i < 8; i++) { if (crc & 0x80) { crc <<= 1; crc ^= poly; } else { crc <<= 1; } } } return (crc >> 1) ^ xorOut; } } uint8_t CheckCrc8(uint8_t poly, uint8_t init, bool refIn, bool refOut, uint8_t xorOut, const uint8_t *buffer, uint32_t length) { uint32_t i = 0; uint8_t crc = init; while (length--) { if (refIn == true) { crc ^= ReflectedData(*buffer++, REF_8BIT); } else { crc ^= *buffer++; } for (i = 0; i < 8; i++) { if (crc & 0x80) { crc <<= 1; crc ^= poly; } else { crc <<= 1; } } } if (refOut == true) { crc = ReflectedData(crc, REF_8BIT); } return crc ^ xorOut; } uint16_t CheckCrc16(uint16_t poly, uint16_t init, bool refIn, bool refOut, uint16_t xorOut, const uint8_t *buffer, uint32_t length) { uint32_t i = 0; uint16_t crc = init; while (length--) { if (refIn == true) { crc ^= ReflectedData(*buffer++, REF_8BIT) << 8; } else { crc ^= (*buffer++) << 8; } for (i = 0; i < 8; i++) { if (crc & 0x8000) { crc <<= 1; crc ^= poly; } else { crc <<= 1; } } } if (refOut == true) { crc = ReflectedData(crc, REF_16BIT); } return crc ^ xorOut; } uint32_t CheckCrc32(uint32_t poly, uint32_t init, bool refIn, bool refOut, uint32_t xorOut, const uint8_t *buffer, uint32_t length) { uint32_t i = 0; uint32_t crc = init; while (length--) { if (refIn == true) { crc ^= ReflectedData(*buffer++, REF_8BIT) << 24; } else { crc ^= (*buffer++) << 24; } for (i = 0; i < 8; i++) { if (crc & 0x80000000) { crc <<= 1; crc ^= poly; } else { crc <<= 1; } } } if (refOut == true) { crc = ReflectedData(crc, REF_32BIT); } return crc ^ xorOut; } uint32_t CrcCheck(CRC_Type crcType, const uint8_t *buffer, uint32_t length) { switch (crcType.width) { case 4: return CheckCrc4(crcType.poly, crcType.init, crcType.refIn, crcType.refOut, crcType.xorOut, buffer, length); case 5: return CheckCrc5(crcType.poly, crcType.init, crcType.refIn, crcType.refOut, crcType.xorOut, buffer, length); case 6: return CheckCrc6(crcType.poly, crcType.init, crcType.refIn, crcType.refOut, crcType.xorOut, buffer, length); case 7: return CheckCrc7(crcType.poly, crcType.init, crcType.refIn, crcType.refOut, crcType.xorOut, buffer, length); case 8: return CheckCrc8(crcType.poly, crcType.init, crcType.refIn, crcType.refOut, crcType.xorOut, buffer, length); case 16: return CheckCrc16(crcType.poly, crcType.init, crcType.refIn, crcType.refOut, crcType.xorOut, buffer, length); case 32: return CheckCrc32(crcType.poly, crcType.init, crcType.refIn, crcType.refOut, crcType.xorOut, buffer, length); } return 0; }
crc.h
/* *一口Linux *2021.6.21 *version: 1.0.0 */ #ifndef __CRC_H__ #define __CRC_H__ #include <stdint.h> #include <stdbool.h> typedef struct { uint8_t width; uint32_t poly; uint32_t init; bool refIn; bool refOut; uint32_t xorOut; }CRC_Type; uint32_t CrcCheck(CRC_Type crcType, const uint8_t *buffer, uint32_t length); #endif
main.c
/* *一口Linux *2021.6.21 *version: 1.0.0 */ #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include "crc.h" #define LENGTH 8 const uint8_t data[3][LENGTH] = { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }, { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f }}; typedef struct { CRC_Type crcType; uint32_t result[3]; }CRC_Test; CRC_Test crc4_ITU = { { 4, 0x03, 0x00, true, true, 0x00 }, { 0x0f, 0x0a, 0x0e } }; CRC_Test crc5_EPC = { { 5, 0x09, 0x09, false, false, 0x00 }, { 0x00, 0x0c, 0x17 } }; CRC_Test crc5_ITU = { { 5, 0x15, 0x00, true, true, 0x00 }, { 0x16, 0x0a, 0x17 } }; CRC_Test crc5_USB = { { 5, 0x05, 0x1f, true, true, 0x1f }, { 0x10, 0x09, 0x17 } }; CRC_Test crc6_ITU = { { 6, 0x03, 0x00, true, true, 0x00 }, { 0x1d, 0x30, 0x00 } }; CRC_Test crc7_MMC = { { 7, 0x09, 0x00, false, false, 0x00 }, { 0x57, 0x30, 0x5b } }; CRC_Test crc8 = { { 8, 0x07, 0x00, false, false, 0x00 }, { 0x3e, 0xe1, 0x36 } }; CRC_Test crc8_ITU = { { 8, 0x07, 0x00, false, false, 0x55 }, { 0x6b, 0xb4, 0x63 } }; CRC_Test crc8_ROHC = { { 8, 0x07, 0xff, true, true, 0x00 }, { 0x6b, 0x78, 0x93 } }; CRC_Test crc8_MAXIM = { { 8, 0x31, 0x00, true, true, 0x00 }, { 0x83, 0x60, 0xa9 } }; CRC_Test crc16_IBM = { { 16, 0x8005, 0x0000, true, true, 0x0000 }, { 0xc4f0, 0x2337, 0xa776 } }; CRC_Test crc16_MAXIM = { { 16, 0x8005, 0x0000, true, true, 0xffff }, { 0x3b0f, 0xdcc8, 0x5889 } }; CRC_Test crc16_USB = { { 16, 0x8005, 0xffff, true, true, 0xffff }, { 0x304f, 0xd788, 0x53c9 } }; CRC_Test crc16_MODBUS = { { 16, 0x8005, 0xffff, true, true, 0x0000 }, { 0xcfb0, 0x2877, 0xac36 } }; CRC_Test crc16_CCITT = { { 16, 0x1021, 0x0000, true, true, 0x0000 }, { 0xeea7, 0xfe7c, 0x7919 } }; CRC_Test crc16_CCITT_FALSE = { { 16, 0x1021, 0xffff, false, false, 0x0000 }, { 0x4792, 0x13a7, 0xb546 } }; CRC_Test crc16_X25 = { { 16, 0x1021, 0xffff, true, true, 0xffff }, { 0x6dd5, 0x7d0f, 0xfa6a } }; CRC_Test crc16_XMODEM = { { 16, 0x1021, 0x0000, false, false, 0x0000 }, { 0x76ac, 0x2299, 0x8478 } }; CRC_Test crc16_DNP = { { 16, 0x3D65, 0x0000, true, true, 0xffff }, { 0x7bda, 0x0535, 0x08c4 } }; CRC_Test crc32 = { { 32, 0x04c11db7, 0xffffffff, true, true, 0xffffffff }, { 0x3fca88c5, 0xe0631a53, 0xa4051a26 } }; CRC_Test crc32_MPEG2 = { { 32, 0x4c11db7, 0xffffffff, false, false, 0x00000000 }, { 0x14dbbdd8, 0x6509b4b6, 0xcb09d294 } }; void CrcTest(CRC_Test crcTest) { uint32_t i; for (i = 0; i < 3; i++) { printf("%08x\t%08x\r\n", CrcCheck(crcTest.crcType, data[i], LENGTH), crcTest.result[i]); } printf("\r\n"); } int main(void) { CrcTest(crc4_ITU); CrcTest(crc5_EPC); CrcTest(crc5_ITU); CrcTest(crc5_USB); CrcTest(crc6_ITU); CrcTest(crc7_MMC); CrcTest(crc8); CrcTest(crc8_ITU); CrcTest(crc8_ROHC); CrcTest(crc8_MAXIM); CrcTest(crc16_IBM); CrcTest(crc16_MAXIM); CrcTest(crc16_USB); CrcTest(crc16_MODBUS); CrcTest(crc16_CCITT); CrcTest(crc16_CCITT_FALSE); CrcTest(crc16_X25); CrcTest(crc16_XMODEM); CrcTest(crc16_DNP); CrcTest(crc32); CrcTest(crc32_MPEG2); return 0; }
注意
不同的CRC算法,对00H或FFH数据流的计算结果不一样,部分算法存在校验结果也为00H或FFH的情况(也就意味着存储空间处于初始化状态时:全0或全1,CRC校验反而是正确的),在应用中需要注意避免。
------------ END ------------
欢迎关注我的公众号,回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。
欢迎关注我的视频号:
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。
-
计算机网络校验码
2021-09-01 10:36:39计算机系统在运行过程中,需要进行信息交换,为确保信息在传输过程中无误,通常使用效验码,对接收到的数据进行效验,常见的效验码有三种:奇偶效验码、海明效验码、循环冗余效验码。 校验码原理 奇偶校验码()...目录
校验码
计算机系统在运行过程中,需要进行信息交换,为确保信息在传输过程中无误,通常使用效验码,对接收到的数据进行效验,常见的效验码有三种:奇偶效验码、海明效验码、循环冗余效验码。
校验码原理
对于如下编码表:
信息 A B C D 编码 00 01 10 11 A电脑要传输'C'(10)给B电脑,若在传输过程中发生意外,传输信息发生错误导致10变成了11,则B电脑就会获得到11的编码,由于11是一种合法的编码,解码后接收到信息D。
但对于下列的编码表:
信息 A B
C D 编码 100 001 010 111 A电脑要传输'C'(010)给B电脑,若在传输过程中发生意外,传输信息发生错误导致010变成了011(发生了一位错误),则B电脑就会获得到011的编码,由于011不是一种合法的编码,B电脑就会判断出信息发生错误,会让A电脑重新发送。若010变成了111(发生了2位错误),由于111('D')是合法编码,导致B电脑会接收这个错误的信息。
引入如下概念:
码字:由若干代码组成的一个字(如编码1的11,10,编码2的101)。
两个码字之间的距离:将2个码字逐位对比,不同位的个数称为两个码字之间的距离。
码距:各合法码字之间最小的距离。(编码1的码距为1,编码2的码距为2)
由于编码2的码距为2,即最小要改变2位才会变为另一个合法编码,也就是如果发生1位错误,则肯定是非法编码。
对于码距为k的编码表,若错误位数大于等于k,编码可能会变为另一合法编码,接收方则会接收错误信息;若错误位数小于k则会变为非法编码,被接收方判定为错误编码。
奇偶校验码
奇校验码:整个校验码(有效信息位和奇偶校验位)中“1”的个数为奇数
偶校验码:整个校验码(有效信息位和奇偶校验位)中“1”的个数为偶数设置最高位为奇偶校验位,对于1101010的7个有效信息位的编码其奇校验码为:11101010(5个1);偶校验码为:01101010(4个1)
假设采用的是偶校验的编码,编码为:11001010,发生错误后变为11001011(改变了1位),该编码有奇数个‘1’(5个),不满足偶校验,由此确认这个编码发生了错误,要求重新发送。若发生错误后变为11100010(改变了2位),该编码有偶数个‘1’(4个),是符合偶校验的,会被认定为有效编码。
由此可见若偶数个位发生了错误,则奇偶校验是无法判断出来的。
计算机硬件对于奇偶校验的实现(以偶校验为例):将各个位进行异或运算,所得结果为0则符合偶校验规则,为1则不符合。
对于10001编码,他的偶检验位为,即偶校验码为010001;
对于11001编码,他的偶检验位为,即偶校验码为111001。
所以可以根据异或运算的以上性质来实现。海明校验码
- 海明码最多只能检测出2位错,纠正1位错
- 海明码默认进行偶校验(除非特殊说明使用奇校验)。
- 海明码是一串由0和1组成的序列
偶校验只能发现奇数位的错误,但无法确定是哪一位发生了错误,而海明校验码则可以确定哪一位发生了错误。
设计思路:将信息位分组进行偶校验->具有多个校验位->能标注出错位置
如何确定有多少个校验位呢?假设我们现在有n位的信息位和k位的校验位,那么k个校验位总共可以表示
种状态。由于一个位置出错(纠正1位错)都需要有一种状态来表示,就要求
(+1的1表示正确的状态)
对于信息位1010
- 确定海明码的位数:
设信息位
(1010),共4位,检验位
,共3位,对应的海明码为
- 确定校验位的分布
校验位放在海明位号为
的位置上,信息位按顺序放到其余位置上
1 0 1 0 - 分组
我们需要确定是负责校验哪些位置的,我们将1,2,4的二进制码写出来,保持位数相同
1:001 2:010 4:100 ,若将0写成'*'表示通配符,如下表:
将1-7根据统配规则填入表中001 010 100 **1 *1* 1** 1 2 4 **1 *1* 1** 001(1) 010(2) 100(4) 011(3) 011(3) 101(5) 101(5) 110(6) 110(6) 111(7) 111(7) 111(7) 可以看出
**1可以管理1,3,5,7
*1*可以管理2,3,6,7
1**可以管理4,5,6,7 -
求校验位
则可以让负责
的校验,
负责
的校验,
负责
的校验。
,
,
由此则.....
由此得出海明码:1 0 1 0 0 1 0
-
查错
或者
或者
中的一组不满足偶校验,则出错
-
纠错
首先理解一下为什么海明码能纠错若蓝色区域偶校验错误,则(1,3,5,7位中有一个发生了错误)
1.黄色和红色区域的偶校验均正确,则说明(2,3,6,7)和(4,5,6,7)均正确,则1位置发生错误
2.黄色区域正确,红色区域错误,则说明(2,3,6,7)正确,(4,5,6,7)中有一个错误,则5位置发生错误
3.红色区域正确,黄色区域错误,则说明(2,3,6,7)中有一个错误,(4,5,6,7)正确,则3位置发生错误
4.黄色和红色区域都错误,则说明(2,3,6,7)中一个错误,(4,5,6,7)中一个错误,则7位置发生错误
同理可得其他各种情况。
由于(1,3,5,7)是由**1负责的,所以若(1,3,5,7)发生错误,则错误位置的二进制的第3位置必然为1,若(2,3,6,7)发生错误,则错误位置的二进制的第2位置必然为1,若(4,5,6,7)发生错误,则错误位置的二进制的第1位置必然为1。
若发生错误,设错误位置对应的二进制为
则(若偶检验成功则为0,失败则为1),同理可得
,最终就能确定发生错误的位置
循环冗余校验码
奇偶校验会在每一个字符信息的首部或尾部添加一个奇偶校验码,对于大量传输的数据进行校验时,会增加大量的额外开销,尤其在网络通信中,传输的数据信息都是二进制比特流,因而没有必要将数据再分解成一个个字符,这样也就无法采用奇偶校验码,因此,通常采用CRC码进行校验。
CRC校验的基本原理
设生成多项式
,信息码为101001
- 增加冗余码 (校验码)
- 生成多项式G(x)
双方约定的一个(r+1)位二进制数,发送方利用G(x)对信息多项式做模2除运算,生成校验码。接收方利用G(x)对收到的编码多项式做模2除运算检测差错及错误定位。
G(x)应满足的条件
1.最高位和最低位必须为1
2.当被传送信息(CRC码)任何一位发生错误时,被生成多项式做除后应该使余数不为0
3.不同位发生错误时,模2除运算后余数不同
4.对不为0余数继续进行模2除运算应使余数循环除2模运算
除2模运算对于每一位,若不够则不用借位,即0-1=1,0-0=0,1-1=0,1-0=1,即异或运算操作。
1.部分余数首位为1时,商为1,减除数
2.部分余数首位为0时,商为0,减0
3.当部分余数的位数小于除数的位数时,该余数即为最后余数 - 移位相除确定CRC码
将信息码左移r位,低位补0,对移位后的信息码,用生成多项式进行模2除运算,产生除数,CRC码为信息码+余数
假设信息码为101001,生成多项式为1101,则余数为001,最终确定CRC码为101001 001 - 检验与纠错
接收方用接收到的信息与约定好的生成多项式进行模2除运算,若余数为0,则正确,不为0则发生错误。
若接收方收到的信息为:101001 001与生成多项式(1101)进行运算得到的余数为0,则正确
若接收方收到的信息为:101001 011与生成多项式(1101)进行运算得到的余数为010,则发生错误,错误位置为2,恰好为010的十进制数。但在这里并不能说所得余数就代表出错位置,如下表接受 余数 出错位 101001000 001 1 101001011 010 2 101001101 011 3 101000001 100 4 101011001 101 5 101101001 110 6 100001001 111 7 111001001 001 8 001001001 010 9 由上表可以看出出错位并不等于余数,但是有一定的联系。余数在001-111中循环出现,因为冗余码只有3位,只能表示8种情况,但是CRC码共有3+6=9位,无法表示完全。由于有r位校验位,即余数有r位,因此余数能表示
种状态,为纠错范围能覆盖所有CRC码,则要求
(1为正确的情况)。
因此当
时,CRC码可以纠错一位,但是由于CRC码通常用于计算机网络,都是几千bit+几个校验位,因此主要为了检错,而非纠错。
黄大牙牙yyds
-
jquery密码强度校验
2020-10-23 05:45:46主要介绍了jquery密码强度校验,这是一个非常常见的功能,在输入密码的时候提示密码的强度,本文使用jQuery来实现,有需要的小伙伴可以参考下。 -
一下子看懂校验码,CRC,海明码
2018-07-09 19:42:05校验码 能够发现错误或者自动纠错的数据编码,也称为检错纠错码。校验码的原理是通过增加一些...三种常见的校验码: 1、奇偶校验码 在首部增加一位二进制位(校验位),称为奇偶校验码,它可以检测出一位错误,... -
几种常见的校验算法
2020-12-31 03:41:34素材来源:网络编辑整理:strongerHuangUART有一个奇偶校验,...下面就介绍几种常见的校验算法。一、校验和校验和是最基本,也是嵌入式工程师最常用的一种校验算法,其实现方法很简单,简单到只有几行代码。实现的... -
计算机网络(18)数据链路层:差错控制(奇偶校验码、循环冗余码、海明编码)
2020-05-07 12:04:471、差错控制简介 概括来说,传输中的差错都是由噪声引起的。噪声有两大类:一类是信道所固有的、持续存在的随机...通常利用编码技术来进行错差控制,主要有两类:自动重传请求(Automatic Retransmission reQuest,... -
计算机系统基础:校验码知识笔记
2020-09-25 05:58:081、校验码概念校验码主要是为了解决计算机各部件进行数据传输和交换,确保传送过程的正确无误,一是为了提高硬件电路的可靠性,二是提高代码的校验能力。通常会用校验码来检查传送的数据是否正确。校... -
校验码——揭开海明校验码求解之谜
2015-09-27 12:43:29引言 计算机系统在运行时,各个部件之间要进行数据交换,为了确保数据在传送过程中正确无误,通常使用校验码的方法来检测传送的数据是否出错。合理的设计错误编码以及编码规则,舍得数据...常见的校验码有中华人民共 -
常见的数据校验方法
2019-08-09 18:00:54校验,是为保护数据的完整性,用一种指定的算法对原始数据计算出的一个校验值。当接收方用同样的算法再算一次校验值,如果两次校验值一样,表示数据完整。 1. 奇偶校验 实现方法:在数据存储和传输中,字节中额外... -
数据链路层差错控制——奇偶校验码、循环冗余码和汉明码(海明码)
2020-02-20 23:38:55通常采用编码技术来进行差错控制,主要有两类:自动重传请求(ARQ)和前向控制(FEC)。在ARQ方式中,接收端只进检错,而不进行纠错。而在FEC方式中,不仅进行检错,还可以检测出二进制数码错... -
网上几种常见校验码图片分析
2006-09-21 09:10:00前几天受刺激了,准备把CSDN的校验码图片修改。就上网找了一些参考示例。和分析了一些校验码的功能。不敢独享,整理到一起,跟大家分享。至于CSDN新的校验码写法,不是这里面的任何一种。也不是网上可以找到的。这个... -
校验方法与校验码
2014-09-26 16:43:03校验方法与校验码 信息编码在计算机内传输、存取过程中,难免会出现一些随机性的错误,例如受到外界干扰导致产生了码元错误,例如把“1”码元变成了“0”码元...常见的信息编码校验方法有奇偶校验法、海明校验法、C -
短码LDPC编码系统的MATLAB仿真
2022-03-27 19:23:34特别在空间通信中,LDPC编码扮演着极其重要的角色,本文主要针对基于空间通信的超短码LDPC编码系统进行了研究,主要研究工作包括如下几个方面: 第一、首先对LDPC编码的基本原理进行介绍,包括几种常见的几种稀疏... -
几种常见的校验方式(学习整理ing)
2017-03-21 09:42:271.什么是数据校验 通俗的说,就是为保证数据的完整性,用一种指定的算法对原始数据计算出的一个校验值。接收方用同样的算法计算一次校验值,如果和随数据提供的校验值一样,就说明数据是完整的。 2.最简单的... -
vant常见的两种校验方式
2022-01-03 20:16:25校验规则我这里主要用了两种校验方式,一种就是正则表达式,还有一种就是用函数进行校验 <van-form validate-first @failed="onFailed"> <van-field v-model="username" name="账号" <!-- 正则... -
CRC校验
2020-12-21 15:07:19CRC循环冗余校验码是数据通信中的一种查错校验码。 原理 CRC 算法的基本思想是将传输的数据[M(X)] 当做一个位数很长的数。将这个数除以另一个数[G(X)] ,得到的余数[R(X)] 作为校验数据附加到原数据后面,组成循环校验... -
常用的数据校验方式(奇偶,CRC,异或校验, LRC校验,累加和,MD5等校验)概念及源码
2021-05-13 15:44:23BCC异或校验法(Block Check Character) 实现方法:将数据按字节与校验码寄存器(寄存器初始值通常0))进行异或计数并存入校验码寄存器。 应用例子:IC卡接口通讯、很多单片机系统的串口通讯都使用。 CRC循环冗余... -
JS常见简单正则表达式验证功能小结【手机,地址,企业税号,金额,身份证等】
2020-10-20 13:56:13主要介绍了JS常见简单正则表达式验证功能,结合实例形式总结分析了JS针对手机,地址,企业税号,金额,身份证等的常见验证技巧,需要的朋友可以参考下 -
使用verilog实现CRC校验
2021-02-23 15:02:35我是桂林理工大三苦逼通信学子?...循环冗余校验码简称CRC(循环码),是一种能力相当强的检错、纠错码,并且实现编码和检码的电路比较简单,常用于串行传送的辅助存储器与主机的数据通信和计算机网络 -
web系统中常见的密码加密方式
2020-02-04 16:32:37web系统中通常都会有登录的功能,登录功能的逻辑是这样的:一个用户拥有一个用户名为zhangsan,密码为123456的账号,在登录时,前端去调用后端的登录接口,并传入zhangsan与123456作为参数;在后端代码内容运行时,... -
如何在 Spring/Spring Boot 中优雅地做参数校验?拒绝 if/else 参数校验 !
2021-04-23 10:55:54数据的校验的重要性就不用说了,即使在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据。 最普通的做法就像下面这样... -
常用的数据校验方法
2019-07-15 15:14:281.什么是数据校验 通俗的说,就是为保证数据的完整性,用一种指定的算法对原始数据计算出的一个校验值。接收方用同样的算法计算一次校验值,如果和随数据提供的校验值一样,就说明数据是完整的。 2.最简单的检验 ... -
常见密码和编码总结 CTF中Crypto和Misc必备
2020-11-20 13:00:40对常见的编码和密码做个归纳,并记录一些可用的网站和工具,可以当做手册使用 -
检错码与纠错码,一码归一码
2020-05-25 22:37:04检错码与纠错码,一码归一码 写在前面: 为什么需要差错处理 任何信道,即使是光纤,也会出错。 差错的类型 单个错误:分散在各块中 突发错误:集中在某个块中 注:突发错误比单个错误更加难于处理,通常利用处理单个...