close
CRC 的計算方式

CRC 為校驗和的一種,是兩個位元組資料流採用二進制除法(沒有進位,使用XOR來代替減法)相除所得到的餘數。其中被除數是需要計算校驗和的資訊資料流的二進制表示;除數是一個長度為 (n+1) 的預定義(短)的二進制數,通常用多項式的係數來表示。在做除法之前,要在資訊資料之後先加上 n 個 0。

舉例來說,除數為 "1011",資訊資料為 "11100110",在做除法之前,已在資訊資料之後先加上 3 個 0,計算過程如下圖所示,:


圖右邊是長除法的計算過程,圖中間列出詳細的分解,可以視為:
     111 0011 0000
   ^ 101 1000 0000
    ^ 10 1100 0000
        ^ 101 1000
         ^ 10 1100
------------------
               100


也就是說 111 0011 0000 ^ 101 1000 0000 ^ 10 1100 0000 ^ 101 1000 ^ 10 1100 = 100
又因為 XOR 運算可以符合結合律,也就是說:
A ^ B ^ C = (A ^ B) ^ C = A ^ (B ^ C)
所以下式同樣會成立:
111 0011 0000 ^ (101 1000 0000 ^ 10 1100 0000 ^ 101 1000 ^ 10 1100) = 100
這樣的話,又因為資訊資料會補 3 個 0,所以只需要計算 (101 1000 0000 ^ 10 1100 0000 ^ 101 1000 ^ 10 1100) 的後三位,就可以得到 100。

接下來是重點部分,該如何決定要拿哪些資料來作 XOR 運算?
為方便解釋,先作一些數值設定:
regs 用於儲存 XOR 的計算結果,初始值為:0000。
msb 為資訊資料的第一個位元。
regs_msb 為 regs 的第一個位元。
polynomial 為除數。

運算的方式為:
  1. 如果 msb 跟 regs_msb 只有一個為 1,則:
    regs = regs ^ polynomial
  2. regs = (regs << 1)
  3. 跳到第一步,msb 跳到資訊資料的下一個位元,資訊資料如果沒有下一個位元,則結束


最關鍵的部分是第一步,只有在 msb 跟 regs_msb 不一樣的情況下,才需要對 regs 作 ^ polynomial 的運算,如果兩者剛好都為 0 或都為 1,則剛好抵消,不需要作運算。
原因就是如果 msb 跟 regs_msb 如果都為 1 或 0,則彼此相抵消,可以搭配上圖來看,來瞭解這邊要表達的意思,不然不好懂。

再進一步來看:
regs 的第一個 bit,總是會被位移去掉,所以可以不要列入計算,但還是需要拿來作判斷使用,於是運算的方式就變成:
此時 polynomial 也要把第一個 1 的位元去除。
  1. 從 regs 取出 regs_msb,接著
    regs = (regs << 1)
  2. 如果 msb 跟 regs_msb 只有一個為 1,則:
    regs = regs ^ polynomial
  3. 跳到第一步,msb 跳到資訊資料的下一個位元,資訊資料如果沒有下一個位元,則結束




用 C 語言來計算 CRC

用程式來撰寫計算 CRC32 的方式,如下所示:
static uint32_t calculate_crc32(void *buf, uint32_t len)
{
    uint32_t i, j;
    uint32_t byte_length = 8;       /*length of unit (i.e. byte) */
    int msb = 0;
    int polynomial = 0x04C11DB7;    /* IEEE 32bit polynomial */
    unsigned int regs = 0xFFFFFFFF; /* init to all ones */
    int regs_mask = 0xFFFFFFFF;     /* ensure only 32 bit answer */
    int regs_msb = 0;
    unsigned int reflected_regs;
    for (i = 0; i < len; i++) {
        int data_byte = *((uint8_t *)buf + i);
        data_byte = reflect(data_byte, 8);
        for (j = 0; j < byte_length; j++) {
            // 取得資訊資料的 msb
            msb = data_byte >> (byte_length - 1); /* get MSB */
            msb &= 1;                             /* ensure just 1 bit */
            // regs 的 msb
            regs_msb = (regs >> 31) & 1;          /* MSB of regs */
            // 去除 regs 的 msb,並補 0
            regs = regs << 1;                     /* shift regs for CRC-CCITT */
            // 如果 regs_msb 及 msb 其中,只有 1 組值為 1
            if (regs_msb ^ msb) {                 /* MSB is a 1 */
                // 對 regs 與 polynomial 做 XOR 運算
                regs = regs ^ polynomial;         /* XOR with generator poly */
            }
            regs = regs & regs_mask;              /* Mask off excess upper bits */
            // 資訊資料位移 1 個位元
            data_byte <<= 1;                      /* get to next bit */
        }
    }
    regs = regs & regs_mask;
    reflected_regs = reflect(regs, 32) ^ 0xFFFFFFFF;
    return reflected_regs;
}



以上的算法是 CRC-32-IEEE 802.3 的算法,它的參數模型是:
Width :32CRC 的長度
Poly :04C11DB7polynomial 的值
Initial value :0xFFFFFFFFreg 计算前的初始值
RefIn :True輸入資訊資料要進行反轉
RefOut :True輸出結果(CRC)要進行反轉
XorOut :FFFFFFFF對輸出結果(CRC)要運行 XOR 運算


如此,總算能看懂 CRC 的程式寫法。



Reference

Wiki: 循環冗餘校驗

不要跑,CRC没这么难!

CRC的基本原理详解


文字內容 或 影像內容 部份參考、引用自網路,如有侵權,請告知,謝謝。
arrow
arrow
    全站熱搜

    silverwind1982 發表在 痞客邦 留言(0) 人氣()