我在用PHP实现XOR8 8/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的二进制字符串。然后,我尝试创建一个字节数组,并将算法应用到字节数组中,但是它不起作用,我也不知道为什么。
用于将字符串转换为二进制字符串的函数是:
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;
}然后,我们用十进制($checksum+ 0)转换LRC校验和的最后十进制结果,从而得到最后的十六进制校验和。
在所有这些操作之后,我没有得到预期的结果,所以任何帮助都会非常感谢。
提前谢谢。
而且,我不能让它按照CRC8-Check in PHP的答案工作。
发布于 2011-06-09 15:42:33
恐怕StackOverflow上没有人能帮你,原因如下。这个问题困扰着我,所以我去了你提到的DART网站,看看他们的规格。有两个问题变得很明显:
,
\r或\0x0D)开始,星号*不是校验和的不正确转换。
我已经花了时间检查其中的一些,如果您能够联系规范的作者或维护人员,以便他们能够修复它们或澄清它们,那就太好了。这是我发现的。
2.1.2消息分解提到了C/I作为消息状态,即使它没有出现在示例消息中。
2.1.3校验和是错误的,由与字符1对应的0x31关闭。
2.2.3 6校验和错误,由与字符-对应的0x2D关闭。
2.3.1.2我认为dev3和tries之间缺少一个<cr>
2.3.1.3校验和由0x0D关闭,dev3和和与tries之间没有分隔符。如果在dev3值和tries值之间有回车,校验和将是正确的。
2.3.2.2-3与2.3.1.2-3相同.
2.3.3.3又错了校验和,在tries之前没有分隔符。
2.4.2消息分解提到了D$2 = message ID,应该是D$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")
);
}发布于 2011-06-09 11:00:20
值得注意的是,这里是维基百科算法的一个完全未优化的、直接的实现:
$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的算法不一样。
发布于 2013-05-08 15:36:05
我意识到这个问题很老,但我想不出该怎么做。它现在起作用了,所以我想我应该粘贴代码。在我的例子中,校验和需要作为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;
}https://stackoverflow.com/questions/6290781
复制相似问题