4
votes

I have instructions on creating a checksum of a message described like this:

The checksum consists of a single byte equal to the two’s complement sum of all bytes starting from the “message type” word up to the end of the message block (excluding the transmitted checksum). Carry from the most significant bit is ignored.

Another description I found was: The checksum value contains the twos complement of the modulo 256 sum of the other words in the data message (i.e., message type, message length, and data words). The receiving equipment may calculate the modulo 256 sum of the received words and add this sum to the received checksum word. A result of zero generally indicates that the message was correctly received.

I understand this to mean that I sum the value of all bytes in message (excl checksum), get modulo 256 of this number. get twos complement of this number and that is my checksum.

But I am having trouble with an example message example (from design doc so I must assume it has been encoded correctly).

unsigned char arr[] = {0x80,0x15,0x1,0x8,0x30,0x33,0x31,0x35,0x31,0x30,0x33,0x30,0x2,0x8,0x30,0x33,0x35,0x31,0x2d,0x33,0x32,0x31,0x30,0xe};

So the last byte, 0xE, is the checksum. My code to calculate the checksum is as follows:

bool isMsgValid(unsigned char arr[], int len) {
   int sum = 0;
   for(int i = 0; i < (len-1); ++i) {
      sum += arr[i];
   }
   //modulo 256 sum
   sum %= 256;

   char ch = sum;

   //twos complement
   unsigned char twoscompl = ~ch + 1;

   return arr[len-1] == twoscompl;
}


int main(int argc, char* argv[])
{
   unsigned char arr[] = {0x80,0x15,0x1,0x8,0x30,0x33,0x31,0x35,0x31,0x30,0x33,0x30,0x2,0x8,0x30,0x33,0x35,0x31,0x2d,0x33,0x32,0x31,0x30,0xe};
   int arrsize = sizeof(arr) / sizeof(arr[0]);

   bool ret = isMsgValid(arr, arrsize);

   return 0;
}

The spec is here:= http://www.sinet.bt.com/227v3p5.pdf

I assume I have misunderstood the algorithm required. Any idea how to create this checksum?

Flippin spec writer made a mistake in their data example. Just spotted this then came back on here and found others spotted too. Sorry if I wasted your time. I will study responses because it looks like some useful comments for improving my code.

3
I think the checksum in the data (0xe) is not correct. It appears to be just the one's complement of the 8-bit sum of the previous bytes. - Warren Weckesser
The checksum is correct. See my answer. - Emile Cormier
Be aware that if one of the values is one less and another value is one more, the checksum will pass. For example, original set {1,2,3,4,5}, invalid set (but passes) {1,3,3,3,5}. This kind of checksum error cost me 4 days of debugging! - Thomas Matthews

3 Answers

6
votes

You miscopied the example message from the pdf you linked. The second parameter length is 9 bytes, but you used 0x08 in your code.

The document incorrectly states "8 bytes" in the third column when there are really 9 bytes in the parameter. The second column correctly states "00001001".

In other words, your test message should be:

{0x80,0x15,0x1,0x8,0x30,0x33,0x31,0x35,0x31,0x30,0x33,0x30, // param1
 0x2,0x9,0x30,0x33,0x35,0x31,0x2d,0x33,0x32,0x31,0x30,0xe}  // param2
     ^^^

With the correct message array, ret == true when I try your program.

1
votes

Agree with the comment: looks like the checksum is wrong. Where in the .PDF is this data?

Some general tips:

Use an unsigned type as the accumulator; that gives you well-defined behavior on overflow, and you'll need that for longer messages. Similarly, if you store the result in a char variable, make it unsigned char.

But you don't need to store it; just do the math with an unsigned type, complement the result, add 1, and mask off the high bits so that you get an 8-bit result.

Also, there's a trick here, if you're on hardware that uses twos-complement arithmetic: just add all of the values, including the checksum, then mask off the high bits; the result will be 0 if the input was correct.

1
votes

The receiving equipment may calculate the modulo 256 sum of the received words and add this sum to the received checksum word.

It's far easier to use this condition to understand the checksum:

{byte 0} + {byte 1} + ... + {last byte} + {checksum} = 0    mod 256
{checksum} = -( {byte 0} + {byte 1} + ... + {last byte} )   mod 256

As the others have said, you really should use unsigned types when working with individual bits. This is also true when doing modular arithmetic. If you use signed types, you leave yourself open to a rather large number of sign-related mistakes. OTOH, pretty much the only mistake you open yourself up to using unsigned numbers is things like forgetting 2u-3u is a positive number.

(do be careful about mixing signed and unsigned numbers together: there are a lot of subtleties involved in that too)