Search code examples
pythoncrccrc16xmodem

How to apply xmodem CRC on generating and checking


I've iterated of lots of articles - for some reason I couldn't find any which describes what must be a simple procedure: How to combine a calculated CRC with the original message, so that calculating CRC again results in 0 (=checks out as correct)? I did find several examples with 'longhand' calculations (only 2 or 3 bit CRCs), but no example which uses a library function such as [crcmod][1] (Python library).

Here's a simple program I wrote to check it out:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#

import crcmod

def test_cycle():
    test_str = b"123456789"
    orig_msg = test_str
    print("Original message: {:s}".format(orig_msg.hex()))

    #~ crc_calc = crcmod.predefined.Crc('xmodem')
    crc_calc = crcmod.Crc(
                0x11021,
                rev = False,
                initCrc = 0x0000,
                xorOut = 0x0000)

    crc_calc.update(orig_msg)
    print("CRC: {:04x}".format(crc_calc.crcValue))

    lo = crc_calc.crcValue & 0xff
    hi = crc_calc.crcValue >> 8
    new_msg = test_str + bytes((hi, lo))
    print("Crc appended: {:s}".format(new_msg.hex()))

    crc_calc.update(new_msg)
    print("CRC: {:04x}".format(crc_calc.crcValue))


def main(args):
    test_cycle()
    return 0

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))

There are some commented lines, from experiments with different byte ordering. The results from the program are:

Original message: 313233343536373839
CRC: 31c3
Crc appended: 31323334353637383931c3
CRC: 00ef

The first CRC (31C3) seems to check out with correspond with the expected values for xmodem-CRC. I tried, in many ways, to combine the obtained CRC with the original string, but never obtain '0'. Am I missing something here?


Solution

  • crc_calc.update(new_msg) adds the entire content of new_msg to the CRC. Since crc_calc already holds the result of 313233343536373839, you are effectively calculating the CRC of 31323334353637383931323334353637383931c3 and this is indeed 00ef.

    To add only the two bytes to the calculation, use

    crc_calc.update(bytes((hi, lo)))
    

    Alternatively, use a new instance of crcmod.Crc(), or reset the crcValue of the existing instance before doing a new calculation

    crc_calc.crcValue = 0
    crc_calc.update(new_msg)
    

    Both will give you a result of 0