fred basset
fred basset

Reputation: 10072

Need help porting C function to Python

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;

Upvotes: 1

Views: 291

Answers (2)

John Machin
John Machin

Reputation: 82924

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?

Upvotes: 0

Sven Marnach
Sven Marnach

Reputation: 601351

Here is how you can set up a testing environment to diagnose your problem.

  1. Copy the above C function to a file, remove the assert() line, and compile it to a shared library with

    gcc -shared -o checksum.so checksum.c
    

    (If you are on Windows or whatever, do the equivalent of the above.)

  2. 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.

Upvotes: 4

Related Questions