我有来自 CAN 总线日志的消息列表。 有些消息似乎有 2 个字节,其中似乎是校验和,然后是一个序列(或计数器字节)......然后还有 5 个字节。 这是一个示例列表。前两个字节是消息 ID,然后是长度(08),然后是消息内容,其中包含 2 个字节校验和(列表中第一个为 65 AB)、1 个字节序列(第一个为 00) )然后是内容。
0185 Rx 8 65 AB 00 00 00 00 00 00
0185 Rx 8 B6 EC 01 00 00 00 00 00
0185 Rx 8 C3 24 02 00 00 00 00 00
0185 Rx 8 10 63 03 00 00 00 00 00
0185 Rx 8 08 A4 04 00 00 00 00 00
0185 Rx 8 DB E3 05 00 00 00 00 00
0185 Rx 8 AE 2B 06 00 00 00 00 00
0185 Rx 8 7D 6C 07 00 00 00 00 00
0185 Rx 8 BF B5 08 00 00 00 00 00
0185 Rx 8 6C F2 09 00 00 00 00 00
0185 Rx 8 19 3A 0A 00 00 00 00 00
0185 Rx 8 CA 7D 0B 00 00 00 00 00
0185 Rx 8 D2 BA 0C 00 00 00 00 00
0185 Rx 8 01 FD 0D 00 00 00 00 00
0185 Rx 8 74 35 0E 00 00 00 00 00
0185 Rx 8 A7 72 0F 00 00 00 00 00
0185 Rx 8 D1 96 10 00 00 00 00 00
0185 Rx 8 02 D1 11 00 00 00 00 00
0185 Rx 8 77 19 12 00 00 00 00 00
0185 Rx 8 A4 5E 13 00 00 00 00 00
0185 Rx 8 BC 99 14 00 00 00 00 00
0185 Rx 8 6F DE 15 00 00 00 00 00
0185 Rx 8 1A 16 16 00 00 00 00 00
0185 Rx 8 C9 51 17 00 00 00 00 00
0185 Rx 8 0B 88 18 00 00 00 00 00
0185 Rx 8 D8 CF 19 00 00 00 00 00
0185 Rx 8 AD 07 1A 00 00 00 00 00
0185 Rx 8 7E 40 1B 00 00 00 00 00
0185 Rx 8 66 87 1C 00 00 00 00 00
0185 Rx 8 B5 C0 1D 00 00 00 00 00
0185 Rx 8 C0 08 1E 00 00 00 00 00
0185 Rx 8 13 4F 1F 00 00 00 00 00
0185 Rx 8 0D D0 20 00 00 00 00 00
0185 Rx 8 DE 97 21 00 00 00 00 00
0180 Rx 8 43 13 01 00 00 00 00 00
0180 Rx 8 36 DB 02 00 00 00 00 00
0180 Rx 8 E5 9C 03 00 00 00 00 00
0180 Rx 8 FD 5B 04 00 00 00 00 00
0180 Rx 8 2E 1C 05 00 00 00 00 00
0180 Rx 8 5B D4 06 00 00 00 00 00
0180 Rx 8 88 93 07 00 00 00 00 00
0180 Rx 8 4A 4A 08 00 00 00 00 00
0180 Rx 8 99 0D 09 00 00 00 00 00
0180 Rx 8 EC C5 0A 00 00 00 00 00
0180 Rx 8 3F 82 0B 00 00 00 00 00
0180 Rx 8 27 45 0C 00 00 00 00 00
0180 Rx 8 F4 02 0D 00 00 00 00 00
0180 Rx 8 81 CA 0E 00 00 00 00 00
0180 Rx 8 52 8D 0F 00 00 00 00 00
0180 Rx 8 24 69 10 00 00 00 00 00
0180 Rx 8 F7 2E 11 00 00 00 00 00
0180 Rx 8 82 E6 12 00 00 00 00 00
0180 Rx 8 51 A1 13 00 00 00 00 00
0180 Rx 8 49 66 14 00 00 00 00 00
0180 Rx 8 9A 21 15 00 00 00 00 00
0180 Rx 8 EF E9 16 00 00 00 00 00
0180 Rx 8 3C AE 17 00 00 00 00 00
0180 Rx 8 FE 77 18 00 00 00 00 00
0180 Rx 8 2D 30 19 00 00 00 00 00
0180 Rx 8 58 F8 1A 00 00 00 00 00
0180 Rx 8 8B BF 1B 00 00 00 00 00
0180 Rx 8 93 78 1C 00 00 00 00 00
0180 Rx 8 40 3F 1D 00 00 00 00 00
0180 Rx 8 35 F7 1E 00 00 00 00 00
0180 Rx 8 E6 B0 1F 00 00 00 00 00
0180 Rx 8 F8 2F 20 00 00 00 00 00
我包含了 id 0x180 和 0x185 的消息,它们具有相同的内容但不同的校验和,这使我相信消息 id 在某种程度上影响了校验和的计算。
我尝试使用reveng工具(https://reveng.sourceforge.io/)并可以获得“poly”,“init”和其他参数,这些参数是有效的并且适用于4条消息,但它们不起作用对于第 5 条消息,如果我对接下来的 4 条消息运行 reveng,我会得到相同的“poly”但不同的“init”参数。 例如:
reveng -w16 -s 00000000000065AB 010000000000B6EC 020000000000C324 0300000000001063
width=16 poly=0x887b init=0xb372 refin=true refout=true xorout=0x0000 check=0x81e8 residue=0x0000 name=(none)
reveng -w16 -s 04000000000008A4 050000000000DBE3 060000000000AE2B 0700000000007D6C
width=16 poly=0x887b init=0x1ea4 refin=true refout=true xorout=0x0000 check=0x35c0 residue=0x0000 name=(none)
每条消息的初始化参数似乎不太可能发生变化。所以我错过了一些东西。
我也阅读并尝试了本文中描述的方法(https://www.csse.canterbury.ac.nz/greg.ewing/essays/CRC-Reverse-Engineering.html),虽然校验和有相同位的相同异或结果发生了变化,它没有为我提供任何关于如何计算这些校验和的进一步线索。
我还在这里阅读了类似的问题(Unknown CRC Calculation),并编译了提供的答案,但无法复制问题中的结果。
#include <stddef.h>
// Return a with the low 16 bits reversed and any bits above that zeroed.
static unsigned rev16(unsigned a) {
a = (a & 0xff00) >> 8 | (a & 0x00ff) << 8;
a = (a & 0xf0f0) >> 4 | (a & 0x0f0f) << 4;
a = (a & 0xcccc) >> 2 | (a & 0x3333) << 2;
a = (a & 0xaaaa) >> 1 | (a & 0x5555) << 1;
return a;
}
// Implement the CRC specified in the BASECAM SimpleBGC32 2.6x serial protocol
// specification. Return crc updated with the length bytes at message. If
// message is NULL, then return the initial CRC value. This CRC is like
// CRC-16/ARC, but with the bits reversed.
//
// This is a simple bit-wise implementation. Byte-wise and word-wise algorithms
// using tables exist for higher speed if needed. Also this implementation
// chooses to reverse the CRC bits as opposed to the data bits, as done in the
// specficiation appendix. The CRC only needs to be reversed once at the start
// and once at the end, whereas the alternative is reversing every data byte of
// the message. Reversing the CRC twice is faster for messages with length
// greater than two bytes.
unsigned crc16_simplebgc(unsigned crc, void const *message, size_t length) {
if (message == NULL)
return 0;
unsigned char const *data = message;
crc = rev16(crc);
for (size_t i = 0; i < length; i++) {
crc ^= data[i];
for (int k = 0; k < 8; k++)
crc = crc & 1 ? (crc >> 1) ^ 0x887b : crc >> 1;
//crc = crc & 1 ? (crc >> 1) ^ 0xa001 : crc >> 1;
}
return rev16(crc);
}
#include <stdio.h>
// Example usage of crc_simplebgc(). A CRC can be computed all at once, or with
// portions of the data at a time.
int main(void) {
unsigned crc = crc16_simplebgc(0, NULL, 0); // set initial CRC
//crc = crc16_simplebgc(crc, "\x01\x85", 2); // message id
//crc = crc16_simplebgc(crc, "\x08", 1); // message length
crc = crc16_simplebgc(crc, "\x00\x00\x00\x00\x00\x00", 6); // message content
printf("%04x\n", crc); // expecting 65AB
return 0;
}
我在 python 中也有一些代码来测试数据列表,但是如果没有正确的 crc 函数参数,它是无用的。
最终我想在node-red函数中使用它,所以我需要在javascript中使用它,但我认为一旦我有了任何语言的工作代码,我就可以自己做到这一点。
这两个字节确实看起来是其他位的 GF(2) 的线性函数。 CRC 属于该类,但它比 CRC 更通用。我没有找到表明它们是 CRC 的位排序。我所能得到的只是变化的位的线性函数。
因此,对于在这些位中变化的一个数据字节的六位中的每一位,您将取每一位,并将此表中的 16 位值异或在一起以获得校验字节,对于地址
0180
:
20: 687b
10: b43d
08: da1e
04: 6d0f
02: a68f
01: d347
如果地址是
0185
,那么也异或这个值:
5: f5ff
然后将结果与
9054
进行异或。
这是简单地通过行减少来确定的。