当输入是奇数个BITS(不是字节)时,生成CRC 8/16的最佳方法是什么? C或Python

时间:2010-08-05 04:02:17

标签: python c algorithm

所以我坚持使用在奇数位上添加CRC8 / CRC16的协议。 (即,它不能被8整除)在软件中为它生成CRC的最佳方法是什么?

有很多CRC算法使用表,但它们是每字节查找。当然,一次只做一次“故障安全”。但是有更好的方法吗?也许主要是通过查表进行,然后一次完成它?

我目前正在python中使用bitarray来处理这个问题。但是C中的解决方案也可以。谢谢!

编辑:请注意,我正在与现有硬件接口,这些硬件在奇数位上计算CRC。 (对于HW来说很容易,因为它们一次只使用LFSR - 1位!)因此,虽然使用已知模式的填充可以用于完整性检查,但它会破坏hw兼容性。

1 个答案:

答案 0 :(得分:7)

在前面用零填充不应该改变结果。计算CRC基本上是二进制长除法。不幸的是,这涉及拆分每个字节。使用移位运算符和按位或。

很容易

最后的零填充更容易,并且根据您计算CRC的原因,这是一个完全合理的事情。例如,如果您使用CRC进行完整性检查。

修改从我的评论中提取我的示例。如果您有11位11101110111并且想要计算CRC,请填充它们以获得00000111 01110111 = 0x777,不要填充它们以获得0x7770,因为这将具有不同的CRC。

这有效的原因是CRC基本上是二进制长除法

                    1 0 1 = 5
            -------------
1 0 0 1 1 / 1 1 0 1 1 0 1
            1 0 0 1 1 | |
            --------- | |
              1 0 0 0 0 |
              0 0 0 0 0 |
              --------- |
              1 0 0 0 0 1
                1 0 0 1 1
                ---------
                  1 1 1 0 = 14 = remainder

具有完全相同的结果
                      1 0 1 = 5
            ---------------
1 0 0 1 1 / 0 1 1 0 1 1 0 1
              1 0 0 1 1 | |
              --------- | |
                1 0 0 0 0 |
                0 0 0 0 0 |
                --------- |
                1 0 0 0 0 1
                  1 0 0 1 1
                  ---------
                    1 1 1 0 = 14 = remainder

,同样适用于任意数量的前导零。

注意此时,除非你是寻找野外工作的精神科医生,想成为一个,或者暗中渴望需要看一个,否则你可能值得一试跳过< strong>超级双秘密试用编辑

由于问题更改而进一步编辑

如果您有一个非常重要的初始向量,则可以执行以下操作。假设我们想要使用FFFF的初始化程序计算上述字符串的CRC-CCITT CRC。我们填充字符串以获得0x0FFF计算CRC-CCIT,初始化器0获得0x0ECE,然后计算CRC-CCIT,初始化器0xFFFF为0x0000,得到0x1D0F,xor为0x0ECE xor 0x1D0F = 0x13C1。

如果多项式是原始的(我认为它们都是原始的),则可以快速计算0和0的非零初始化器的CRC,但它变得复杂,我没有足够的时间。

该技术的本质是我们可以将移位寄存器的状态视为多项式。如果我们用n个初始化它,这与考虑初始多项式相同,因为 p(x)= x ^(n - 1)+ x ^(n - 2)... + x + 1 。计算 k 零的字符串的CRC等同于找到 p(x)x ^ k mod CRC。通过重复的平方和缩小很容易找到 x ^ k mod CRC。 GF(2)上的任何多项式算术库都应该这样做。

更进一步编辑在非零初始值设定项的情况下,用零填充并将初始化程序更改为一个值,使得在读取| pad |之后可能更有意义。移位寄存器包含FFFF(或者你想要的任何值)的零数。这些可以预先计算,你只需要存储16或32个(或者你的crc多项式中有多少位。

例如,对于具有初始化器0xFFFF的CRC-CCIT和单个位0填充,我们将要使用0xF7EF的初始化器。这些可以通过使用扩展的欧几里德算法找到x ^( - 1)mod CRC然后针对各种填充长度计算初始化器* x ^( - k)mod CRC来计算。任何GF(2)polynomail包都应该让这一切变得简单。我过去使用NTL并发现它非常灵活,但这可能有点过分了。即使对于32位crcs,exhjaustive搜索也可能比编写代码更快地找到初始化器。

超级双重秘密试用版

好吧,事情实际上比我想象的要简单得多。上面的一般想法是正确的,我们想要在前面用0填充字符串以将大小扩展到8,16或32的倍数,这取决于我们的软件实现需要什么,并且我们想要更改我们的初始向量来设置我们的在读取填充零之后,LFSR将被设置为我们想要的初始向量。我们当然可以使用galois字段算法来做到这一点,但有一种更简单的方法:只需向后运行LFSR。

例如,如果我们想要计算11位11位11101110111的CRC-CCITT(0xFFFF),我们用5 0来填充它们以获得00000111 01110111然后将LFSR返回五个空格以获得0xF060的初始向量。 (我已经手工完成了计算,所以要小心)。 因此,如果您启动IV为0xF060的LSFR(或软件实现)并在0x0fff上运行,则应该得到与在原始11位上运行IV 0xFFFF的LFSR相同的结果。