1

I'm trying to implement a 16-CRC [DNP] using c#, the generator polynomial is given as

enter image description here

I found a standard solution for 16-crc : [ Source ]

public class Crc16
{
    const ushort polynomial = 0xA001;
    ushort[] table = new ushort[256];

    public ushort ComputeChecksum ( byte[] bytes )
    {
        ushort crc = 0;
        for ( int i = 0; i < bytes.Length; ++i )
        {
            byte index = ( byte ) ( crc ^ bytes[i] );
            crc = ( ushort ) ( ( crc >> 8 ) ^ table[index] );
        }
        return crc;
    }

    public byte[] ComputeChecksumBytes ( byte[] bytes )
    {
        ushort crc = ComputeChecksum ( bytes );
        return BitConverter.GetBytes ( crc );
    }

    public Crc16 ()
    {
        ushort value;
        ushort temp;
        for ( ushort i = 0; i < table.Length; ++i )
        {
            value = 0;
            temp = i;
            for ( byte j = 0; j < 8; ++j )
            {
                if ( ( ( value ^ temp ) & 0x0001 ) != 0 )
                {
                    value = ( ushort ) ( ( value >> 1 ) ^ polynomial );
                }
                else
                {
                    value >>= 1;
                }
                temp >>= 1;
            }
            table[i] = value;
        }
    }
}

Now, If I convert my polynomial I get 1 0011 1101 0110 0111 => (3D65)h & my question is what do I need to change to work the above solution for the given polynomial.

Edit: I also need to consider two things,

1) The initial value will be 0 &
2) The final CRC has to be complemented.

SanVEE
  • 2,009
  • 5
  • 34
  • 55

3 Answers3

3

This was actually very helpful for me. However, I did not use the solution SanVEE did, I actually modified the code from his original post as described by Mark Adler and it works great. At least, so far the result matches up with the DNP3 checksum calculator found here: http://www.lammertbies.nl/comm/info/crc-calculation.html

The code posted as the answer for SanVEE looks like it might be very inefficient (e.g. using bools to store each bit), though I have not tested them to compare. Anyone facing the same question may want to examine both answers to see which works better for them.

    public class Crc16DNP3
    {
        const ushort polynomial = 0xA6BC; //0xA001;
        ushort[] table = new ushort[256];

        public ushort ComputeChecksum(byte[] bytes)
        {
            ushort crc = 0;
            for (int i = 0; i < bytes.Length; ++i)
            {
                byte index = (byte)(crc ^ bytes[i]);
                crc = (ushort)((crc >> 8) ^ table[index]);
            }
            crc = SwapBytes((ushort)(crc ^ 0xffff));
            return crc;
        }

        public byte[] ComputeChecksumBytes(byte[] bytes)
        {
            ushort crc = ComputeChecksum(bytes);
            return BitConverter.GetBytes(crc);
        }

        // SwapBytes taken from http://stackoverflow.com/questions/19560436/bitwise-endian-swap-for-various-types
        private ushort SwapBytes(ushort x)
        {
            return (ushort)((ushort)((x & 0xff) << 8) | ((x >> 8) & 0xff));
        }

        public Crc16DNP3()
        {
            ushort value;
            ushort temp;
            for (ushort i = 0; i < table.Length; ++i)
            {
                value = 0;
                temp = i;
                for (byte j = 0; j < 8; ++j)
                {
                    if (((value ^ temp) & 0x0001) != 0)
                    {
                        value = (ushort)((value >> 1) ^ polynomial);
                    }
                    else
                    {
                        value >>= 1;
                    }
                    temp >>= 1;
                }
                table[i] = value;
            }
        }
    }
Derek
  • 145
  • 6
2

What's wrong with the code at your first link? That also specifies how the CRC bytes are ordered in the message.

You need to reverse the polynomial below x16. The polynomial in bit form is 10011110101100101. Drop the leading 1 (x16), and you have in groups of four: 0011 1101 0110 0101. Reversed that is: 1010 0110 1011 1100. So you should set polynomial = 0xA6BC.

The initial value is already zero. Complementing the final CRC can be done simply with ^ 0xffff.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158
  • Sorry I wasnt clear before, please see the Edit section above, also could you please explain how did you get `0XA6BC` ? – SanVEE Sep 19 '14 at 16:07
1

Finally, I ended up using the following solution & thought it's worth sharing & it may be useful for someone.

private static int GetCrc ( string BitString )
    {
        bool[] Res = new bool[17];
        bool[] CRC = new bool[16];
        int i;
        bool DoInvert = false;
        string crcBits = string.Empty;

        for ( i = 0; i < 16; ++i ) // Init before calculation
            CRC[i] = false;

        for ( i = 0; i < BitString.Length; ++i )
        {
            DoInvert = ('1' == BitString[i]) ^ CRC[15]; // XOR required?

            CRC[15] = CRC[14];
            CRC[14] = CRC[13];
            CRC[13] = CRC[12] ^ DoInvert;
            CRC[12] = CRC[11] ^ DoInvert;
            CRC[11] = CRC[10] ^ DoInvert;
            CRC[10] = CRC[9] ^ DoInvert;
            CRC[9] = CRC[8];
            CRC[8] = CRC[7] ^ DoInvert;
            CRC[7] = CRC[6];
            CRC[6] = CRC[5] ^ DoInvert;
            CRC[5] = CRC[4] ^ DoInvert;
            CRC[4] = CRC[3];
            CRC[3] = CRC[2];
            CRC[2] = CRC[1] ^ DoInvert;
            CRC[1] = CRC[0];
            CRC[0] = DoInvert;
        }

        for ( i = 0; i < 16; ++i )
            Res[15 - i] = CRC[i] ? true : false;

        Res[16] = false;

        // The final result must be Complemented            
        for ( i = 0; i < 16; i++ )
        {
            if ( Res[i] )
                crcBits += "0";
            else
                crcBits += "1";
        }

        return Convert.ToInt32 ( crcBits, 2 );
    }

The above C# solution is converted from C based auto generated code from here.

SanVEE
  • 2,009
  • 5
  • 34
  • 55