如何在PHP中实现纵向冗余校验(LRC / CRC8 / XOR8)校验和?

时间:2011-06-09 09:34:07

标签: php checksum

根据此处的算法,我在尝试在PHP中实现XOR8 / LRC校验和时遇到了实际问题:http://en.wikipedia.org/wiki/Longitudinal_redundancy_check

我正在尝试做的是,给定任何字符串计算其LRC校验和。

例如,我确定知道这个字符串:

D$1I 11/14/2006 18:15:00 1634146 3772376 3772344 3772312 3772294 1*

具有39的十六进制校验和(包括最后*个字符。)

对于任何感兴趣的人,字符串的含义是什么,它是DART(海洋深海评估和海啸报告)消息 - http://nctr.pmel.noaa.gov/Dart/Pdf/dartMsgManual3.01.pdf

我将字符串转换为带有1和0的二进制字符串。从那里,我尝试创建一个字节数组并将算法应用于字节数组,但它不起作用,我无法弄清楚原因。

我用来转换为String到Binary String的函数是:

        function str2binStr($str) { 
         $ret = '';
         for ($i = 0, $n = strlen($str); $i < $n; ++$i) 
           $ret .= str_pad(decbin(ord($str[$i])), 8, 0, STR_PAD_LEFT); 
         return $ret; 
        }

我用于从二进制字符串转换为二进制数组的函数是:

        function byteStr2byteArray($s) {
          return array_slice(unpack("C*", "\0".$s), 1);
        }

最后,我使用的LRC实现,按位运算符,是:

        function lrc($byteArr) {
           $lrc = 0;
           $byteArrLen = count($byteArr);
           for ($i = 0; $i < $byteArrLen; $i++) {
             $lrc = ($lrc + $byteArr[$i]) & 0xFF;
           }
           $lrc = (($lrc ^ 0xFF) + 1) & 0xFF;
           return $lrc;
        }

然后,我们使用dechex($ checksum + 0)转换LRC校验和的最终十进制结果,因此我们有最终的十六进制校验和。

完成所有这些操作后,我没有得到预期的结果,所以任何帮助都将受到高度赞赏

提前致谢。


另外,我无法按照CRC8-Check in PHP回答的说法继续工作。

3 个答案:

答案 0 :(得分:1)

我担心StackOverflow上的任何人都无法帮助你,这就是原因。这个问题困扰着我,所以我去了你提到的DART网站,看看他们的规格。两个问题变得明显:

  • 第一个是你误解了他们的部分内容。邮件以回车符(\r\0x0D)开头,而星号* 是校验和的一部分

  • 第二个更大的问题是他们的规格包含多个错误。其中一些可能源于错误的复制/粘贴和/或从Microsoft .doc到PDF的错误转换。

我花时间检查其中的一些,如果您可以联系规范作者或维护者,以便他们可以修复或澄清它们,那将是很好的。这是我发现的。

2.1.2 消息细分提醒C/I作为消息状态,即使它没有出现在示例消息中。

2.1.3 校验和错误,0x31关闭,对应于字符1

2.2.3 六个校验和错误,由0x2D关闭,对应于字符-

2.3.1.2 我认为<cr>dev3之间缺少tries

2.3.1.3 校验和已由0x0D关闭,dev3tries之间没有分隔符。如果dev3值和tries值之间存在回车符,则校验和将是正确的。

2.3.2.2-3 2.3.1.2-3 相同。

2.3.3.3 错误的校验和,tries之前没有分隔符。

2.4.2 消息细分提及D$2 = message IDD$3 = message ID

以下是我用来验证校验和的代码:

$messages = array(
    "\rD\$0 11/15/2006 13:05:28 3214.2972 N 12041.3991 W* 46",
    "\rD\$1I 11/14/2006 18:15:00 1634146 3772376 3772344 3772313 3772294 1* 39",
    "\rD\$1I 11/14/2006 19:15:00 1634146 3772275 3772262 3772251 3772249 1* 38",
    "\rD\$1I 11/14/2006 20:15:00 1634146 3772249 3772257 3772271 3772293 1* 3E",
    "\rD\$1I 11/14/2006 21:15:00 1634146 3772315 3772341 3772373 3772407 1* 39",
    "\rD\$1I 11/14/2006 22:15:00 1634146 3772440 3772472 3772506 3772540 1* 3C",
    "\rD\$1I 11/14/2006 23:15:00 1634146 3772572 3772603 3772631 3772657 1* 3B",
    "\rD\$2I 00 tt 18:32:45 ts 18:32:00 3772311\r00000063006201* 22",
    "\rD\$2I 01 tt 18:32:45 ts 18:32:00 3772311\r000000630062706900600061005f005ffffafff9fff8fff8fff7fff6fff401* 21",
    "\rD\$2I 02 tt 18:32:45 ts 18:32:00 3772335\rfffdfffafff7fff5fff1ffeeffea00190048ffe1ffddffdaffd8ffd5ffd101* 21"
);

foreach ($messages as $k => $message)
{
    $pos = strpos($message, '*');

    $payload = substr($message, 0, $pos);
    $crc = trim(substr($message, $pos + 1));

    $checksum = 0;

    foreach (str_split($payload, 1) as $c)
    {
        $checksum ^= ord($c);
    }

    $crc = hexdec($crc);

    printf(
        "Expected: %02X - Computed: %02X - Difference: %02X - Possibly missing: %s\n",
        $crc, $checksum, $crc ^ $checksum, addcslashes(chr($crc ^ $checksum), "\r")
    );
}

答案 1 :(得分:0)

对于它的价值,这是一个完全未经优化的,直接实现维基百科的算法:

$buffer = 'D$1I 11/14/2006 18:15:00 1634146 3772376 3772344 3772312 3772294 1*';

$LRC = 0;
foreach (str_split($buffer, 1) as $b)
{
    $LRC = ($LRC + ord($b)) & 0xFF;
}
$LRC = (($LRC ^ 0xFF) + 1) & 0xFF;

echo dechex($LRC);

从你的例子中得到0x0E字符串,所以要么我设法捏造了实现,要么产生0x39的算法不一样。

答案 2 :(得分:0)

我意识到这个问题很老了,但我很难搞清楚如何做到这一点。它现在正在工作,所以我想我应该粘贴代码。在我的例子中,校验和需要作为ASCII字符串返回。

public function getLrc($string)
{
    $LRC = 0;
    // Get hex checksum.
    foreach (str_split($string, 1) as $char) {
        $LRC ^= ord($char);
    }
    $hex = dechex($LRC);
    // convert hex to string
    $str = '';
    for($i=0;$i<strlen($hex);$i+=2) $str .= chr(hexdec(substr($hex,$i,2)));
    return $str;
}