6

I'm trying to evaluate appropriate checksum based on CRC-16 algorithm using crcmod Python module and 2.7 version of Python interpreter. The checksum parameters are:

  • CRC order: 16
  • CRC polynomial: 0x8005
  • Inital value: 0xFFFF
  • Final value: 0x0000
  • Direct: True

Code:

crc16 = crcmod.mkCrcFun(0x18005, rev=False, initCrc=0xFFFF, xorOut=0x0000)
print hex(crc16(str(int(0x5A0001))))

and for the input 0x5A0001 it prints 0x7E16 while I should get something like 0xCE0A.

I checked on http://www.lokker.net/Java/crc/CRCcalculation2.htm and the computed value is 0xACE which is correct (with respect to the order).

Qrlet
  • 91
  • 1
  • 2
  • 8
  • 2
    Looks like you have `0x18005` as your polynomial in the python code, but you listed `0x8005` in your checksum parameters above. – djhoese Feb 04 '16 at 16:09
  • 2
    No, `0x18005` is correct for `crcmod`. That package determines the number of bits in the CRC from the complete polynomial. It is common to provide a CRC polynomial without the high term, e.g. `0x8005` and separately specify that it is a 16-bit CRC. – Mark Adler Feb 05 '16 at 00:23
  • First off, you created `c16` and then tried to use `crc16`. Did you mean `c16`? Second, what exactly do you think you are computing the CRC of? You do know that `str(int(0x5A0001))` returns the string of ASCII digits `5898241`, yes? What did you input into the web CRC calculator? – Mark Adler Feb 05 '16 at 00:30
  • Yes, it was a typo. The input into the calcuator was %5A%00%01 with appropriate parameters. – Qrlet Feb 05 '16 at 06:11
  • 2
    Consider also `binascii.crc_hqx(data, 0)`: docs [here](https://docs.python.org/3/library/binascii.html#binascii.crc_hqx) – hoc_age Oct 24 '19 at 02:48

6 Answers6

6

crcmod is working fine. You are not giving it the three bytes you think you are giving it. Your str(int(0x5A0001)) is providing seven bytes, which are the ASCII characters 5898241 — the conversion of 0x5a0001 to decimal.

To feed it the bytes 0x5a 0x00 0x01, you would instead (as one approach):

print hex(crc16("5a0001".decode("hex")))

That prints 0xace.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158
  • Thanks Mark! Now the crc is being calculated properly. Is it possible to express those 3 separate bytes: **0x5a** **0x00** **0x01** as one integer or am i missing something? – Qrlet Feb 07 '16 at 15:01
  • Sure, you could write something to extract those three bytes from the integer `0x5a0001`, e.g. `(x >> 16) & 0xff`, `(x >> 8) & 0xff`, and `x & 0xff`. But why would you want to do that? – Mark Adler Feb 07 '16 at 15:26
  • I was thinking how to divide `0x5a0001` into those 3 bytes without using `.decode(str)`. In other words, how i can for example calculate the checksum for `0x5a00` and `0x01` ? – Qrlet Feb 09 '16 at 09:42
  • 1
    First you would need to know decide somehow many bytes to use from each integer. Is it `0x5a00` or `0x005a00` or `0x00005a00`? It doesn't make sense to ask "what is the CRC of `0x5a00`", since a CRC is applied to a sequence of bytes (or bits), not to integers. – Mark Adler Feb 09 '16 at 14:39
6

Here is a python implementation of CRC-16/CCITT-FALSE

def crc16(data : bytearray, offset , length):
    if data is None or offset < 0 or offset > len(data)- 1 and offset+length > len(data):
        return 0
    crc = 0xFFFF
    for i in range(0, length):
        crc ^= data[offset + i] << 8
        for j in range(0,8):
            if (crc & 0x8000) > 0:
                crc =(crc << 1) ^ 0x1021
            else:
                crc = crc << 1
    return crc & 0xFFFF
  • data : bytearray of the data you want to calculate CRC for
  • offset : from which offset you want to start calculating CRC
  • length : to which offset you want to calculate CRC
Amin Saidani
  • 69
  • 1
  • 2
  • 1
    Didn't work for me with python3, seemed to infinite loop – alastairtree Jul 29 '22 at 11:50
  • It works for me on Python 3. The output matches https://crccalc.com if you enter the same input data then click "CRC-16" then look at the first column of the "CRC-16/CCITT-FALSE" row. Keep in mind that if you place any spaces or newlines in the field, the output will not match if your Python string does not have that (I pressed enter and didn't realize why the output differed, then backspaced and it matched). – Poikilos Feb 22 '23 at 20:34
2

A working single function example for CRC-16-ANSI, CRC-16-IBM based on pycrc code.

It is easy to modify but input or output reflection capability is not included:

def crc16(data: bytes):
    xor_in = 0x0000  # initial value
    xor_out = 0x0000  # final XOR value
    poly = 0x8005  # generator polinom (normal form)

    reg = xor_in
    for octet in data:
        # reflect in
        for i in range(8):
            topbit = reg & 0x8000
            if octet & (0x80 >> i):
                topbit ^= 0x8000
            reg <<= 1
            if topbit:
                reg ^= poly
        reg &= 0xFFFF
        # reflect out
    return reg ^ xor_out

Sz'
  • 83
  • 1
  • 5
  • This is CRC-16/BUYPASS algorithm apparently, by matching the output to results at https://crccalc.com/ (tested "123456789" and "ab" using Python 3). – Poikilos Feb 17 '23 at 16:32
1

summary of all answers

# https://stackoverflow.com/questions/35205702/calculating-crc16-in-python
# some crc16 variants in python
# expected values: https://crccalc.com/

test_data = [
  b"123456789",
]

def test(name, fn):
  for x in test_data:
    res = fn(x)
    print(f"{repr(x)} -> dec {res} = hex 0x{res:04X} # {name}")

# CRC-16/CCITT-FALSE
def crc16_ccitt_false(data : bytearray, offset , length):
    if data is None or offset < 0 or offset > len(data)- 1 and offset+length > len(data):
        return 0
    crc = 0xFFFF
    for i in range(0, length):
        crc ^= data[offset + i] << 8
        for j in range(0,8):
            if (crc & 0x8000) > 0:
                crc =(crc << 1) ^ 0x1021
            else:
                crc = crc << 1
    return crc & 0xFFFF
def wrapfn(fn):
    def wrapped(x):
        return fn(x, 0, len(x))
    return wrapped
test("CRC-16/CCITT-FALSE", wrapfn(crc16_ccitt_false))

# CRC-16/BUYPASS, CRC-16-ANSI, CRC-16-IBM
def crc16_buypass(data: bytes):
    xor_in = 0x0000  # initial value
    xor_out = 0x0000  # final XOR value
    poly = 0x8005  # generator polinom (normal form)
    reg = xor_in
    for octet in data:
        # reflect in
        for i in range(8):
            topbit = reg & 0x8000
            if octet & (0x80 >> i):
                topbit ^= 0x8000
            reg <<= 1
            if topbit:
                reg ^= poly
        reg &= 0xFFFF
        # reflect out
    return reg ^ xor_out
test("CRC-16/BUYPASS", crc16_buypass)

# https://docs.python.org/3/library/binascii.html
import binascii
def crc16_xmodem(data: bytes):
  return binascii.crc_hqx(data, 0)
test("CRC-16/XMODEM = binascii.crc_hqx", crc16_xmodem)

def crc16_modbus(data : bytearray, offset, length):
    if data is None or offset < 0 or offset > len(data) - 1 and offset + length > len(data):
        return 0
    #print("uzunluk=", len(data))
    #print(data)
    crc = 0xFFFF
    for i in range(length):
        crc ^= data[offset + i]
        for j in range(8):
            #print(crc)
            if ((crc & 0x1) == 1):
                #print("bb1=", crc)
                crc = int((crc / 2)) ^ 40961
                #print("bb2=", crc)
            else:
                crc = int(crc / 2)
    return crc & 0xFFFF
def wrapfn(fn):
    def wrapped(x):
        return fn(x, 0, len(x))
    return wrapped
test("CRC-16/MODBUS", wrapfn(crc16_modbus))

# CRC32 ...

import binascii
def crc32(data: bytes):
  return binascii.crc32(data, 0)
#test("binascii.crc32 = CRC-32", crc32)

# https://docs.python.org/3/library/zlib.html
import zlib
def crc32(data: bytes):
  return zlib.crc32(data, 0)
#test("zlib.crc32 = CRC-32", crc32)

output

b'123456789' -> dec 10673 = hex 0x29B1 # CRC-16/CCITT-FALSE
b'123456789' -> dec 65256 = hex 0xFEE8 # CRC-16/BUYPASS
b'123456789' -> dec 12739 = hex 0x31C3 # CRC-16/XMODEM = binascii.crc_hqx
b'123456789' -> dec 19255 = hex 0x4B37 # CRC-16/MODBUS

see also

milahu
  • 2,447
  • 1
  • 18
  • 25
0
def crc16(data : bytearray, offset, length):
    if data is None or offset < 0 or offset > len(data) - 1 and offset + length > len(data):
        return 0
    print("uzunluk=", len(data))
    print(data)

    crc = 0xFFFF
    for i in range(length):
        crc ^= data[offset + i]
        for j in range(8):
            print(crc)
            if ((crc & 0x1) == 1):
                print("bb1=", crc)
                crc = int((crc / 2)) ^ 40961
                print("bb2=", crc)
            else:
                crc = int(crc / 2)
    return crc & 0xFFFF
Matphy
  • 1,086
  • 13
  • 21
-1

Here is a code you can use to generate a crc 16 for a data packet to send

def crc16_generator_hex(data: list[int]) -> str:
"""CRC-16-MODBUS Hex Algorithm
Parameters
----------
data : list[int]
    Data packets received.
Returns
-------
str
    CRC as hex string

Raises
----------
ValueError
    If data packet in each index contains a byte > 256
"""
data = bytearray(data)
crc = 0xFFFF

# Calculate CRC-16 checksum for data packet
for b in data:
    crc ^= b
    for _ in range(0, 8):
        bcarry = crc & 0x0001
        crc >>= 1
        if bcarry:
            crc ^= 0xa001

return hex(crc)

I have a repo with detailed examples you can use as a reference. https://github.com/jonahbardos/Python-CRC16