迴圈冗餘校驗碼(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;
}
對於查表法,過幾天小弟弄熟悉了再發上來吧。呵呵 ^_^