1. 程式人生 > >迴圈冗餘校驗碼(CRC)應用總結(包括C++原始碼)

迴圈冗餘校驗碼(CRC)應用總結(包括C++原始碼)

 最近在實習期間需要用到資料的校驗,所選為CRC16,那麼就在此總結一番吧。

現在此說明下什麼是CRC:迴圈冗餘碼校驗 英文名稱為Cyclical Redundancy Check,簡稱CRC,它是利用除法及餘數的原理來作錯誤偵測(Error Detecting)的。實際應用時,傳送裝置計算出CRC值並隨資料一同傳送給接收裝置,接收裝置對收到的資料重新計算CRC並與收到的CRC相比較,若兩個CRC值不同,則說明資料通訊出現錯誤

那麼其實CRC有比較多種,比如CRC16、CRC32 ,為什麼叫16、32呢。在這裡並非與位有和關係。而是由所確定的多項式最高次冪確定的。如下所示。理論上講冪次越高校驗效果越好。

  CRC(12位) =X12+X11+X3+X2+X+1

  CRC(16位) = X16+X15+X2+1

  CRC(CCITT) = X16+X12 +X5+1

  CRC(32位) = X32+X26+X23+X16+X12+X11+X10+ X8+X7+X5+X4+X2+X+1

迴圈冗餘校驗碼(CRC)的基本原理是:在K位資訊碼後再拼接R位的校驗碼,整個編碼長度為N位,因此,這種編碼又叫(N,K)碼。對於一個給定的(N,K)碼,可以證明存在一個最高次冪為N-K=R的多項式G(x)。根據G(x)可以生成K位資訊的校驗碼,而G(x)叫做這個CRC碼的生成多項式。

校驗碼的具體生成過程為:假設傳送資訊用資訊多項式C(X)表示,將C(x)左移R位,則可表示成C(x)*2R,這樣C(x)的右邊就會空出R位,這就是校驗碼的位置。通過C(x)*2R除以生成多項式G(x)得到的餘數就是校驗碼。

幾個基本概念

1、多項式與二進位制數碼

多項式和二進位制數有直接對應關係:x的最高冪次對應二進位制數的最高位,以下各位對應多項式的各冪次,有此冪次項對應1,無此冪次項對應0。可以看出:x的最高冪次為R,轉換成對應的二進位制數有R+1位。

多項式包括生成多項式G(x)和資訊多項式C(x)。

如生成多項式為G(x)=x4+x3+x+1, 可轉換為二進位制數碼11011。

而傳送資訊位 1111,可轉換為資料多項式為C(x)=x3+x2+x+1。

2、生成多項式

是接受方和傳送方的一個約定,也就是一個二進位制數,在整個傳輸過程中,這個數始終保持不變。

在傳送方,利用生成多項式對資訊多項式做模2除生成校驗碼。在接受方利用生成多項式對收到的編碼多項式做模2除檢測和確定錯誤位置。

應滿足以下條件:

a、生成多項式的最高位和最低位必須為1。

b、當被傳送資訊(CRC碼)任何一位發生錯誤時,被生成多項式做模2除後應該使餘數不為0。

c、不同位發生錯誤時,應該使餘數不同。

d、對餘數繼續做模2除,應使餘數迴圈。

將這些要求反映為數學關係是比較複雜的。但可以從有關資料查到常用的對應於不同碼制的生成多項式如圖9所示:

N           K           碼距d           G(x)多項式           G(x)

7           4           3           x3+x+1           1011

7           4           3           x3+x2+1           1101

7           3           4           x4+x3+x2+1           11101

7           3           4           x4+x2+x+1           10111

15           11           3           x4+x+1           10011

15           7           5           x8+x7+x6+x4+1           111010001

31           26           3           x5+x2+1           100101

31           21           5           x10+x9+x8+x6+x5+x3+1           11101101001

63           57           3           x6+x+1           1000011

63           51           5           x12+x10+x5+x4+x2+1           1010000110101

1041           1024                       x16+x15+x2+1           11000000000000101

圖9 常用的生成多項式

3、模2除(按位除)

模2除做法與算術除法類似,但每一位除(減)的結果不影響其它位,即不向上一位借位。所以實際上就是異或。然後再移位移位做下一位的模2減。步驟如下:

a、用除數對被除數最高几位做模2減,沒有借位。

b、除數右移一位,若餘數最高位為1,商為1,並對餘數做模2減。若餘數最高位為0,商為0,除數繼續右移一位。

c、一直做到餘數的位數小於除數時,該餘數就是最終餘數。

【例】1111000除以1101:

1011———商

————

1111000-----被除數

1101———— 除數

————

010000

1101

————

01010

1101

————

111————餘數

CRC碼的生成步驟

1、將x的最高冪次為R的生成多項式G(x)轉換成對應的R+1位二進位制數。

2、將資訊碼左移R位,相當與對應的資訊多項式C(x)*2R

3、用生成多項式(二進位制數)對資訊碼做模2除,得到R位的餘數。

4、將餘數拼到資訊碼左移後空出的位置,得到完整的CRC碼。

【例】假設使用的生成多項式是G(x)=x3+x+1。4位的原始報文為1010,求編碼後的報文。

解:

1、將生成多項式G(x)=x3+x+1轉換成對應的二進位制除數1011。

2、此題生成多項式有4位(R+1),要把原始報文C(x)左移3(R)位變成1010000

3、用生成多項式對應的二進位制數對左移4位後的原始報文進行模2除:

1001-------商

------------------------

1010000

1011----------除數

------------

1000

1011

------------

011-------餘數(校驗位)

5、編碼後的報文(CRC碼):

1010000

+          011

------------------

1010011

CRC的和糾錯

在接收端收到了CRC碼後用生成多項式為G(x)去做模2除,若得到餘數為0,則碼字無誤。若如果有一位出錯,則餘數不為0,而且不同位出錯,其餘數也不同。可以證明,餘數與出錯位的對應關係只與碼制及生成多項式有關,而與待測碼字(資訊位)無關。圖10給出了G(x)=1011,C(x)=1010的出錯模式,改變C(x)(碼字),只會改變表中碼字內容,不改變餘數與出錯位的對應關係。

            收到的CRC碼字           餘數           出錯位

碼位           A7           A6           A5           A4           A3           A2           A1

正確           1           0           1           0           0           1           1

           000           無

錯 誤           1           0           1           0           0           1           0

1           0           1           0           0           0           1

1           0           1           0           1           1           1

1           0           1           1           0           1           1

1           0           0           0           0           1           1

1           1           1           0           0           1           1

0           0           1           0           0           1           1

           001010100011110111101           1234567

圖10 (7,4)CRC碼的出錯模式(G(x)=1011)

如果迴圈碼有一位出錯,用G(x)作模2除將得到一個不為0的餘數。如果對餘數補0繼續除下去,我們將發現一個有趣的結果;各次餘數將按圖10順序迴圈。例如第一位出錯,餘數將為001,補0後再除,第二次餘數為010,以後依次為100,0ll…,反覆迴圈,這就是“迴圈碼”名稱的由來。這是一個有價值的特點。如果我們在求出餘數不為0後,一邊對餘數補0繼續做模2除,同時讓被檢測的校驗碼字迴圈左移。圖10說明,當出現餘數(101)時,出錯位也移到A7位置。可通過異或門將它糾正後在下一次移位時送回A1。這樣我們就不必像海明校驗那樣用譯碼電路對每一位提供糾正條件。當位數增多時,迴圈碼校驗能有效地降低硬體代價,這是它得以廣泛應用的主要原因。

通訊與網路中常用的CRC

在資料通訊與網路中,通常k相當大,由一千甚至數千資料位構成一幀,而後採用CRC碼產生r位的校驗位。它只能檢測出錯誤,而不能糾正錯誤。一般取r=16,標準的16位生成多項式有CRC-16=x16+x15+x2+1 和 CRC-CCITT=x16+x15+x2+1。

一般情況下,r位生成多項式產生的CRC碼可檢測出所有的雙錯、奇數位錯和突發長度小於等於r的突發錯以及(1-2-(r-1))的突發長度為r+1的突發錯和(1-2-r)的突發長度大於r+1的突發錯。例如,對上述r=16的情況,就能檢測出所有突發長度小於等於16的突發錯以及99.997%的突發長度為17的突發錯和99.998%的突發長度大於17的突發錯。所以CRC碼的檢錯能力還是很強的。這裡,突發錯誤是指幾乎是連續發生的一串錯,突發長度就是指從出錯的第一位到出錯的最後一位的長度(但是,中間並不一定每一位都錯)。

【例1】某迴圈冗餘碼(CRC)的生成多項式 G(x)=x3+x2+1,用此生成多項式產生的冗餘位,加在資訊位後形成 CRC 碼。若傳送資訊位 1111 和 1100 則它的 CRC 碼分別為_A_和_B_。由於某種原因,使接收端收到了按某種規律可判斷為出錯的 CRC 碼,例如碼字_C_、_D_、和_E_。(1998年試題11)

供選擇的答案

A:① lllll00           ② 1111101           ③ 1111110           ④ 1111111

B:① 1100100           ② 1100101           ③ 1100110           ④ 1100111

C~E:① 0000000           ② 0001100           ③ 0010111        

         ⑤ 1000110           ⑥ 1001111           ⑦ 1010001           ⑧ 1011000

解:

A:G(x)=1101,C(x)=1111 C(x)*23÷G(x)=1111000÷1101=1011餘111

得到的CRC碼為1111111

B:G(x)=1101,C(x)=1100 C(x)*23÷G(x)=1100000÷1101=1001餘101

得到的CRC碼為1100101

C~E:

分別用G(x)=1101對①~⑧ 作模2除: ① 0000000÷1101 餘000     ② 1111101÷1101 餘001

③ 0010111÷1101 餘000     ④ 0011010÷1101 餘000     ⑤ 1000110÷1101 餘000

⑥ 1001111÷1101 餘100     ⑦ 1010001÷1101 餘000     ⑧ 1011000÷1101 餘100

所以_C_、_D_和_E_的答案是②、⑥、⑧

【例2】計算機中常用的一種檢錯碼是CRC,即 _A_ 碼。在進行編碼過程中要使用 _B_ 運算。假設使用的生成多項式是 G(X)=X4+X3+X+1, 原始報文為11001010101,則編碼後的報文為 _C_ 。CRC碼 _D_ 的說法是正確的。

在無線電通訊中常採用它規定碼字長為7位.並且其中總有且僅有3個“1”。這種碼的編碼效率為_E_。

供選擇的答案:

A:① 水平垂直奇偶校驗                        ② 迴圈求和                           ③ 迴圈冗餘                           ④正比率

B:① 模2除法                        ②定點二進位制除法                      ③二-十進位制除法                    ④迴圈移位法

C:① 1100101010111              ② 110010101010011         ③ 110010101011100         ④ 110010101010101

D:① 可糾正一位差錯                                                   ②可檢測所有偶數位錯

③ 可檢測所有小於校驗位長度的突發錯                     ④可檢測所有小於、等於校驗位長度的突發錯

E:① 3/7          ② 4/7          ③ log23/log27        ④ (log235)/7

解:從前面有關CRC的論述中可得出:     A:③ 迴圈冗餘     B:① 模2除法

C:G(x)=11011,C(x)=11001010101,C(x)*24÷G(x)=110010101010000÷11011 餘0011

得到的CRC碼為② 110010101010011

D:從前面有關通訊與網路中常用的CRC的論述中可得出:④ 可檢測所有小於、等於校驗位長度的突發錯

E:定比碼又叫定重碼,是奇偶校驗的推廣。在定比碼中,奇數或偶數的性質保持不變,然而附加一種限制,每個字中1的總數是固定的。隨用途之不同,定比碼要求的附加校驗位可能多於一個,但較之單一的奇偶校驗將增加更多的檢錯能力。

所謂7中取3定比碼,就是整個碼字長度為7位,其中1的位數固定為3。所有128個7位程式碼(0000000~1111111)中只有1的位數固定為3的才是其合法碼字。可以用求組合的公式求出其合法碼字數為:C73=7!/(3!*(7-3)!)=7*6*5/(1*2*3)=35

編碼效率=合法碼字所需位數/碼字總位數=(log235)/7

而對於CRC的實現有兩種方式,分別為多項式和查表法

下面先講講多餘多項式的實現,附程式碼如下

/*  
 *  函式名:GetCrc32  
 *  函式原型:unsigned int GetCrc32(char* InStr,unsigned int len)  
 *      引數:InStr  ---指向需要計算CRC32值的字串  
 *          len  ---為InStr的長度  
 *      返回值為計算出來的CRC32結果。  
 *  
 *  函式名:GetCrc16  
 *  函式原型:unsigned short GetCrc16(char* InStr,unsigned int len)  
 *      引數:InStr  ---指向需要計算CRC32值的字串  
 *          len  ---為InStr的長度  
 *      返回值為計算出來的CRC32結果。  
 *  
 *    2009/03/26   Edit By iawen  
 *  
 */  
  
unsigned int GetCrc32(char* InStr,unsigned int len){      
  //生成Crc32的查詢表   
  unsigned int Crc32Table[256];    
  int i,j;      
  unsigned int Crc;      
  for (i = 0; i < 256; i++){      
    Crc = i;      
    for (j = 0; j < 8; j++){      
      if (Crc & 1)      
        Crc = (Crc >> 1) ^ 0xEDB88320;      
      else     
        Crc >>= 1;    
    }      
    Crc32Table[i] = Crc;      
  }      
  
  //開始計算CRC32校驗值   
  Crc=0xffffffff;      
  for(int i=0; i<len; i++){        
    Crc = (Crc >> 8) ^ Crc32Table[(Crc & 0xFF) ^ InStr[i]];      
  }   
     
  Crc ^= 0xFFFFFFFF;   
  return Crc;      
}      
  
unsigned short GetCrc16(char* InStr,unsigned int len){      
  //生成Crc16的查詢表   
  unsigned short Crc16Table[256];    
  unsigned int i,j;   
  unsigned short Crc;      
  for (i = 0; i < 256; i++)
  {     
    Crc = i;      
    for (j = 0; j < 8; j++)
	{      
      if(Crc & 0x1)      
        Crc = (Crc >> 1) ^ 0xA001;      
      else     
        Crc >>= 1;    
    }      
    Crc16Table[i] = Crc;      
  }   
     
  //開始計算CRC16校驗值   
  Crc=0x0000;        
  for(i=0; i<len; i++){        
    Crc = (Crc >> 8) ^ Crc16Table[(Crc & 0xFF) ^ InStr[i]];     
  
  }   
  //Crc ^= 0x0000;     
  return Crc;      
}    
#include<iostream>     
using namespace std;   
  
int main()
{   
  char str[]="iawen";   
  unsigned int crc;
  //crc 32校驗
  crc=GetCrc32(str,5);   
     
  printf("%08X\n",crc);   
  //crc 16校驗
  crc=GetCrc16(str,5);//0x5359   
  //printf("%04X\n",crc);   
  system("pause");
  return 0;   
} 
對於查表法,過幾天小弟弄熟悉了再發上來吧。呵呵 ^_^