海明码(Hamming Code)是一个可以有多个校验位,具有检测并纠正一位错误代码的纠错码,所以它也仅用于信道特性比较好的环境中,如以太局域网中,因为如果信道特性不好的情况下,出现的错误通常不是一位。
海明码的检错、纠错基本思想是将有效信息按某种规律分成若干组,每组安排一个校验位进行奇偶性测试,然后产生多位检测信息,并从中得出具体的出错位置,最后通过对错误位取反(也是原来是1就变成0,原来是0就变成1)来将其纠正。
要采用海明码纠错,需要按以下步骤来进行:
摘自 茶乡浪子
要使用海明码纠错,首先就要确定发送的数据所需要要的校验码(也就是“海明码”)位数(也称“校验码长度”)。它是这样的规定的:假设用N表示添加了校验码位后整个信息的二进制位数,用K代表其中有效信息位数,r表示添加的校验码位,它们之间的关系应满足:
N = K + r ≤ 2 r - 1 N=K+r \le 2r-1 N=K+r≤2r-1
如K=5,则要求 2 r − r ≥ 5 + 1 = 6 2r-r≥5+1=6 2r−r≥5+1=6,根据计算可以得知r的最小值为4,也就是要校验5位信息码,则要插入4位校验码。如果信息码是8位,则要求 2 r − r ≥ 8 + 1 = 9 2r-r \ge 8+1=9 2r−r≥8+1=9,根据计算可以得知r的最小值也为4。
信息码位数与校验码位数之间的关系
信息码位数 | 1 | 2~4 | 5~11 | 12~26 | 27~57 | 58~120 | 121~247 |
---|---|---|---|---|---|---|---|
校验码位数 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
上一步我们确定了对应信息中要插入的校验码位数,但这还不够,因为这些校验码不是直接附加在信息码的前面、后面或中间的,而是分开插入到不同的位置。但不用担心,校验码的位置很容易确定的,那就是校验码必须是在 2 n 2^n 2n 次方位置,如第1、2、4、8、16、32,……位(对应20、21、22、23、24、25,……,是从最左边的位数起的),这样一来就知道了信息码的分布位置,也就是非 2 n 2^n 2n 次方位置,如第3、5、6、7、9、10、11、12、13,……位(是从最左边的位数起的)。
举一个例子,假设现有一个8位信息码,即b1、b2、b3、b4、b5、b6、b7、b8,它需要插入4位校验码,即p1、p2、p3、p4,也就是整个经过编码后的数据码(称之为“码字”)共有12位。根据以上介绍的校验码位置分布规则可以得出,这12位编码后的数据就是p1、p2、b1、p3、b2、b3、b4、p4、b5、b6、b7、b8。
现假设原来的8位信息码为10011101,因现在还没有求出各位校验码值,现在这些校验码位都用“?”表示,最终的码字为:??1?001?1101。
这些校验码的值不是随意的,每个校验位的值代表了代码字中部分数据位的奇偶性(最终要根据是采用奇校验,还是偶校验来确定),其所在位置决定了要校验的比特位序列。总的原则是:第i位校验码从当前位开始,每次连续校验 2 n − 1 2^{n-1} 2n−1位后再跳过i位,然后再连续校验 2 n − 1 2^{n-1} 2n−1位,再跳过 2 n − 1 2^{n-1} 2n−1位,以此类推。最后根据所采用的是奇校验,还是偶校验即可得出第 n n n位校验码的值。如下表示意:原文
数据位位置 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ... | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
编码后数据位置 | p1 | p2 | d1 | p4 | d2 | d3 | d4 | p8 | d5 | d6 | d7 | d8 | d9 | d10 | d11 | p16 | d12 | d13 | d14 | d15 | ||
奇偶校验位 覆盖率 |
p1 | X | X | X | X | X | X | X | X | X | X | |||||||||||
p2 | X | X | X | X | X | X | X | X | X | X | ||||||||||||
p4 | X | X | X | X | X | X | X | X | X | |||||||||||||
p8 | X | X | X | X | X | X | X | X | ||||||||||||||
p16 | X | X | X | X | X |
对每组数字位通过奇偶校验的方式来依次确定校验位的值。比如使用偶校验,校验位的值使用每组中1的个数为偶数,对组内已有数字进行异或运算得到校验位的值
对11000010进行汉明编码,求编码后的码字。
列出表格,从左往右(或从右往左)填入数字,但2的次方的位置不填。
位置 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
据 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
把数据行有1的列的位置写为二进制。
位置 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
据 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | ||||||
进制 | 0011 | 0101 | 1011 |
收集所有二进制数字,求异或。 0011 ⊕ 0101 ⊕ 1011 = 1101 0011\oplus 0101 \oplus 1011=1101 0011⊕0101⊕1011=1101
把1101依次填入表格中2的次方的位置(低位在左)。
位置 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
据 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | ||||||
进制 | 0011 | 0101 | 1011 | |||||||||||
验 | 1 | 0 | 1 | 1 |
所以编码后的码字是101110010010。
把以上这些校验码所校验的位分成对应的组,则在接收端的对各校验位再进行逻辑“异或运算”,如果采用的是偶校验,正常情况下均为0。
如果最终发现只是一个校验组中的校验结果不符,则直接可以知道是对应校验组中的校验码在传输过程中出现了差错,因为所有校验码所在的位是只由对应的校验码进行校验;如果发现多组校验结果不正确,则查看这些组中公共校验的数据位(只有数据位才可能被几个校验码进行校验),以最终确定是哪个数据位出了差错(海明码只能检查一位出错);最后,对所找到的出错数据位取反即可实现纠错。
如计算出的每组的校验结果为p1、p2、p3、p4,均为0则正确,有一个不为0的则出错的位置在 p 1 + 10 ∗ p 2 + 100 ∗ p 3 + 1000 ∗ p 4 p1+10*p2+100*p3+1000*p4 p1+10∗p2+100∗p3+1000∗p4的位置处
#include
using namespace std;
namespace Encode
{
// 参考 https://zh.wikipedia.org/wiki/%E6%B1%89%E6%98%8E%E7%A0%81
/**
* @brief encode a [5-11]-bit int to hamming code
* @param inCode IN as orginal code
* @param inLen IN as the length infomation
* @param outCode OUT as the encoded int
* @param outLen OUT as the length
* @return none
*/
bool Hamming26(int const inCode, int const inLen, int& outCode, int& outLen)
{
if (inLen <= 5 || inLen >= 11)
{
return false;
}
int checkNum = 4; // 校验码位
outLen = checkNum + inLen;
const int dataBitLoc[] = { 2, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14 };
const int checkBitLoc[] = { 0, 1, 3, 7 };
int checkSum = 0;
for (int i = 0; i < inLen; ++i)
{
int dataBit = ((inCode >> i) & 0x1);
if (dataBit)
{
outCode |= (dataBit << dataBitLoc[i]);
checkSum ^= (dataBitLoc[i] + 1);
}
}
cout << checkSum << endl;
for (int i = 0; i < checkNum; ++i)
{
outCode |= (((checkSum >> i) & 0x1) << checkBitLoc[i]);
}
return true;
}
// 参考http://www.cnblogs.com/scrutable/p/6052127.html
/**
* @brief check the correctness of a hamming code
* @param code received code
* @param length the length of the code
* @return return -1 if no error, or return the position of the error bit
*/
bool CheckCode(int code, int length, int& errLoc)
{
if (length <= 9 || length >= 15)
{
return false;
}
errLoc = 0;
int step[4] = { 1, 2, 4, 8 };
for (int i = 0; i < 4; ++i)
{
int stepNum = 0;
int checkCode = 0;
for (int j = step[i] - 1; j < length;)
{
checkCode ^= ((code >> j) & 0x1);
++stepNum;
if (stepNum % step[i] == 0)
{
j += (step[i] + 1);
}
else
{
++j;
}
}
errLoc |= ((checkCode&0x1) << i);
}
return true;
}
}
int main()
{
int code = 157;
int length = 8;
int result, reLen;
Encode::Hamming26(code, length, result, reLen);
cout << "The orignal code " << code << "[" << length << "] ";
// display from LSB to MSB
for (int i = 0; i < length; ++ i)
{
cout << ((code & (1 << i)) ? 1 : 0);
}
cout << endl;
cout << "the hamming code " << result << "[" << reLen << "] ";
for (int i = 0; i < reLen; ++ i)
{
cout << ((result & (1 << i)) ? 1 : 0);
}
cout << endl;
cout << "now to test the result " << endl;
// test and indicate which bit is wrong
// notice the binary number should be reversed
// when you want to know the decimal value
for (int i = 0; i < reLen; ++ i)
{
int test = result & ~(1 << i);// result & ( 1 << i);
int errLoc;
Encode::CheckCode(test,reLen, errLoc);
cout << test << " " << errLoc;
cout << ' ';
}
cout << endl;
return 0;
}
一个开源的C demo
// ------------------------------------------------------------------------
// File: hamming.c
// Date: August 7, 2000
//
// Encoding and decoding of a Hamming code.
// ------------------------------------------------------------------------
// This program is complementary material for the book:
//
// R.H. Morelos-Zaragoza, The Art of Error Correcting Coding, Wiley, 2002.
//
// ISBN 0471 49581 6
//
// This and other programs are available at http://the-art-of-ecc.com
//
// You may use this program for academic and personal purposes only.
// If this program is used to perform simulations whose results are
// published in a journal or book, please refer to the book above.
//
// The use of this program in a commercial product requires explicit
// written permission from the author. The author is not responsible or
// liable for damage or loss that may be caused by the use of this program.
//
// Copyright (c) 2002. Robert H. Morelos-Zaragoza. All rights reserved.
// ------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#define MAX_RANDOM LONG_MAX // Maximum value of random()
int i,j,l,index;
int n, k;
int code[1024];
int red[1024], info[1024];
int m;
int parity[10];
int syn;
int error;
int test, result;
main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Usage: %s m position_error\n", argv[0]);
exit(0);
}
sscanf(argv[1],"%d", &m);
sscanf(argv[2],"%d", &error);
n = pow(2,m)-1;
k = n - m;
// Compute parity positions
parity[1] = 1;
for (i=2; i<=m; i++)
parity[i] = (parity[i-1]<<1) & 0xfffffffe;
printf("parity positions: ");
for (i=1; i<=m; i++) printf("%2d ", parity[i]); printf("\n");
// Generate random message
for (i=1; i<=k; i++)
info[i] = ( random() >> 10) & 0x01;
printf("information bits = ");
for (j=1; j<=k; j++) printf("%1d", info[j]);
printf("\n");
// Compute parity bits
for (j=1; j<=m; j++)
{
red[j] = 0;
l = 0;
for (i=1; i<=n; i++)
{
// Check that "i" is not a parity position = not a power of 2
result = 0;
test = 1;
for (index=1; index<=m; index++)
{
if (i==test) result = 1;
test *= 2;
}
if (!result)
{
l++;
if ( (i>>(j-1)) & 0x01 )
red[j] ^= info[l];
}
}
}
printf("parity bits = ");
for (j=1; j<=m; j++) printf("%1d", red[j]);
printf("\n");
// Transmit codeword
i = 1;
l = 1;
for (j=1; j<=n; j++)
if (j==parity[l] && l<=m)
{
code[j] = red[l]; l++;
}
else
{
code[j] = info[i]; i++;
}
printf("codeword = ");
for (j=1; j<=n; j++) printf("%1d", code[j]);
printf("\n");
// Add a hard error
code[error] ^= 1;
printf("received = ");
for (j=1; j<=n; j++) printf("%1d", code[j]);
printf("\n");
// Compute syndrome
syn = 0;
for (i=1; i<=n; i++)
if (code[i]) syn ^= i;
printf("syndrome = %d\n", syn);
// Correct error if needed
if (syn)
code[syn] ^= 1;
printf("estimate = ");
for (j=1; j<=n; j++) printf("%1d", code[j]);
printf("\n");
}
没找到很好的算法实现源码,不知道有没有开源,如有开源的请留言