【问题标题】:8-bit binary addition8位二进制加法
【发布时间】:2011-02-25 10:22:24
【问题描述】:

有人可以解释如何用 8 位二进制加法计算校验和吗?这是文档的摘录:

这是消息的一般形式:

STX | TYPE | FS | DATA | FS | CHK | ETX

STX 是 HEX 02

ETX 是 HEX 03

FS 是十六进制 15

“类型”是唯一的 1 字节消息标识符(例如,“P”表示轮询消息)。 “数据”包含可打印的 ASCII 字符。

校验和

校验和是针对所有字符计算的,包括<STX><CHK> 之间的所有<FS> 字符。 校验和由所有包含字符的 8 位二进制加法计算得出,其中第 8 位或奇偶校验位假定为零。超过第 8 位的进位将丢失。 8 位结果被转换为两个可打印的 ASCII 十六进制字符,范围从 00 到 FF,然后作为<CHK> 插入到数据流中。十六进制字符 A-F 是大写的。接收设备重新计算缓冲消息的校验和,并将其与接收到的校验和进行比较。比较是后续确认(<ACK>)或否定确认(<NAK>)传输的基础。

【问题讨论】:

  • 对我来说似乎很清楚。你不明白什么?
  • 你能给我看一个例子(示例代码)吗?从来没有做过这样的事情......
  • 你需要在 Delphi 或 Java 中使用它吗?
  • 没关系,两种语言我都懂,虽然这是在delphi中。

标签: java algorithm delphi binary checksum


【解决方案1】:

将每个字符视为整数值。由于每个字符的高位 假定 为零(因为在规范中没有说你需要检查它),所以用这样的东西(伪 C/C++/Java/whatever ):

get_next_character() & 0x7f;

现在您只需添加(伪 C/C++/Java/whatever):

int s = 0;
while(!end_of_string())
{
    s += get_next_character() & 0x7f;
    s &= 0xff;
}

这将连续添加每个 ASCII 字符并从结果总和中删除第 8 位之后的所有内容。当你全部完成时(C 或写得很糟糕的 C++):

printf("Checksum: %02x\n", s);  /* You may need %02X for uppercase.
                                   I don't remember my printf codes anymore. */

作为一种优化(如果你真的需要它——在这种情况下不太可能!)你可以推迟s &= 0xff 位,而是在校验和的使用点使用截断。但是,这不会为您节省太多性能 - 您的 I/O 会更加昂贵 - 并且可能会导致您在以后重构代码时忘记执行此操作。

【讨论】:

    【解决方案2】:

    要添加,请使用以下功能。

    function Summatory(const Data: AnsiString): Byte;
    var
        C: AnsiChar;
    begin
        Result := 0;
    
        for C in Data do
        begin
            Result := Result + Ord(C);
        end;
    end;
    

    对于旧版本的 Delphi,没有“for in”:

    function Summatory(const Data: AnsiString): Byte;
    var
        I: Integer;        
    begin
        Result := 0;
    
        for I := 1 to Length(Data) do
        begin
            Result := Result + Ord(Data[I]);
        end;
    end;
    

    函数 Summatory 被声明为 Byte,因此它将“忽略”超过第 8 位的进位。您可以传递所有要添加的字节。

    使用 SysUtils 中的函数 IntToHex 将 8 位结果转换为两个可打印的 ASCII 十六进制字符。

    例如:ChkSum := IntToHex(Summatory(Data), 2);

    【讨论】:

    • +1 用于 Delphi 示例代码 - 您应该使用 (const Data: AnsiString) 作为参数,但其余的都可以。对于旧版本的 Delphi,没有“in”,你应该添加一个 i 局部变量,然后 for i := 1 to length(Data) do result := result+ord(Data[i]);
    • 谢谢@A.Bouchez。我把你的建议放在了答案中。
    【解决方案3】:

    在 Java 中,您可以执行以下操作。

    byte[] bytes =
    byte total = 0;
    for(byte b: bytes) total += b;
    
    OutputStream os = 
    os.write(total);
    

    【讨论】:

      【解决方案4】:

      当字节总和超过 256 时,上述答案均无效。

      OP 可能正在尝试将 Siemens Dimension EXL200 化学分析仪与他的 _Lab 信息系统连接。

      他发布的段落(关于校验和的规范)是从他们的手册中复制/粘贴的。 在过去的 12 个小时里,我一直在努力解决这个特殊问题,但找不到任何解决方法。 我包括了西门子手册中提到的一些字符串,因为它们提供了校验和。

      您会注意到上述所有答案都有效,但仅适用于某些字符串。 在其他情况下,手册中的校验和与我们使用上面发布的方法得到的完全不同。

      我发布了两个示例,1 个失败,1 个成功 => 相同的算法。

      为方便起见(由于协议使用 HEX 字符,我将包含实际的字节数组,以便阅读此内容的任何人都可以使用他/她选择的语言进行尝试)。 我希望有人可以在这里找到解决方案:

      1. 示例一:@Just My Correct Opinion 的解决方案失败:
      /***
      
      RAW STRING:
      P<FS>9300<FS>1<FS>1<FS>0<FS>
      
      BYTE ARRAY:
      {80, 28, 57, 51, 48, 48, 28, 49, 28, 49, 28, 48, 28}
      
      EXPECTED CHECKSUM : 
      6C
      
      ACTUAL CHECKSUM : 
      3A
      
      CODE TO REPRODUCE BELOW:
      ***/
      
      byte[] byteArry = {80, 28, 57, 51, 48, 48, 28, 49, 28, 49, 28, 48, 28};
      String ss = new String(byteArry);
      int s = 0;
      
      for(int i = 0; i < ss.length(); i++)
          {
              s += ss.charAt(i) & 0x7f;
              s &= 0xff;
          }
          System.out.println(s);
          System.out.format("Checksum: %02X\n", s);
      }
      
      
      1. 示例二:@Just My Correct Opinion 的解决方案成功:
      /***
      
      
      RAW STRING:
      M<FS>A<FS><FS>
      
      BYTE ARRAY:
      {77, 28, 65, 28, 28}
      
      EXPECTED CHECKSUM : 
      E2
      
      ACTUAL CHECKSUM : 
      E2
      
      CODE TO REPRODUCE BELOW:
      ***/
      
      byte[] byteArry = {77, 28, 65, 28, 28};
      String ss = new String(byteArry);
      int s = 0;
      
      for(int i = 0; i < ss.length(); i++)
          {
              s += ss.charAt(i) & 0x7f;
              s &= 0xff;
          }
          System.out.println(s);
          System.out.format("Checksum: %02X\n", s);
      }
      
      

      鉴于上述示例,我注意到它似乎会在字节总和超过某个值时立即失败(我不能确定),但我认为它在 256 左右。任何高于 256 的结果都会给出结果不同于西门子员工似乎想要的任何东西。 第一个示例已大大超过 256,但第二个示例为 226。手册中还有另一个示例字节和更小,我们上面的代码似乎可以工作,产生预期的校验和。 我不是一个很好的程序员,但我确信这与溢出有关。

      巴尔加夫·R.

      【讨论】:

      • 你有没有让通信正常工作?如果可以的话,你能分享一些代码吗,我正盯着一个有同样问题的 RAPIDPoint 500
      【解决方案5】:

      这个(未经测试的)类似 JavaScript 的函数能解决您的问题吗?

      function calc_checksum( DATA ) {
        var i;
        var checksum = 0;
        for ( i = 0; i < DATA.length; ++i ) { 
          checksum += DATA[i];      // any carry just falls off the high-order end
        }
        return 0x7F & checksum;
      }
      

      【讨论】:

      • 我认为 OP 正在寻找 8 位校验和,而不是 7 位校验和。
      猜你喜欢
      • 2016-10-31
      • 1970-01-01
      • 2020-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-24
      • 1970-01-01
      • 2018-09-15
      相关资源
      最近更新 更多