How to calculate this CRC using Python?

17,633

Solution 1

According to the cited document, the algorithm is actually a standard 16 Bit CCITT CRC. This can be calculated with crcmod.

Here you go:

import crcmod

# this is a standard CCITT CRC even if it does not look like
# (crcmod applies xorOut to initCrc, so initCrc is in reality 0xffff, not 0)
_CRC_FUNC = crcmod.mkCrcFun(0x11021, initCrc=0, xorOut=0xffff)

data = bytearray.fromhex("023b010000000000")
crc = _CRC_FUNC(data)
data.append(crc & 0xff)
data.append(((crc >> 8) & 0xff))

print (data.hex())

Output: 023b010000000000ff2c

Solution 2

You need to apply that algorithm to each byte of your message. A slight complication is that the algorithm given in the Aurora PDF file assumes the calculation is being performed with 8 bit unsigned arithmetic. To handle that in Python we can use a bitmask of 0xff. Here's a slightly optimized version of that code.

def crc_16(msg):
    lo = hi = 0xff
    mask = 0xff
    for new in msg:
        new ^= lo
        new ^= (new << 4) & mask
        tmp = new >> 5
        lo = hi
        hi = new ^ tmp
        lo ^= (new << 3) & mask
        lo ^= new >> 4
    lo ^= mask
    hi ^= mask
    return hi << 8 | lo

# Test

msg = bytes.fromhex("023b010000000000")
out = crc_16(msg)
hi, lo = out >> 8, out & 0xff
print('{:04x} = {:02x} {:02x}'.format(out, hi, lo))

output

2cff = 2c ff

The above code works, but there are simpler ways to calculate CRCs. And we can use a table to speed up the process, if you need to calculate a lot of CRCs.

As the Wikipedia Cyclic redundancy check article mentions, CRC algorithms are usually specified in terms of a polynomial encoded as a hexadecimal number. Here's a function that does that using the reversed polynomial representation.

def crc_16_CCITT(msg):
    poly = 0x8408
    crc = 0xffff
    for byte in msg:
        for _ in range(8):
            if (byte ^ crc) & 1:
                crc = (crc >> 1) ^ poly
            else:
                crc >>= 1
            byte >>= 1
    return crc ^ 0xffff

To speed things up, we can compute a table.

def make_crc_table():
    poly = 0x8408
    table = []
    for byte in range(256):
        crc = 0
        for bit in range(8):
            if (byte ^ crc) & 1:
                crc = (crc >> 1) ^ poly
            else:
                crc >>= 1
            byte >>= 1
        table.append(crc)
    return table

table = make_crc_table()

def crc_16_fast(msg):
    crc = 0xffff
    for byte in msg:
        crc = table[(byte ^ crc) & 0xff] ^ (crc >> 8)
    return crc ^ 0xffff

# Test

msg = bytes.fromhex("023b010000000000")
out = crc_16_fast(msg)
hi, lo = out >> 8, out & 0xff
print('{:04x} = {:02x} {:02x}'.format(out, hi, lo))

If you like, you can print the table & paste it into your script, so that you don't have to compute the table every time you run the script.

Share:
17,633

Related videos on Youtube

Simone Luconi
Author by

Simone Luconi

Updated on June 11, 2022

Comments

  • Simone Luconi
    Simone Luconi almost 2 years

    I need to calculate this CRC using Python for the communication with Aurora (ABB) solar inverter.

    This is the document: http://www.drhack.it/images/PDF/AuroraCommunicationProtocol_4_2.pdf in the last page there are the instructions to calculate the CRC, i need to do that in python.

    The message that i have is

    MESSAGE_GRID_VOLTAGE = bytes.fromhex("023b010000000000")
    

    The results should be:

    CRC_L = FF

    CRC_H = 2C

    Then i need to send the message complete with the CRC like this:

    MESSAGE_GRID_VOLTAGE = bytes.fromhex("023b010000000000ff2c")
    

    How can i do that in python? Thanks!

    Here is the code that i tried:

    message = "023b010000000000"
    
    BccLo= int ("FF",16)
    BccHi= int("FF", 16)
    
    New = int(message, 16)
    
    New = New ^ BccLo
    Tmp=New << 4
    New=Tmp ^ New
    Tmp=New >> 5
    BccLo=BccHi
    BccHi= New ^ Tmp
    Tmp=New << 3
    BccLo=BccLo ^ Tmp
    Tmp=New >> 4
    BccLo=BccLo ^ Tmp
    
    CRC_L = ~BccLo
    CRC_H = ~BccHi
    
    • Klaus D.
      Klaus D. over 5 years
      What have you tried so far?
    • Simone Luconi
      Simone Luconi over 5 years
      I tried using the ^ operator, but it doesen't support bytes, maybe i should try to convert the bytes to a number and then recovert it to bytes, i have the fear to mess up something with theese to many conversion.
    • Simone Luconi
      Simone Luconi over 5 years
      i found this code in c#, still i have the same problem if i try to convert it into python: dreamincode.net/forums/topic/193903-crc-calculation-problem
    • Simone Luconi
      Simone Luconi over 5 years
      i tried it in c#, but it doesn't work, the crc is not correct, even if i try with the example messages written by the user.
    • Klaus D.
      Klaus D. over 5 years
      Show us some Python code! Here on SO it is expected that you show us the efforts you made to solve the problem.
    • Simone Luconi
      Simone Luconi over 5 years
      i added it to the question
    • Simone Luconi
      Simone Luconi over 5 years
      made some progress, i updated the answer, but the output is not correct
    • Mark Adler
      Mark Adler about 2 years
      For future reference, specification of that particular CRC: width=16 poly=0x1021 init=0xffff refin=true refout=true xorout=0xffff check=0x906e residue=0xf0b8 name="CRC-16/IBM-SDLC"
  • Simone Luconi
    Simone Luconi over 5 years
    Thanks, I appreciate the work that you have done to explain the process, but the answer from the user "Adrian W" its simple to implement
  • PM 2Ring
    PM 2Ring over 5 years
    crcmod isn't a standard Python module, but I suppose it's easy enough to install from PyPI.
  • Adrian W
    Adrian W over 5 years
    You're right, crcmod is not part of the standard distribution. You can install it with pip install crcmod. See also pypi.org/project/crcmod
  • skyking
    skyking about 2 years
    I'd suggest updating the answer to reflect that crcmod isn't standard.
  • Admin
    Admin about 2 years
    As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.