海明纠错码工作原理

海明纠错码

海明码(Hamming Code)是一个可以有多个校验位,具有检测并纠正一位错误代码的纠错码,所以它也仅用于信道特性比较好的环境中,如以太局域网中,因为如果信道特性不好的情况下,出现的错误通常不是一位。

海明码的检错、纠错基本思想是将有效信息按某种规律分成若干组,每组安排一个校验位进行奇偶性测试,然后产生多位检测信息,并从中得出具体的出错位置,最后通过对错误位取反(也是原来是1就变成0,原来是0就变成1)来将其纠正。

要采用海明码纠错,需要按以下步骤来进行:

  1. 计算校验位数
  2. 确定校验码位置
  3. 确定校验码
  4. 实现校验和纠错

摘自 茶乡浪子

1. 计算校验位数

要使用海明码纠错,首先就要确定发送的数据所需要要的校验码(也就是“海明码”)位数(也称“校验码长度”)。它是这样的规定的:假设用N表示添加了校验码位后整个信息的二进制位数,用K代表其中有效信息位数,r表示添加的校验码位,它们之间的关系应满足:

N = K + r ≤ 2 r - 1 N=K+r \le 2r-1 N=Kr2r1

如K=5,则要求 2 r − r ≥ 5 + 1 = 6 2r-r≥5+1=6 2rr5+1=6,根据计算可以得知r的最小值为4,也就是要校验5位信息码,则要插入4位校验码。如果信息码是8位,则要求 2 r − r ≥ 8 + 1 = 9 2r-r \ge 8+1=9 2rr8+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.确定校验码位置

上一步我们确定了对应信息中要插入的校验码位数,但这还不够,因为这些校验码不是直接附加在信息码的前面、后面或中间的,而是分开插入到不同的位置。但不用担心,校验码的位置很容易确定的,那就是校验码必须是在 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,因现在还没有求出各位校验码值,现在这些校验码位都用“?”表示,最终的码字为:??10011101

3. 确定校验码

这些校验码的值不是随意的,每个校验位的值代表了代码字中部分数据位的奇偶性(最终要根据是采用奇校验,还是偶校验来确定),其所在位置决定了要校验的比特位序列。总的原则是:第i位校验码从当前位开始,每次连续校验 2 n − 1 2^{n-1} 2n1位后再跳过i位,然后再连续校验 2 n − 1 2^{n-1} 2n1位,再跳过 2 n − 1 2^{n-1} 2n1位,以此类推。最后根据所采用的是奇校验,还是偶校验即可得出第 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

3.1 计算方法1

对每组数字位通过奇偶校验的方式来依次确定校验位的值。比如使用偶校验,校验位的值使用每组中1的个数为偶数,对组内已有数字进行异或运算得到校验位的值

3.1 计算方法2

对11000010进行汉明编码,求编码后的码字。

  1. 列出表格,从左往右(或从右往左)填入数字,但2的次方的位置不填。

    位置 1 2 3 4 5 6 7 8 9 10 11 12 13 14
    1 1 0 0 0 0 1 0
  2. 把数据行有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
  3. 收集所有二进制数字,求异或。 0011 ⊕ 0101 ⊕ 1011 = 1101 0011\oplus 0101 \oplus 1011=1101 001101011011=1101

  4. 把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
  5. 所以编码后的码字是101110010010。

4. 校验与纠错

把以上这些校验码所校验的位分成对应的组,则在接收端的对各校验位再进行逻辑“异或运算”,如果采用的是偶校验,正常情况下均为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+10p2+100p3+1000p4的位置处

5. 简单的算法实现

#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");

}

没找到很好的算法实现源码,不知道有没有开源,如有开源的请留言

你可能感兴趣的:(ACM&算法)