根据此处的算法,我在尝试在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回答的说法继续工作。
答案 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
关闭,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")
);
}
答案 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;
}