开发者

8-bit binary addition

Can someone explain how to calculate checksum with 8-bit binary addition? This is an excerpt from the documentation:

This is the general form of the messages:

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

STX is HEX 02

ETX is HEX 03

FS is HEX 15

The "type" is a uniqu开发者_运维问答e, 1-byte message identifier (e.g., 'P' for Poll message). "Data" contains printable ASCII characters.

Checksum

The Checksum is computed on all characters, including all the <FS> characters, between <STX> and <CHK>. The Checksum is calculated by the 8-bit binary addition of all included characters with the 8th or parity bit assumed to be zero. Carries beyond the 8th bit are lost. The 8-bit result is converted into two printable ASCII Hex characters, ranging from 00 to FF, which are then inserted into the data stream as <CHK>. The Hex characters A-F are uppercase. The receiving device recalculates the checksum on the buffered message and compares it with the checksum it received. The comparison is the basis for subsequent acknowledgement (<ACK>) or negative acknowledgement (<NAK>) of the transmission.


Treat each character as an integer value. Since each character's high bit is assumed to be zero (as in the spec doesn't say you need to check it), mask its value with something like this (pseudo-C/C++/Java/whatever):

get_next_character() & 0x7f;

Now you just do the add (pseudo-C/C++/Java/whatever):

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

This will consecutively add each ASCII character and remove everything past the 8th bit from the resulting sum. When you're all finished (C or badly-written C++):

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

As an optimization (if you really need it -- unlikely in this case!) you can defer the s &= 0xff bit and instead use the truncation at the point of use for the checksum. This won't save you much on performance, however -- your I/O will be far more expensive -- and leads to the possibility of you forgetting to do it at some later date when you refactor your code.


For addition, use the following function.

function Summatory(const Data: AnsiString): Byte;
var
    C: AnsiChar;
begin
    Result := 0;

    for C in Data do
    begin
        Result := Result + Ord(C);
    end;
end;

For older versions of Delphi, with no "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;

The function Summatory is declared as Byte, so it will "ignore" carries beyond the 8th bit. You can pass all of the bytes that you want to add.

Use the function IntToHex from SysUtils to converted the 8-bit result into two printable ASCII Hex characters.

Ex: ChkSum := IntToHex(Summatory(Data), 2);


In Java you can do the following.

byte[] bytes =
byte total = 0;
for(byte b: bytes) total += b;

OutputStream os = 
os.write(total);


None of the answers above work, when the sum of the bytes crosses 256.

The OP was probably trying to interface a Siemens Dimension EXL200 chemistry analyzer with his _Lab information system.

The paragraph he's posted (about the spec for the checksum) is copy/paste from their manual. I've been banging my head against this particular problem for the last 12 hours and cannot find any way around it. I am including some of the strings mentioned in the Siemens Manual, as they have provided the checksums.

You will notice that all the answers above work, but only for some of the strings. In others, the checksum in the manual is completely different from what we get by using the methods posted above.

I am posting two examples, 1 failure, and 1 success => same algorithm.

For convenience(as the protocol uses HEX characters, I'm including the actual byte arrays, so that anyone reading this can try it in his/her language of choice). I hope someone can find a solution here:

  1. Example One: @Just My Correct Opinion's Solution Fails:
/***

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. Example Two: @Just My Correct Opinion's Solution SUCCEEDS:
/***


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);
}

Given the above examples, I've noticed that it seems to fail as soon as the sum of the bytes crosses a certain value(what I cannot be sure), but I think its around 256. Anything above 256 gives a result different from whatever the people at Siemens seem to want. The first example has crossed 256 by a big margin, but the second one is at 226. There is another example in the manual of an even smaller byte sum, where our code above seems to work, producing the expected checksum. I'm not a very good programmer, but I'm sure this has something to do with overflows.

Bhargav R.


Does this (untested) JavaScript-like function solve your problem?

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;
}

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜