0

我正在寻找一种用 JavaScript 编写并在普通浏览器中运行的 CRC-16 CRC 算法。我知道在各种编程语言中都有大量用于 CRC-16 实现的代码示例。但我仍然无法解决我的问题。以下示例显示了 NodeJ 的 CRC-16 校验和检查。

https://github.com/donvercety/node-crc16/blob/master/crc16.js

为了让 NodeJS 代码在普通浏览器中运行,我对其进行了如下调整。

const crctab16 = new Uint16Array([
        0X0000, 0X1189, 0X2312, 0X329B, 0X4624, 0X57AD, 0X6536, 0X74BF,
        0X8C48, 0X9DC1, 0XAF5A, 0XBED3, 0XCA6C, 0XDBE5, 0XE97E, 0XF8F7,
        0X1081, 0X0108, 0X3393, 0X221A, 0X56A5, 0X472C, 0X75B7, 0X643E,
        0X9CC9, 0X8D40, 0XBFDB, 0XAE52, 0XDAED, 0XCB64, 0XF9FF, 0XE876,
        0X2102, 0X308B, 0X0210, 0X1399, 0X6726, 0X76AF, 0X4434, 0X55BD,
        0XAD4A, 0XBCC3, 0X8E58, 0X9FD1, 0XEB6E, 0XFAE7, 0XC87C, 0XD9F5,
        0X3183, 0X200A, 0X1291, 0X0318, 0X77A7, 0X662E, 0X54B5, 0X453C,
        0XBDCB, 0XAC42, 0X9ED9, 0X8F50, 0XFBEF, 0XEA66, 0XD8FD, 0XC974,
        0X4204, 0X538D, 0X6116, 0X709F, 0X0420, 0X15A9, 0X2732, 0X36BB,
        0XCE4C, 0XDFC5, 0XED5E, 0XFCD7, 0X8868, 0X99E1, 0XAB7A, 0XBAF3,
        0X5285, 0X430C, 0X7197, 0X601E, 0X14A1, 0X0528, 0X37B3, 0X263A,
        0XDECD, 0XCF44, 0XFDDF, 0XEC56, 0X98E9, 0X8960, 0XBBFB, 0XAA72,
        0X6306, 0X728F, 0X4014, 0X519D, 0X2522, 0X34AB, 0X0630, 0X17B9,
        0XEF4E, 0XFEC7, 0XCC5C, 0XDDD5, 0XA96A, 0XB8E3, 0X8A78, 0X9BF1,
        0X7387, 0X620E, 0X5095, 0X411C, 0X35A3, 0X242A, 0X16B1, 0X0738,
        0XFFCF, 0XEE46, 0XDCDD, 0XCD54, 0XB9EB, 0XA862, 0X9AF9, 0X8B70,
        0X8408, 0X9581, 0XA71A, 0XB693, 0XC22C, 0XD3A5, 0XE13E, 0XF0B7,
        0X0840, 0X19C9, 0X2B52, 0X3ADB, 0X4E64, 0X5FED, 0X6D76, 0X7CFF,
        0X9489, 0X8500, 0XB79B, 0XA612, 0XD2AD, 0XC324, 0XF1BF, 0XE036,
        0X18C1, 0X0948, 0X3BD3, 0X2A5A, 0X5EE5, 0X4F6C, 0X7DF7, 0X6C7E,
        0XA50A, 0XB483, 0X8618, 0X9791, 0XE32E, 0XF2A7, 0XC03C, 0XD1B5,
        0X2942, 0X38CB, 0X0A50, 0X1BD9, 0X6F66, 0X7EEF, 0X4C74, 0X5DFD,
        0XB58B, 0XA402, 0X9699, 0X8710, 0XF3AF, 0XE226, 0XD0BD, 0XC134,
        0X39C3, 0X284A, 0X1AD1, 0X0B58, 0X7FE7, 0X6E6E, 0X5CF5, 0X4D7C,
        0XC60C, 0XD785, 0XE51E, 0XF497, 0X8028, 0X91A1, 0XA33A, 0XB2B3,
        0X4A44, 0X5BCD, 0X6956, 0X78DF, 0X0C60, 0X1DE9, 0X2F72, 0X3EFB,
        0XD68D, 0XC704, 0XF59F, 0XE416, 0X90A9, 0X8120, 0XB3BB, 0XA232,
        0X5AC5, 0X4B4C, 0X79D7, 0X685E, 0X1CE1, 0X0D68, 0X3FF3, 0X2E7A,
        0XE70E, 0XF687, 0XC41C, 0XD595, 0XA12A, 0XB0A3, 0X8238, 0X93B1,
        0X6B46, 0X7ACF, 0X4854, 0X59DD, 0X2D62, 0X3CEB, 0X0E70, 0X1FF9,
        0XF78F, 0XE606, 0XD49D, 0XC514, 0XB1AB, 0XA022, 0X92B9, 0X8330,
        0X7BC7, 0X6A4E, 0X58D5, 0X495C, 0X3DE3, 0X2C6A, 0X1EF1, 0X0F78,
    ]);
  
  function crc16(data) {
        var res = 0x0ffff;

        for (let b of data) {
            res = ((res >> 8) & 0x0ff) ^ crctab16[(res ^ b) & 0xff];
        }

        return (~res) & 0x0ffff;
    }
  
  alert(crc16("010400030002"))

如果我用 01 04 0003 0002 测试它,我得到 13428。但我正在寻找的是 81 CB。有谁知道如何解决这个问题?

在线工具中CRC计算截图

链接到在线 CRC 计算工具

4

2 回答 2

2

“我正在寻找一种 CRC-16 CRC 算法”——那将是一个错误。查看有关“循环冗余校验”的 Wikipedia 页面,您会注意到它列出了 11 种 CRC-16 算法(将为相同的输入提供不同的结果)。当你看到这个支持 23 种不同算法的在线计算器时,情况会变得更糟!

我不确定您找到的代码使用的是哪种算法,但它不是生成您期望的值的算法。

看起来您想要计算MODBUS over Serial Line Specification and Implementation Guide附录 B 中指定的 CRC 值(基于 modbus 标签的使用和消息内容)。它值得一看规范中提出的算法,因为它很容易遵循(你可能自己将它转换为 Javascript)。

一个好的起点是现有的 Javascript Modbus 实现,因此让我们从node-modbus-serial(ISC 许可证)中的代码开始,并将其与您的测试数据一起使用(也解决了 Mark Adler 指出的问题):

/**
 * Calculates the buffers CRC16.
 *
 * @param {Buffer} buffer the data buffer.
 * @return {number} the calculated CRC16.
 * 
 * Source: github.com/yaacov/node-modbus-serial
 */
function crc16(buffer) {
    var crc = 0xFFFF;
    var odd;

    for (var i = 0; i < buffer.length; i++) {
        crc = crc ^ buffer[i];

        for (var j = 0; j < 8; j++) {
            odd = crc & 0x0001;
            crc = crc >> 1;
            if (odd) {
                crc = crc ^ 0xA001;
            }
        }
    }

    return crc;
};

console.log(crc16(Uint8Array.from([01,04,00,03,00,02])).toString(16))

这将输出结果CB81。MODBUS 对寄存器等使用“大端”表示,但出于某种原因...... CRC 是“小端”(“首先附加低位字节,然后是高位字节”)所以这将是编码为81CB01 04 00 03 00 02 81 CB使用modbus 解析器检查确认这是一个有效的 MODBUS RTU 请求。

于 2022-01-31T01:10:13.007 回答
0

首先,Javascript 字符串存储为 UTF-16,每个字符两个字节。您可以使用b.charCodeAt(0)将每个字符转换为整数,但这仅适用b于 0..127 中的 ASCII 字符。由于 Javascript 的转换规则,您实际计算的 CRC 是bytes 00 01 00 04 00 00 00 03 00 00 00 02

你没有给 CRC 你认为你给它的数据。您应该将 aUint8Array用于一系列字节。

其次,您想要的 CRC 到底是什么?你从哪里来81 CB的?你说你用“01 04 0003 0002”测试它,但实际上你(试图)用“010400030002”(没有空格)测试它。它是哪一个?或者你真的是指字节01 04 00 03 00 02?或者因为你对它们进行了奇怪的分组,也许后两个是需要小端序的 16 位字?那么字节将是01 04 03 00 02 00. 它是其中之一吗?

您需要准确了解他们在您的示例中使用什么来获得81 CB. 您还需要知道 CRC 本身是小端还是大端。是CRC 81cb,还是它cb81

于 2022-01-30T18:10:16.513 回答