Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CRC16 ISO 13239 Implementation

Tags:

c#

crc

crc16

i'm trying to implement Crc16 in C#. I already tried many different implementations, but most of them gives me different values. Here are some of the codes that i already used.

    private static int POLYNOMIAL = 0x8408;
    private static int PRESET_VALUE = 0xFFFF;

    public static int crc16(byte[] data)
    {
        int current_crc_value = PRESET_VALUE;
        for (int i = 0; i < data.Length; i++)
        {
            current_crc_value ^= data[i] & 0xFF;
            for (int j = 0; j < 8; j++)
            {
                if ((current_crc_value & 1) != 0)
                {
                    current_crc_value = (current_crc_value >> 1) ^ POLYNOMIAL;
                }
                else
                {
                    current_crc_value = current_crc_value >> 1;
                }
            }
        }
        current_crc_value = ~current_crc_value;

        return current_crc_value & 0xFFFF;
    }

this is the another implementation that i used but both gives different values

    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;
        }
    }

The value I`m using is an Octet String "[jp3]TEST [fl]Flashing[/fl]" and its expected value is 95F9 in hex. This is an example on the guide of NTCIP protocol

Thanks

like image 535
Fabio Reis Avatar asked Sep 03 '13 14:09

Fabio Reis


2 Answers

This:

static readonly ushort[] fcstab = new ushort[] { 
    0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 
    0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 
    0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 
    0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 
    0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 
    0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 
    0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 
    0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 
    0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 
    0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 
    0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 
    0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 
    0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 
    0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 
    0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 
    0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 
    0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 
    0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 
    0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 
    0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 
    0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 
    0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 
    0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 
    0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 
    0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 
    0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 
    0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 
    0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 
    0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 
    0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 
    0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 
    0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 
};

static ushort compute_fcs(byte[] data)
{
    return compute_fcs(data, 0, data.Length);
}

static ushort compute_fcs(byte[] data, int start, int length)
{
    ushort fcs = 0xFFFF;

    int end = start + length;

    for (int i = start; i < end; i++)
    {
        fcs = (ushort)(((ushort)(fcs >> 8)) ^ fcstab[(fcs ^ data[i]) & 0xFF]);
    }

    return (ushort)(~fcs);
}

static void Main(string[] args)
{
    byte[] pattern = new byte[] { 0x02, 0x07, 0x01, 0x03, 0x01, 0x02, 0x00, 0x34, 0x07, 0x07, 0x1C, 0x59, 0x34, 0x6F, 0xE1, 0x83, 0x00, 0x00, 0x41, 0x06, 0x06, 0x7B, 0x3C, 0xFF, 0xCF, 0x3C, 0xC0 };

    // http://www.ite.org/standards/1203v03-04%20Part%201%20dms2011.pdf
    // Page 158, CRC = 0x52ED
    ushort fcs = compute_fcs(pattern); // 0x52ED
}

will work for the only test given here http://www.ite.org/standards/1203v03-04%20Part%201%20dms2011.pdf (around page 158, CRC = 0x52ED).

For the string example of the PDF, as written some pages later:

" Indicates the CRC-16 (polynomial defined in ISO/IEC 3309) value created using the values of the dmsMessageMultiString (MULTI-Message), the dmsMessageBeacon, and the dmsMessagePixelService objects in the order listed, not including the OER type or length fields. Note that the calculation shall assume a value of zero (0) for the dmsMessageBeacon object and/or for the dmsMessagePixelService object if they are not supported

(emphasis added)

so:

string str = "[jp3]TEST [fl]Flashing[/fl]";
var bytes = Encoding.ASCII.GetBytes(str);
Array.Resize(ref bytes, bytes.Length + 2);

// Note that these two rows are useless, because the Array.Resize will have already filled with 0
bytes[bytes.Length - 2] = 0; // dmsMessageBeacon
bytes[bytes.Length - 1] = 0; // dmsMessagePixelService 

ushort fcs2 = compute_fcs(bytes); // 0xF995
var bytes2 = BitConverter.GetBytes(fcs2); // 0x95 0xF9

This shows that the protocol is little endian (as my PC, that is an Intel). In fact the CRC-16 of the string is 0xF995, but these 16 bits in memory appear as 0x95 0xF9 (as in the example, that shows the single bytes).

like image 85
xanatos Avatar answered Oct 11 '22 19:10

xanatos


There are many algorithms of CRC-16 calculation.

For instance:

  • CRC-16-IBM which used in Modbus protocol, USB etc. is most popular.
  • CRC-16-CCITT used in Bluetooth.

I use CRC-16-IBM in my applications which work with industrial controllers.

public static UInt16 FastCRC16(byte[] Buffer, UInt16 ui_length)
{

UInt16[] crc_table = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040};

        UInt16 Crc = 65535;
        UInt16 x;
        for (UInt16 i = 0; i < ui_length; i++)
        {
            x = (UInt16)(Crc ^ Buffer[i]);
            Crc = (UInt16)((Crc >> 8) ^ crc_table[x & 0x00FF]);
        }

        return Crc;
}
like image 36
Artem Tsetkhalin Avatar answered Oct 11 '22 19:10

Artem Tsetkhalin