使用 crcmod 在 Python 中重新创建 C CRC32 算法

Recreating a C CRC32 algorithm in Python with crcmod

我正在尝试解码 Python 中的 Novatel GPS 流,并且我拥有同步的所有组件,header 和有效负载排序,但我无法复制使用的 CRC32作为消息的一部分。用于生成附加在消息上的CRC的算法用c写成如下:

#include <iostream>
#include <string>

using namespace std;

#define CRC32_POLYNOMIAL 0xEDB88320L
//#define CRC32_POLYNOMIAL 0x04C11DB7L

unsigned long CRC32Value(int i)
{
    int j;
    unsigned long ulCRC;
    ulCRC = i;
    for (j = 8; j > 0; j--)
    {
        if (ulCRC & 1)
            ulCRC = (ulCRC >> 1) ^ CRC32_POLYNOMIAL;
        else
            ulCRC >>= 1;
    }
    return ulCRC;
}
unsigned long CalculateBlockCRC32(
    unsigned long ulCount,
    unsigned char *ucBuffer)
{
    unsigned long ulTemp1;
    unsigned long ulTemp2;
    unsigned long ulCRC = 0;
    while (ulCount-- != 0)
    {
        ulTemp1 = (ulCRC >> 8) & 0x00FFFFFFL;
        ulTemp2 = CRC32Value(((int)ulCRC ^ *ucBuffer++ ) & 0xff );
        ulCRC = ulTemp1 ^ ulTemp2;
    }
    return(ulCRC);
}

int main()
{
    unsigned char buffer[] = {0xaa, 0x44, 0x12, 0x1c, 0x2a, 0x00, 0x02, 0x20, 0x48, 0x00, 0x00, 0x00, 0x90, 0xb4, 0x93, 0x05, 0xb0, 0xab, 0xb9, 0x12, 0x00, 0x00, 0x00, 0x00, 0x45, 0x61, 0xbc, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1b, 0x04, 0x50, 0xb3, 0xf2, 0x8e, 0x49, 0x40, 0x16, 0xfa, 0x6b, 0xbe, 0x7c, 0x82, 0x5c, 0xc0, 0x00, 0x60, 0x76, 0x9f, 0x44, 0x9f, 0x90, 0x40, 0xa6, 0x2a, 0x82, 0xc1, 0x3d, 0x00, 0x00, 0x00, 0x12, 0x5a, 0xcb, 0x3f, 0xcd, 0x9e, 0x98, 0x3f, 0xdb, 0x66, 0x40, 0x40, 0x00, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x03};

    unsigned long crc = CalculateBlockCRC32(sizeof(buffer), buffer);
    cout << hex << crc << endl;
}

结果是 0x42、0xdc、0x4c、0x48 - 与手册中给出的示例相匹配,所以这是一个不错的开始。

虽然我可能会在我正在编写的代码中使用这个算法,但我更愿意尽可能保留在 Python 中,所以在这一点上我觉得我有两个选择。我可以自己将它翻译成 Python,或者希望有一个 built-in Python 函数或模块能够为我做这件事,这正是我一直在考虑做的事情。到目前为止我找到了三个 functions/modules:

其中前两个给出了相同的错误结果,我无法说出任何更改设置或指定可能对此产生积极影响的参数的方法。

crcmod 另一方面有大量的设置和预定义的功能,我认为应该能够做我正在寻找的事情。创建CRC函数的格式如下:

crcmod.mkCrcFun(poly[, initCrc, rev, xorOut])

我发现的关键事情之一是有预定义的 CRC 函数,它的 CRC32 版本与 binascii 和 zlib 的结果相匹配:

Name    Polynomial  Reversed?   Init-value  XOR-out Check
crc-32  0x104C11DB7 True    0x00000000  0xFFFFFFFF  0xCBF43926

所以至少这有助于我理解 zlib 和 binascii 变体在所用多项式方面的来源。

我确信我应该能够使用此函数生成与 C 算法相同的结果,但不是 well-versed 在 C 中我不确定我是如何做到这一点的。 C 算法中使用的多项式是多项式的反向表示,因此这意味着上面的设置应该有效,但它们不会按照 C 代码生成上面的答案。

我在寻找什么来确定这些设置,是否有一个 module/function 可以执行我从未见过的操作,或者这是否行不通,我应该开始翻译 C进入 Python 我自己?

对于crcmod,你需要在多项式前面加上1,因为第33位需要是1,否则你会得到一个异常,说次数必须是 8、16、24 或 32。这似乎会产生您预期的输出:

import crcmod
crc = crcmod.mkCrcFun(0x104C11DB7, 0, True, 0)

然后下面的示例应该在 Python 2 和 3 上都能正常工作:

value = bytes(bytearray(
    [0xaa, 0x44, 0x12, 0x1c, 0x2a, 0x00, 0x02, 0x20,
     0x48, 0x00, 0x00, 0x00, 0x90, 0xb4, 0x93, 0x05,
     0xb0, 0xab, 0xb9, 0x12, 0x00, 0x00, 0x00, 0x00, 
     0x45, 0x61, 0xbc, 0x0a, 0x00, 0x00, 0x00, 0x00,
     0x10, 0x00, 0x00, 0x00, 0x1b, 0x04, 0x50, 0xb3,
     0xf2, 0x8e, 0x49, 0x40, 0x16, 0xfa, 0x6b, 0xbe,
     0x7c, 0x82, 0x5c, 0xc0, 0x00, 0x60, 0x76, 0x9f, 
     0x44, 0x9f, 0x90, 0x40, 0xa6, 0x2a, 0x82, 0xc1, 
     0x3d, 0x00, 0x00, 0x00, 0x12, 0x5a, 0xcb, 0x3f, 
     0xcd, 0x9e, 0x98, 0x3f, 0xdb, 0x66, 0x40, 0x40,
     0x00, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 
     0x00, 0x00, 0x00, 0x00, 0x0b, 0x0b, 0x00, 0x00,
     0x00, 0x06, 0x00, 0x03]))

print(hex(crc(value))))

打印

0x484cdc42

八位字节 0x42, 0xdc, 0x4c, 0x48.

的小端有序值