Search code examples
c#javachecksum

Converting small C# checksum program into Java


I'm trying to build a simple ground control station for an RC airplane. I've almost finished it, but I'm having a LOT of trouble with the checksum calculation. I understand that the data types of Java and C# are different. I've attempted to account for that but I'm not sure I've succeeded. The program utilizes the CRC-16-CCITT method.

Here is my port:

public int crc_accumulate(int b, int crc) {
        int ch = (b ^ (crc & 0x00ff));
        ch = (ch ^ (ch << 4));
        return ((crc >> 8) ^ (ch << 8) ^ (ch << 3) ^ (ch >> 4));
}

public byte[] crc_calculate() {
        int[] pBuffer=new int[]{255,9,19,1,1,0,0,0,0,0,2,3,81,4,3};
        int crcEx=0;
        int clength=pBuffer.length;
        int[] X25_INIT_CRC=new int[]{255,255};
        byte[] crcTmp=new byte[]{(byte)255,(byte)255};
        int crcTmp2 = ((crcTmp[0] & 0xff) << 8) | (crcTmp[1] & 0xff);
        crcTmp[0]=(byte)crcTmp2;
        crcTmp[1]=(byte)(crcTmp2 >> 8);
        System.out.println("pre-calculation: 0x"+Integer.toHexString((crcTmp[0]&0xff))+" 0x"+Integer.toHexString((crcTmp[1]&0xff))+";   ushort: "+crcTmp2);
        if (clength < 1) {
                System.out.println("clength < 1");
                return crcTmp;
        }
        for (int i=1; i<clength; i++) {
                crcTmp2 = crc_accumulate(pBuffer[i], crcTmp2);
        }
        crcTmp[0]=(byte)crcTmp2;
        crcTmp[1]=(byte)(crcTmp2 >> 8);
        System.out.print("crc calculation: 0x"+Integer.toHexString((crcTmp[0]&0xff))+" 0x"+Integer.toHexString((crcTmp[1]&0xff))+";   ushort: "+crcTmp2);
        if (crcEx!=-1) {
                System.out.println("  extraCRC["+crcEx+"]="+extraCRC[crcEx]);
                crcTmp2=crc_accumulate(extraCRC[crcEx], crcTmp2);
                crcTmp[0]=(byte)crcTmp2;
                crcTmp[1]=(byte)(crcTmp2 >> 8);
        System.out.println("with extra CRC:  0x"+Integer.toHexString((crcTmp[0]&0xff))+" 0x"+Integer.toHexString((crcTmp[1]&0xff))+";   ushort: "+crcTmp2+"\n\n");
        }
        return crcTmp;
}

This is the original C# file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ArdupilotMega
{
class MavlinkCRC
{
    const int X25_INIT_CRC = 0xffff;
    const int X25_VALIDATE_CRC = 0xf0b8;

    public static ushort crc_accumulate(byte b, ushort crc)
    {
        unchecked
        {
            byte ch = (byte)(b ^ (byte)(crc & 0x00ff));
            ch = (byte)(ch ^ (ch << 4));
            return (ushort)((crc >> 8) ^ (ch << 8) ^ (ch << 3) ^ (ch >> 4));
        }
    }

    public static ushort crc_calculate(byte[] pBuffer, int length)
    {
        if (length < 1)
        {
            return 0xffff;
        }
        // For a "message" of length bytes contained in the unsigned char array
        // pointed to by pBuffer, calculate the CRC
        // crcCalculate(unsigned char* pBuffer, int length, unsigned short* checkConst) < not needed

        ushort crcTmp;
        int i;

        crcTmp = X25_INIT_CRC;

        for (i = 1; i < length; i++) // skips header U
        {
            crcTmp = crc_accumulate(pBuffer[i], crcTmp);
            //Console.WriteLine(crcTmp + " " + pBuffer[i] + " " + length);
        }

        return (crcTmp);
    }

}
}

I'm quite sure that the problem in my port lies between lines 1 and 5. I expect to get an output of 0x94 0x88, but instead the program outputs 0x2D 0xF4.

I would greatly appreciate it if someone could show me where I've gone wrong.

Thanks for any help, Cameron


Solution

  • Alright, for starters lets clean up the C# code a little:

    const int X25_INIT_CRC = 0xffff;
    
    public static ushort crc_accumulate(byte b, ushort crc)
    {
        unchecked
        {
            byte ch = (byte)(b ^ (byte)(crc & 0x00ff));
            ch = (byte)(ch ^ (ch << 4));
    
            return (ushort)((crc >> 8) ^ (ch << 8) ^ (ch << 3) ^ (ch >> 4));
        }
    }
    
    public static ushort crc_calculate(byte[] pBuffer)
    {
        ushort crcTmp = X25_INIT_CRC;
    
        for (int i = 1; i < pBuffer.Length; i++) // skips header U
            crcTmp = crc_accumulate(pBuffer[i], crcTmp);
    
        return crcTmp;
    }
    

    Now the biggest problem here is that there are no unsigned numeric types in Java, so you have to work around that by using the next bigger numeric type instead of ushort and byte and masking off the high bits as needed. You can also just drop the unchecked because Java has no overflow checking anyway. The end result is something like this:

    public static final int X25_INIT_CRC = 0xffff;
    
    public static int crc_accumulate(short b, int crc) {
        short ch = (short)((b ^ crc) & 0xff);
        ch = (short)((ch ^ (ch << 4)) & 0xff);
    
        return ((crc >> 8) ^ (ch << 8) ^ (ch << 3) ^ (ch >> 4)) & 0xffff;
    }
    
    public static int crc_calculate(short[] pBuffer) {
        int crcTmp = X25_INIT_CRC;
    
        for (int i = 1; i < pBuffer.length; i++) // skips header U
            crcTmp = crc_accumulate(pBuffer[i], crcTmp);
    
        return crcTmp;
    }
    

    For the input in your question ({ 255, 9, 19, 1, 1, 0, 0, 0, 0, 0, 2, 3, 81, 4, 3 }) the original C#, cleaned up C# and Java all produce 0xfc7e.