16字节数据的4位fletcher校验和

时间:2012-11-21 10:54:15

标签: c microcontroller crc

我正在尝试实现8位fletcher校验和功能。

我的数据总是长达17个字节。

我从Remake of Fletcher checksum from 32bit to 8

的代码开始

以下是我最终拥有的内容:

// 8-bit Fletcher checksum
//   data is always 17 byte long
uint8_t fletcher(uint8_t *data) {
    uint8_t sum1 = 0x0f, sum2 = 0x0f, len = 17;

    while(len) {
        sum1 += *data++;
        sum2 += sum1;
        sum1 = (sum1 & 0x0f) + (sum1 >> 4);
        sum2 = (sum2 & 0x0f) + (sum2 >> 4);
        len--;
    }
    sum1 = (sum1 & 0x0f) + (sum1 >> 4);
    sum2 = (sum2 & 0x0f) + (sum2 >> 4);
    return sum2<<4 | sum1;
}

我想知道它是否好,而且我的印象是我可以进一步简化,但我找不到哪里(也许它毕竟不能进一步简化......)。

我的主要问题是这段代码看起来是否可行。我在基于无线的数据链路的两侧使用它将“工作”(从相同的数据返回相同的校验和)但在fletcher方式可能是错误的,并且不提供预期的错误检测...

希望我足够清楚......

提前致谢!

2 个答案:

答案 0 :(得分:4)

首先,Fletcher的任何变体都不是CRC。这是一个校验和。

其次,只要算法正确,您处理的字节数就没有什么特别之处。

第三,你不需要也不应该在每一步都做模数15。对于速度(与CRC相比,这是Fletcher和的整点),应该有一个仅由sum2 += sum1 += *data++;组成的内环。根据{{​​1}}和sum1数据类型的大小,您可以计算在溢出sum2之前可以执行的迭代次数,假设所有输入字节都是sum2。然后一个外部循环运行该内部循环多次,然后是两个模数15。外部循环遍历所有输入数据。

第四,当0xff最终为15时,(x >> 4) + (x & 0xf)操作无法完成预期的x % 15。需要有一个最终x

更新

好的,所以这是代码:

if (x == 15) x = 0;

请注意,我在#include <stdio.h> #define MAXPART 5803 /* for 32-bit unsigned sum1, sum2 */ /* #define MAXPART 22 for 16-bit unsigned sum1, sum2 */ unsigned fletcher8(unsigned f8, unsigned char *data, size_t len) { unsigned long sum1, sum2; size_t part; sum1 = f8 & 0xf; sum2 = (f8 >> 4) & 0xf; while (len) { part = len > MAXPART ? MAXPART : len; len -= part; do { sum2 += sum1 += *data++; } while (--part); sum1 %= 15; sum2 %= 15; } return (sum2 << 4) + sum1; } #define SIZE 131072 int main(void) { unsigned f8 = 1; unsigned char buf[SIZE]; size_t got; while ((got = fread(buf, 1, SIZE, stdin)) > 0) f8 = fletcher8(f8, buf, got); printf("0x%02x\n", f8); return 0; } 而不是1处启动了Fletcher8值。这足以确保任何长度的零的字符串将产生相同的零结果。您可以将初始值设置为您喜欢的任何值,只要它不是零。

如果机器上的模(0xff)运算非常慢,那么使用一组移位和加法进行%运算可能会更快。以下是32位类型的示例:

% 15

对于k = (k >> 16) + (k & 0xffff); k = (k >> 8) + (k & 0xff); k = (k >> 4) + (k & 0xf); k = (k >> 4) + (k & 0xf); if (k > 14) k -= 15; 所述的情况,代码可以简化为使用len == 17代替unsigned unsigned longsum1,并跳过自sum2以来的外循环。非%模运算也可以缩短。这就是我在循环中消除了不必要的减量操作的地方:

len <= 22

为了进行比较,您可以尝试这个8位CRC,看看它如何比较速度(crc应初始化为零):

unsigned fletch8_17(unsigned char *data)
{
    unsigned sum1 = 1;
    unsigned sum2 = 0;
    unsigned char *end = data + 17;
    do {
        sum2 += sum1 += *data++;
    } while (data < end);
    sum1 = (sum1 >> 8) + (sum1 & 0xff);
    sum1 = (sum1 >> 4) + (sum1 & 0xf);
    if (sum1 > 14) {
        sum1 -= 15;
        if (sum1 > 14)
            sum1 -= 15;
    }
    sum2 = (sum2 >> 8) + (sum2 & 0xff);
    sum2 = (sum2 >> 4) + (sum2 & 0xf);
    if (sum2 > 14) {
        sum2 -= 15;
        if (sum2 > 14)
            sum2 -= 15;
    }
    return (sum2 << 4) + sum1;
}

8位CRC肯定会比8位Fletcher校验和提供更好的错误检测性能。在这种情况下甚至可能更快!

答案 1 :(得分:2)

您的代码看起来很像Fletcher校验和中维基百科文章的Optimizations section中给出的代码。我猜your source没有正确归属就把它从那里拿走了。

延期减少

您的代码看起来大多正确。但是,你总结得太多了。如果您的输入只有17个字节的数据,则sum2的最大值将为17*(17+1)/2*0xf + 0xf = 0x906,这符合uint16_t。要将其减少到单个半字节,两个减少步骤就足够了。对于sum1,最大值为17*0xf + 0xf = 0x10e,这也需要两次缩减。所以你可以写

uint8_t fletcher(uint8_t *data) {
    uint16_t sum1 = 0xf, sum2 = 0xf, len = 17;
    while(len) {
        sum1 += *data++;
        sum2 += sum1;
        len--;
    };
    sum1 = (sum1 & 0x0f) + (sum1 >> 4);
    sum1 = (sum1 & 0x0f) + (sum1 >> 4);
    sum2 = (sum2 & 0x0f) + (sum2 >> 4);
    sum2 = (sum2 & 0x0f) + (sum2 >> 4);
    return sum2<<4 | sum1;
}

您可以对代码进行进一步的“优化”,例如

do { sum2 += ( sum1 += *data++ ); } while (--len);

甚至手动展开该循环,但一个好的优化编译器应该为你解决这个问题。

您可以将上述考虑因素调整为其他输入长度。

整个字节为半字节流

上述答案假设您的数据仅包含未打包的半字节,即最多使用每个字节的最不重要的一半。我可能在这里错了,但我想如果你在处理整个字节,那么最符合Fletcher校验和精神的解决方案就是将它们视为两倍数量的半字节序列,例如。

        sum1 += *data & 0xf;
        sum2 += sum1;
        sum1 += *data >> 4;
        sum2 += sum1;
        ++data;
        --len;

可能有更有效的方法将其写为较少的优化。您的编译器可能会也可能找不到其中一个。

校验和的质量

  

但在fletcher方式可能有误并且没有提供预期的错误检测...

我不确定这里的fletcher校验和是多么有用。也许一些具有8位输出的真实 CRC可以更好地满足您在错误检查方面的需求。