我试图计算DNP3链接级别的CRC。该规范说它是基于多项式X ^ 16 + X ^ 13 + X ^ 12 + X ^ 11 + X ^ 10 + X ^ 8 + X ^ 6 + X ^ 5 + X ^ 2的2个八位字节CRC + 1.它也被反转并首先放入数据块LSB。
使用Perl的Digest软件包,我将算法设置为:
my $ctx = Digest::CRC->new(width=>16, init=>0, xorout=>0xffff, refin=>1, refout=>1, poly=>0x3d65, cont=>0xea82);
$ctx->add(0x05);
$ctx->add(0x64);
$ctx->add(0x05);
$ctx->add(0xF2);
$ctx->add(0x01);
$ctx->add(0x00);
$ctx->add(0x00);
$ctx->add(0x00);
my $x=$ctx->digest;
printf("x=%04x\n",$x);
标题显示的规范中的示例: 05 64 05 F2 01 00 00 00 校验和应该是 52℃我得到x = 91fc。
我尝试过使用所有参数,但似乎无法让它出来。有什么建议?我需要逐字节地添加()数据吗?字(2字节)逐字?
答案 0 :(得分:1)
您的数据与http://reveng.sourceforge.net/crc-catalogue/16.htm
中的 CRC-16 / DNP 兼容width=16
poly=0x3d65
init=0x0000
refin=true
refout=true
xorout=0xffff
check=0xea82
name="CRC-16/DNP"
您可以使用https://www.lammertbies.nl/comm/info/crc-calculation.html
进行验证答案 1 :(得分:1)
(此问题在PerlMonks上发布 - 请参阅here。以下答案也在PerlMonks上发布 - 请参阅here)
我以为我会挑战自己,看看能否解决这个问题。当我开始研究CRC算法时,我很快就发现了自己。但我认为我实际上能够弄明白。
查看Digest :: CRC的源代码,我猜测'const'的值应为0而不是'0xea82'。这让我更接近ASCII输入。将其与Anonymous Monk的建议结合起来让我更接近十六进制输入。在这两种情况下,接近我的意思是我得到了正确的角色,但顺序错误。基本上,我需要按位反转输出。例如,使用您的示例标题,我得到'c52'而不是'520c'。这里的两位是'0c'和'52'。反转'c52'(或'0c52')的两位会产生'520c'。
在搜索CRC-DNP校验和的引用时,我找到了一个在线校验和计算器(找到here - 并在gammatester的答案中引用)。我用它来“验证”以下代码的输出。
use strict;
use warnings;
use feature 'say';
use Digest::CRC;
sub Left_Pad {
my $value = shift;
if (length($value) % 2) {$value = '0'.$value;}
return $value;
}
sub Bitwise_Reverse {
my $value = shift;
$value = Left_Pad($value);
my $offset = length($value);
my $reversed;
while ($offset > 0) {
$offset -= 2;
my $string = substr($value,$offset,2);
$reversed .= $string;
}
return $reversed;
}
sub CRC_DNP_ASCII {
my $value = shift;
my $ctx = Digest::CRC->new(width=>16, init=>0x0, xorout=>0xffff, refin=>1,
refout=>1, poly=>0x3d65, cont=>0);
$ctx->add($value);
my $digest = $ctx->hexdigest;
my $crc = Bitwise_Reverse($digest);
return $crc;
}
sub CRC_DNP_HEX {
my $value = shift;
my $ctx = Digest::CRC->new(width=>16, init=>0x0, xorout=>0xffff, refin=>1,
refout=>1, poly=>0x3d65, cont=>0);
my $offset = 0;
while ($offset < length($value)) {
my $string = substr($value,$offset,2);
$ctx->add(chr(hex($string)));
$offset += 2;
}
my $digest = $ctx->hexdigest;
my $crc = Bitwise_Reverse($digest);
return $crc;
}
my @data_list = ('056405F201000000','56405F201000000');
foreach my $data (@data_list) {
say "Calculating CRC-DNP checksum for '$data':";
my $ascii = CRC_DNP_ASCII($data);
say " ASCII input: $ascii";
my $hex = CRC_DNP_HEX($data);
say " Hex input: $hex\n";
}
产生了以下输出:
Calculating CRC-DNP checksum for '056405F201000000':
ASCII input: 99fc
Hex input: 520c
Calculating CRC-DNP checksum for '56405F201000000':
ASCII input: 0751
Hex input: 11e3
我已经尝试了一些其他示例来比较我的代码输出和在线计算器,我得到的CRC-DNP校验和值相同。
也许更有知识渊博的人可能能够识别我的代码问题,但我认为它应该可以帮助您进一步了解您正在尝试做的事情。
答案 2 :(得分:0)
好的,在你们大家的帮助下,攻击Digest :: CRC并逐步比较IEEE Std 1815中C样本CRC计算给出的结果,我想出了以下内容:代码:
my $ctx = Digest::CRC->new(width=>16, init=>0, xorout=>0xffff, refin=>1, refout=>1, poly=>0x3d65, cont=>0);
$ctx->add(chr(0x05));
$ctx->add(chr(0x64));
$ctx->add(chr(0x05));
$ctx->add(chr(0xF2));
$ctx->add(chr(0x01));
$ctx->add(chr(0x00));
$ctx->add(chr(0x00));
$ctx->add(chr(0x00));
my $x=$ctx->digest;
printf("x=%02X %02X\n",$x>>8, $x&0xFF);
我的原始代码的问题不在CRC摘要中,而是我将数据传递到模块的方式。我将其作为单个标量值传递给它。 Digest :: CRC然后将其附加到数据缓冲区,代码为:
$self->{_data} .= join '', @_ if @_;
这(显然)将标量转换为字符串(即0x05到“5”)。随之而来的是摘要子程序,它使用代码访问第一个字节:
ord(substr($message, $pos++, 1))
这导致消化了值0x35,而不是我想要的0x05。
解决方案是通过将数据包装为字符串(即chr(0x05))将数据传递到模块中。
谢谢大家,特别是gammatester和dasgar,指出我正确的方向。