I'm trying to port a C function which calculates a GPS checksum over to Python. According to the receiving end I am sometimes miscalculating the checksum, so must still have a bug in there.
C code is
void ComputeAsciiChecksum(unsigned char *data, unsigned int len,
unsigned char *p1, unsigned char *p2)
{
unsigned char c,h,l;
assert(Stack_Low());
c = 0;
while (len--) {
c ^= *data++;
}
h = (c>>4);
l = c & 0xf;
h += '0';
if (h > '9') {
h += 'A'-'9'-1;
}
l += '0';
if (l > '9') {
l += 'A'-'9'-1;
}
*p1 = h;
*p2 = l;
}
My attempt at a Python function is
def calcChecksum(line):
c = 0
i = 0
while i < len(line):
c ^= ord(line[i]) % 256
i += 1
return '%02X' % c;
2 Answers 2
Here is how you can set up a testing environment to diagnose your problem.
Copy the above C function to a file, remove the
assert()line, and compile it to a shared library withgcc -shared -o checksum.so checksum.c(If you are on Windows or whatever, do the equivalent of the above.)
Copy this code to a Python file:
import ctypes import random c = ctypes.CDLL("./checksum.so") c.ComputeAsciiChecksum.rettype = None c.ComputeAsciiChecksum.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_char_p] def compute_ascii_checksum_c(line): p1 = ctypes.create_string_buffer(1) p2 = ctypes.create_string_buffer(1) c.ComputeAsciiChecksum(line, len(line), p1, p2) return p1.value + p2.value def compute_ascii_checksum_py(line): c = 0 i = 0 while i < len(line): c ^= ord(line[i]) % 256 i += 1 return '%02X' % c;
Now you have access to both versions of the checksum function and can compare the results. I wasn't able to find any differences.
(BTW, how are you computing the length of the string in C? If you are using strlen(), this would stop at NUL bytes.)
As a side note, your Python version isn't really idiomatic Python. Here are two more idiomatic versions:
def compute_ascii_checksum_py(line):
checksum = 0
for c in line:
checksum ^= ord(c)
return "%02X" % checksum
or
def compute_ascii_checksum_py(line):
return "%02X" % reduce(operator.xor, map(ord, line))
Note that these implementations should do exactly the same as yours.
Comments
Have you checked out this cookbook recipe? It hints at what input you should include in "line", returns a asterisk in front of the checksum, and gives one (input, output) data pair that you can use as test data.
Are you sure that "the receiver" is working correctly? Is the problem due to upper vs lower case hex letters?
linedoesn't contain aunicodeobject, but rather astr(assuming you are on Python 2.x)? The fact that you% 256the result oford()looks suspicious -- for an ordinary string,ord()should never return anything outside the range0to255.print repr(line)and copy/paste the output into an edit of your question.lineand the checksum? Do you have a URL for the message layout/format?