汉明码(Hamming Code),是在电信领域的一种线性调试码,以发明者理查德·卫斯里·汉明的名字命名。汉明码在传输的消息流中插入验证码,以侦测并更正单一比特错误。由于汉明编码简单,它们被广泛应用于内存(RAM)。其SECDED(single error correction, double error detection)版本另外加入一检测比特,可以侦测两个或以下同时发生的比特错误,并能够更正单一比特的错误。因此,当发送端与接收端的比特样式的汉明距离 (Hamming distance) 小于或等于1时(仅有 1 bit 发生错误),可实现可靠的通信。相对的,简单的奇偶检验码除了不能纠正错误之外,也只能侦测出奇数个的错误。
2^k >= n +k +1 (这货好像叫汉明不等式什么的,反正大家都懂)
n是需要编码的位数,需要的校验位的最小数量是满足这个不等式的最小的K
1. 新建从1开始的数据位的位置序号1,2,3,4,5….
2. 将这些数据位的位置序号转换为二进制, 1, 10, 11, 100, 101, 等.
3. 数据位的位置序号中所有为二的幂次方的位(编号1,2,4,8,等,即数据位位置序号的二进制表示中只有一个1)是校验位
(注释:任何二进制数可以写为如下组合形式,以10100为例:
10100 = 1*10000 + 0*01000 +1*00100 + 0*000010 +0*00001, 而这里的10000,01000,00100,00010,00001 即为二进制数的 8,4,2,1,对应下表中的数据位位置。可以把它想象成向量空间的基,也就不难理解为什么把他们选作校验位)
4. 所有其它位置的数据位(数据位位置序号的二进制表示中至少2个是1)是数据位
每一位的数据包含在特定的两个或两个以上的校验位中,这些校验位取决于这些数据位的位置数值的二进制表示
(如下图中 对于数据位位置 3,3=2 + 1,即 0011 = 0010 +0001, 所以 3 所对应的 数据位d1 即可用位置分别问1和 2 的 p1,p2 来检验)
5. 接下来 p1 = d1 xor d2 xor d4 xor d5….. p2 = d1 xor d3 xor d4 xor d6 xor d7…..
(这个就不要解释了吧,好绕嘴)
6. 然后按数据位位置的顺序写出所得编码,p1p2d1p4d2d3….
即为下图中第二行
关于汉明码的纠错过程
假设我们接收到的汉明码为0110101,我们要判断此码时候正确。先不看此码所给的校验位,我们来计算1101,即去掉校验位后的数据,来计算我们正确的校验位,方法就如上诉的那几个步骤。
这时我们算出p1= 3 xor 5 xor 7= d1 xor d2 xor d4 = 1
P2 = 3 xor 6 xor 7 = d2 xord3 xor d4 = 0
P4 = 5 xor 6 xor 7 = d2 xord3 xor d4 = 0
而我们收的校验码分别是p1 = 0, p2 = 1, p4 = 0
我们将我们算出的校验码和收到的校验码进行xor结果赋值给p1,p2,p4
P1 = 1 xor 0 = 1
P2 = 1 xor 0 = 1
P4 = 0 xor 0 = 0
P4p2p1= 011 = 6
所以第六位是错误的,可纠正为0100101,故欲传送的信息为0101
如何理解这里的p1=1,p2=1,p4=0呢?
我们的结果告诉我们p1和p2不正确,但是p3是正确的,所以我们要找出仅由p1和p2校验的位置,这个位置就是第三位,也就是d1。
为什么是p4p2p1这个顺序?
正如我们前面所提到的,p1所对应的标号为0001,p2 是 0010,p4是0100,所以二进制表示自然是p4p2p1的顺序了
下文示例为(30,24)汉明码计算方法,用在MIPI DSI包头部分(MIPI Alliance Specification for Display Serial Interface,Chapter 9),DSI包头格式固定为24bits Data+8bits ECC,8bitsECC中预设P6=P7=0,所以实际n=30,m=24,k=6。
检验位计算方法参考MIPI DSI table22生成矩阵(Px vs [DataBit0~DataBitx]),比如P5=D10^D11^....D23,表示对应DataBit23的P5列,只有这些DataBit位为1。
int main() { char res; char in[20]={0}; char D0,D1,D2,D3,D4,D5,D6,D7,D8,D9,D10,D11,D12,D13,D14,D15,D16,D17,D18,D19,D20,D21,D22,D23; char P0,P1,P2,P3,P4,P5,P6,P7; cout<<"Checking Codes(eg.0x1234AF, \"-\" for exit): 0x"; cin>>in; if(in[0]=='-') { return 0; } for(int i=0;i<6;i++){ if((in[i]>='0') && (in[i]<='9')) { in[i] = in[i]-0x30; }else if((in[i]>='A') && (in[i]<='F')){ in[i] = in[i]-'A'+10; }else { return 0; } } D0=in[1]&0x01; D1=(in[1]&0x02)>>1; D2=(in[1]&0x04)>>2; D3=(in[1]&0x08)>>3; D4=in[0]&0x01; D5=(in[0]&0x02)>>1; D6=(in[0]&0x04)>>2; D7=(in[0]&0x08)>>3; D8=in[3]&0x01; D9=(in[3]&0x02)>>1; D10=(in[3]&0x04)>>2; D11=(in[3]&0x08)>>3; D12=in[2]&0x01; D13=(in[2]&0x02)>>1; D14=(in[2]&0x04)>>2; D15=(in[2]&0x08)>>3; D16=in[5]&0x01; D17=(in[5]&0x02)>>1; D18=(in[5]&0x04)>>2; D19=(in[5]&0x08)>>3; D20=in[4]&0x01; D21=(in[4]&0x02)>>1; D22=(in[4]&0x04)>>2; D23=(in[4]&0x08)>>3; P7=0; P6=0; P5=D10^D11^D12^D13^D14^D15^D16^D17^D18^D19^D21^D22^D23; P4=D4^D5^D6^D7^D8^D9^D16^D17^D18^D19^D20^D22^D23; P3=D1^D2^D3^D7^D8^D9^D13^D14^D15^D19^D20^D21^D23; P2=D0^D2^D3^D5^D6^D9^D11^D12^D15^D18^D20^D21^D22; P1=D0^D1^D3^D4^D6^D8^D10^D12^D14^D17^D20^D21^D22^D23; P0=D0^D1^D2^D4^D5^D7^D10^D11^D13^D16^D20^D21^D22^D23; res = ((P7&0x01)*8+(P6&0x01)*4+(P5&0x01)*2+(P4&0x01))*16+(P3&0x01)*8+(P2&0x01)*4+(P1&0x01)*2+(P0&0x01); printf("Result:0x%02X\r\n",res); return 0;}