tcl - Tcl 中的 CRC16 计算
问题描述
我试图计算二进制文件的 CRC16。
我首先从二进制文件中读取 2 个字节,然后计算一个具有多项式 = 1021 和 0xFFFF 初始值的 CRC16。我使用了一个 C 代码并试图将它翻译成 TCL。我不能使用字节格式,因为我通过计算得到一个关于使用非数字字符串的错误。所以我将字节转换为字符串。
proc main {}{
# open binary file
set file_read [open "$input_file" rb]
while {1} {
if {! [eof $fr]} {
append binary_data [read $file_read 2]
}
binary scan [string range $binary_data 0 1] H4 str_bin_data
set CRC_data [CRC_calculation $str_bin_data]
puts " CRC_data := $CRC_data"
}
}
proc CRC_calculation {str_bin_data} {
set Polynome 0x1021
set Highbit 0x8000
set CRC_data 0xFFFF
set byte 0
set bit 0
set data_ln [string length $str_bin_data]
# puts " data_ln := $data_ln"
for {set byte 0} {$byte < $data_ln} {incr byte} {
set CRC_data [ expr {$CRC_data ^ ([lindex $str_bin_data $byte] << 8)} ]
for {set bit 8} {$bit > 0} {incr bit -1} {
if {($CRC_data && $Highbit)} {
set CRC_data [expr {($CRC_data << 1) ^ $Polynome}]
} else {
set CRC_data [expr {$CRC_data << 1}]
}
}
puts " byte_index := $byte"
puts " CRC_data := $CRC_data"
}
return $CRC_data
}
在 C 中,当我定义一个字节数组示例(二进制文件中的前 8 个字节)时: unsigned char bytes[3]= {0x55,0x55,0x55,0x55}; 然后 CRC = 0x82b8 在 Tcl 中我没有得到正确的值,甚至没有 32 位 CRC 值。
这是我使用的 C 代码:
#include<stdio.h>
#define Polynom 0x1021
#define Highbit 0x8000
unsigned short getCRC(const unsigned char data[])
{
unsigned short rem = 0xFFFF;
unsigned long byte = 0;
int bit = 0;
for (byte = 0; byte < 3; ++byte)
{
rem ^= (data[byte]<< 8);
for (bit = 8; bit > 0; --bit)
{
if (rem & Highbit)
rem = (rem << 1) ^ Polynom;
else
rem = (rem << 1);
}
}
return (rem);
}
int main() {
int rem ;
unsigned char data[]= {0x55,0x55,0x55,0x55};
rem = getCRC (data);
printf("%x", rem);
}
解决方案
有几个问题。首先,也是最重要的,二进制数据的扫描是不正确的,因为我们希望得到无符号字节(用于与 C 并行操作)而不是十六进制字符。你会更好:
# I'm assuming you've got Tcl 8.6, this is how you read 2 bytes as unsigned chars
binary scan [read $file_read 2] "cu*" str_bin_data
# Process the list of parsed byte data here; I'm not sure if you want this in the loop or not
另一个大问题是您的 CRC 计算不正确。
proc CRC_calculation {str_bin_data} {
set Polynom 0x1021
set Highbit 0x8000
set MASK 0xFFFF; # 16 bit mask; for clamping to C unsigned short range
set rem 0xFFFF
# Assume str_bin_data holds a list of unsigned char values
foreach byte $str_bin_data {
set rem [expr {$rem ^ ($byte << 8)}]
foreach _ {7 6 5 4 3 2 1 0} {
set rem [expr {
(($rem << 1) ^ ($rem & $Highbit ? $Polynom : 0)) & $MASK
}]
}
}
return $rem
}
这里的主要观察?Tcl 的数字是任意精度整数(和 IEEE double
s,虽然在这里不相关)。这意味着您需要限制范围。0xFFFF
至少,在任何可以增加使用的位数的操作之后,这将是一个带有(16 位掩码)的 AND ,这只是<<
在这个算法中。加上首先转换二进制数据的问题,这就是为什么事情不适合你的原因。我也切换到使用foreach
,因为它对于以“做每一个”为基本思想的操作来说更快更清晰,并将内部位合并为一个expr
(是的,expr
如果你愿意,表达式可以是多行的)。
最大的问题是您将完全错误的东西传递给CRC_calculation
代码。改变binary scan
是至关重要的。
推荐阅读
- java - 如何创建一个参数化的 jar,其中参数由用户在运行时传递
- java - 如何使用 elasticsearch java api 连接位于不同服务器上的多个节点?
- ios - Xcode 10 & Xcode 11 Code Completion 将所有对象显示为“int *”
- c++ - 从 SetClipboardData 钩子获取文件名
- angular - 制作正确的 Angular 后卫的困难
- regex - 带有 Lookarounds 的扩展正则表达式 grep 的替代方案,但“指定了冲突的匹配器”
- javascript - 当我点击任何地方时,是否有一个选项可以关闭切换栏?
- r - 如何从 netCDF 创建光栅砖?
- assembly - 伪代码中的“addi a0, zero, 2”是什么意思?
- excel - 如何更改我使用 vba for gmail 发送的电子邮件中的 .from?