【问题标题】:Calculation of CCITT standard CRC with polynomial x^16 + x^12 + x^5 + 1 in Java在 Java 中使用多项式 x^16 + x^12 + x^5 + 1 计算 CCITT 标准 CRC
【发布时间】:2014-09-01 21:37:24
【问题描述】:

在 Java 中使用多项式 x^16 + x^12 + x^5 + 1 (0x1081) 计算 CCITT 标准 CRC 时需要帮助。我在互联网上尝试了很多示例,但每个示例都返回与示例中的值不同的值。

例如对于这个数组 [0xFC] [05] [11],结果需要是 [27] [56]。

使用此代码:

public static void main(String[] args) {
        byte[] array = new byte[3];
        array[0] = (byte) 0xFC;
        array[1] = (byte) 0x05;
        array[2] = (byte) 0x11;
//        array[3] = (byte) 0x00;
//        array[4] = (byte) 0x00;

        System.out.println(Integer.toHexString(crc16(array)));
    }

    private static final int POLYNOMIAL = 0x1081;
    private static final 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;
    } 

我得到结果 FA DE 不是 [27] [56]

使用此代码:

public static void main(String[] args) { 
        int crc = 0x0000;         
        int polynomial = 0x1081;   

        // byte[] testBytes = "123456789".getBytes("ASCII");

//        byte[] array = args[0].getBytes();
        byte[] array = new byte[3];
        array[0] = (byte) 0xFC;
        array[1] = (byte) 0x05;
        array[2] = (byte) 0x11;

        for (byte b : array) {
            for (int i = 0; i < 8; i++) {
                boolean bit = ((b   >> (7-i) & 1) == 1);
                boolean c15 = ((crc >> 15    & 1) == 1);
                crc <<= 1;
                if (c15 ^ bit) crc ^= polynomial;
             }
        }

        crc &= 0xffff;
        System.out.println("CRC16-CCITT = " + Integer.toHexString(crc));
    }

我知道了CRC16-CCITT = 8dca

使用此代码:

private final int polynomial = 0x1081;

    private int[] table = new int[256];

    public int ComputeChecksum(int[] bytes) {
        int crc = 0xffff;
        for (int i = 0; i < bytes.length; ++i) {
            int index = (crc ^ bytes[i]) % 256;
            crc = (crc >> 8) ^ table[index];
        }
        return crc;
    }

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

    public static void main(String[] args) {
        CRC162 c = new CRC162();
        int[] arr = new int[]{0xFC, 0x05, 0x11};
        System.out.println(Integer.toHexString(c.ComputeChecksum(arr)));
    }

我知道了521

希望有人可以帮助我。我需要这个来与使用 ID003 协议的设备进行通信。

编辑: 在http://www.lammertbies.nl/comm/info/crc-calculation.html 使用这个在线计算器输入 FC0511 我从 CRC-CCITT (Kermit) 得到 0x2756。

【问题讨论】:

  • 你确定^ operator 做你想做的事吗?
  • 我不确定,我在stackoverflow和javaranch论坛上找到了这个功能。
  • 我认为有一个定义问题:CCITT-CRC 的表示实际上是 x^16 + x^12 + x^5 + 1,但这给出的是 0x1021 而不是 0x1081(来源 Wikipedia on CRC
  • 看来CRC有很多变种...你确定你想要的是对于这个数组[0xFC] [05] [11]的结果需要是 [27] [56]。因为根据在lammertbies.nl/comm/info/crc-calculation.html 找到的 progs,它不是标准的 CCITT CRC,而是 Kermit 变体。
  • @SergeBallesta,根据reveng.sourceforge.net/crc-catalogue/16.htm,Kermit 变体是“真正的”CCITT-CRC。

标签: java calculator polynomial-math crc16


【解决方案1】:

x^16 + x^12 + x^5 + 1 不是 0x1081。它是 0x1021。 x^5 是 20,而不是 80。(注意 x^16 被丢弃了。)

此外,您需要的 Kermit CRC 已反映,因此多项式反转为 0x8408。

对于这个 CRC,你用零初始化并且不对结果进行补码。

因此,相应地修改您的第一个示例,这将计算出您想要的内容:

public static void main(String[] args) {
    byte[] array = new byte[3];
    array[0] = (byte) 0xFC;
    array[1] = (byte) 0x05;
    array[2] = (byte) 0x11;
    //        array[3] = (byte) 0x00;
    //        array[4] = (byte) 0x00;

    System.out.println(Integer.toHexString(crc16(array)));
}

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

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

    return current_crc_value & 0xFFFF;
}

【讨论】:

  • 预设值 0 暴露了算法的弱点。以任意数量的零开始的数据将具有零 CRC,直到检测到 1 位。许多实现使用 OxFFFF 的预设,从而避免了这个问题..
  • 没关系。如果另一端正在使用这样的 CRC,那么您也必须这样做。
  • 也许如果双方同意在相似的处理器上使用完全相同的代码库,但这不是很实用。在实践中(尤其是在汽车领域),许多模块来自不同的供应商,并期望 CCITT-CRC16 在这些情况下返回合规值,如果您使用的是不合规的实现,那么它确实很重要。这在汽车界是众所周知的。
  • 抱歉,我无法解析。
  • 非常感谢您提供此代码示例。对于输入 FC0511,我能够得到与 lammertbies.nl/comm/info/crc-calculation.html 相同的结果,我从 CRC-CCITT (Kermit) 得到 0x2756。
【解决方案2】:

这是 Kermit CRC 的另一个版本。这是从http://www.lammertbies.nl/comm/info/crc-calculation.html 中的C 代码直接翻译而来的。优化是在类加载时预先计算出任何字节的 CRC 值表,因此剩余的 CRC 计算要简单得多。

public class Crc {

    private static final int POLYNOMIAL = 0x8408;
    private static final int PRESET = 0;
    static private int[] tab;

    static {
        tab = new int[256];
        for (int i = 0; i < 256; i++) {
            tab[i] = initial((byte) i);
        }
    }

    private static int initial(byte c) {
        int crc = 0;
        for (int j = 0; j < 8; j++) {
            if (((crc ^ c) & 1) == 1) {
                crc = ((crc >> 1) ^ POLYNOMIAL);
            } else {
                crc = (crc >> 1);
            }
            c = (byte) (c >> 1);
        }
        return crc;
    }

    private static int update_crc(int crc, byte c) {
        int cc = (0xff & c);

        int tmp = (crc ^ cc);
        crc = (crc >> 8) ^ tab[tmp & 0xff];

        return crc;
    }

    private static int swab(int n) {
        return (((n & 0xFF00) >> 8) + ((n & 0xFF) << 8));
    }

    public static int crc(String str) {
        return crcb(str.getBytes());
    }

    public static int crcb(byte... i) {
        int crc = PRESET;
        for (byte c : i) {
            crc = update_crc(crc, c);
        }
        return swab(crc);
    }

    public static void main(String[] args) {
        int crc = Crc.crcb((byte) 0xFC, (byte) 5, (byte) 0x11);
        System.out.println(Integer.toHexString(crc));
        crc = Crc.crc("123456789");
        System.out.println(Integer.toHexString(crc));
    }
}

输出如预期:

2756
8921

【讨论】:

    【解决方案3】:

    这不是我的,但对我来说很完美,“DatatypeConverter”是我的项目中已经拥有的东西。这对我来说是一个真实的例子,而且很有效。

    *  Reads in a sequence of bytes and prints out its 16 bit
    *  Cylcic Redundancy Check (CRC-CCIIT 0xFFFF).
    *
    *  1 + x + x^5 + x^12 + x^16 is irreducible polynomial.
    
    import javax.xml.bind.DatatypeConverter;
    
    public class CRC16CCITT {
    
        public static void main(String[] args) {
            int crc = 0xFFFF; // initial value
            int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12), in your case: 0x1081
    
            // CRC of this should be 28570
            byte[] testBytes = DatatypeConverter.parseHexBinary("00000000000000000000FA00000002009F1D19B87475445E1122330000000000"); 
    
            for (byte b : testBytes) {
                for (int i = 0; i < 8; i++) {
                    boolean bit = ((b >> (7 - i) & 1) == 1);
                    boolean c15 = ((crc >> 15 & 1) == 1);
                    crc <<= 1;
                    if (c15 ^ bit)
                        crc ^= polynomial;
                }
            }
    
            crc &= 0xffff;
            System.out.println("CRC16-CCITT = " + Integer.toHexString(crc));
            // 6F9A = 28570
        }
    
    }
    

    【讨论】:

      【解决方案4】:
          /**
       * converts the given String to CRC16
       * 
       * @param inputStr
       *            - the input string to get the CRC
       * @param polynomial
       *            - the polynomial (divisor)
       * @param crc
       *            - the CRC mask
       * @param isHex
       *            - if true, treat input string as hex, otherwise, treat as
       *            ASCII
       * @return
       */
      public static String getCRC16CCITT(String inputStr, int polynomial,
              int crc, boolean isHex) {
      
          int strLen = inputStr.length();     
          int[] intArray;
      
          if (isHex) {
              if (strLen % 2 != 0) {
                  inputStr = inputStr.substring(0, strLen - 1) + "0"
                          + inputStr.substring(strLen - 1, strLen);
                  strLen++;
              }
      
              intArray = new int[strLen / 2];
              int ctr = 0;
              for (int n = 0; n < strLen; n += 2) {
                  intArray[ctr] = Integer.valueOf(inputStr.substring(n, n + 2), 16);
                  ctr++;
              }
          } else {
              intArray = new int[inputStr.getBytes().length];
              int ctr=0;
              for(byte b : inputStr.getBytes()){
                  intArray[ctr] = b;
                  ctr++;
              }
          }
      
          // main code for computing the 16-bit CRC-CCITT
          for (int b : intArray) {
              for (int i = 0; i < 8; i++) {
                  boolean bit = ((b >> (7 - i) & 1) == 1);
                  boolean c15 = ((crc >> 15 & 1) == 1);
                  crc <<= 1;
                  if (c15 ^ bit)
                      crc ^= polynomial;
              }
          }
      
          crc &= 0xFFFF;
          return Integer.toHexString(crc).toUpperCase();
      }
      

      适用于 ASCII 和 HEX 计算。

      public static void main(String args[]) {
      
          String testStr = "9142656";
      
          // XModem ASCII
          System.out.println("CRC-CCITT (XModem) Ascii: "
                  + getCRC16CCITT(testStr, 0x1021, 0x0000, false));
      
          // 0xFFFF ASCII
          System.out.println("CRC-CCITT (0xFFFF) Ascii: "
                  + getCRC16CCITT(testStr, 0x1021, 0xFFFF, false));
      
          // 0x1D0F ASCII
          System.out.println("CRC-CCITT (0x1D0F) Ascii: "
                  + getCRC16CCITT(testStr, 0x1021, 0x1D0F, false));
      
          // XModem Hex
          System.out.println("CRC-CCITT (XModem) Hex: "
                  + getCRC16CCITT(testStr, 0x1021, 0x0000, true));
      
          // 0xFFFF Hex
          System.out.println("CRC-CCITT (0xFFFF) Hex: "
                  + getCRC16CCITT(testStr, 0x1021, 0xFFFF, true));
      
          // 0x1D0F Hex
          System.out.println("CRC-CCITT (0x1D0F) Hex: "
                  + getCRC16CCITT(testStr, 0x1021, 0x1D0F, true));
      
      }
      

      输出:

      CRC-CCITT (XModem) Ascii:87F4
      CRC-CCITT (0xFFFF) ASCII:763A
      CRC-CCITT (0x1D0F) ASCII:9F86
      CRC-CCITT (XModem) 十六进制:57FF
      CRC-CCITT (0xFFFF) 十六进制:D33F
      CRC-CCITT (0x1D0F) 十六进制:59EF

      解决方案来自普林斯顿大学http://introcs.cs.princeton.edu/java/61data/CRC16CCITT.java的样本

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-04
        • 2015-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多