一、CRC编码简介
CRC 的英文全称为 Cyclic Redundancy Check(Code),中文名称为循环冗余校验(码)。它是一类重要的线性分组码,编码和解码方法简单,检错和纠错能力强,在通信领域广泛地用于实现差错控制。
二、CRC编码过程
以下步骤将描述 6 字节红外控制码生成过程:
1.从二维码的中依次顺序提取前 2 个英文字母、最后 2 个英文字母(X、x 除外,取英文字母 ASCII 值为原始数据),并从中提取出多项式 g(x)(多项式的最高位为 x16,最低为 1);
2.预置 1 个 16 位的寄存器为十六进制 FFFF(即全为 1),称此寄存器为 CRC 寄存器;
3.把第一个 8 位二进制数据(既原始数据的第一个字节)与 16 位的 CRC 寄存器的低8 位相异或,把结果放于 CRC 寄存器,高八位数据不变;
4.CRC 寄存器向右移一位,MSB(最高位)补零,并检查右移后的移出位 LSB(最低位)。
5.如果 LSB 为 0,重复第 4 步;若 LSB 为 1,CRC 寄存器与多项式码相异或。
6.重复第 4 与第 5 步直到 8 次移位全部完成。此时一个 8-bit 数据处理完毕。
7.重复第 3 至第 5 步直到将剩下 3 个原始数据全部处理完成。
8.最终 CRC 寄存器的内容即为 CRC 值。
9.取 CRC 的得高八位作为红外控制码的第一字节,按顺序取原始数据为红外控制码的二、三、四、五字节,取 CRC 值的低八位为红外控制码的第六字节。
三、算法示例
从二维码中提取的字符串数据为:<Aa12x16,Fg.5tx15/x2+\1/hgBb>,则提取出的 4 个英文字符为 AaBb,多项式 g(x)=x16+x15+x2+1;
提取原始数据为 0x41、 0x61、 0x42 、0x62,多项式码为 0xA001(由多项式忽略了最高位的"1",得到生成项是 0x8005,其中 0xA001 为 0x8005 按位颠倒之后的结果);计算得到的 CRC 码值为 0x8FF4; 所得 6 字节红外控制码为:0x8f 0x41 0x61 0x42 0x62 0xf4。
四、具体代码如下:
import java.util.ArrayList;
import java.util.List;
public class CRC {
public static void main(String[] args) {
String s = "<Aa12x16,Fg.5tx15/x2+\1/hgBb>";
String order = getOrder(s);
System.out.println(order);
}
public static String getOrder(String s){
// 得到前两位和最后两位英文字母
String result_s = decode_S(s);
String fin = result_s.subSequence(0, 2).toString().concat(result_s.subSequence(result_s.length()-2,result_s.length()).toString());
System.out.println("提取出的4个字母:" + fin);
// 将得到的字母的ASCII码值存储在char数组中
byte []data = new byte[4];
for(int i = 0; i < fin.length(); i++) {
byte temp = (byte) (fin.charAt(i) - 1 + 1);
data[i] = temp;
}
// 通过char数组得到CRC
String CRC = getCRC(data);
String first ="0x" + CRC.substring(0, 2);//第一位十六进制
String last = "0x" + CRC.substring(2,4);//最后一位
System.out.println("CRC = " + CRC);
// 将指令进行拼接
String order = "";
for (int i = 0; i < data.length; i++) {
String tem = "0x" + decimalToHex(data[i]) + ",";
order += tem;
}
order = first + "," + order + last;
return order;
}
public static String decode_S(String data){
String res = "";
for (int i = 0; i < data.length(); i++) {
char temp = data.charAt(i);
if(((temp >= 'a') && (temp <= 'z')) || ((temp >= 'A') && (temp <= 'Z'))){
if((temp != 'x') && (temp != 'X')){
res += temp;
}
}
}
return res;
}
// public static String decode_N(String data){
// String res = "";
// for (int i = (data.indexOf('x') - 1); i < data.length(); i++) {
// char temp = data.charAt(i);
// if((temp == 'x') || (temp == 'X')){
// res += temp;
// for (int j = i + 1; j < i + 3; j++) {
// char next = data.charAt(j);
// if((next >= '1') && (next <= '9')){
// res += next;
// }
// }
// res += "+";
// }
// }
// return res;
// }
public static String getCRC(byte[] bytes) {
int CRC = 0x0000ffff;
int POLYNOMIAL = 0x0000a001;
int i, j;
for (i = 0; i < bytes.length; i++) {
CRC ^= ((int) bytes[i] & 0x000000ff);
for (j = 0; j < 8; j++) {
if ((CRC & 0x00000001) != 0) {
CRC >>= 1;
CRC ^= POLYNOMIAL;
} else {
CRC >>= 1;
}
}
}
return Integer.toHexString(CRC);
}
public static String decimalToHex(int decimal) {
String hex = "";
while(decimal != 0) {
int hexValue = decimal % 16;
hex = toHexChar(hexValue) + hex;
decimal = decimal / 16;
}
return hex;
}
//将0~15的十进制数转换成0~F的十六进制数
public static char toHexChar(int hexValue) {
if(hexValue <= 9 && hexValue >= 0)
return (char)(hexValue + '0');
else
return (char)(hexValue - 10 + 'A');
}
}
//得到多项式 多项式码
//String result_Num = decode_N(s) + "1";
//System.out.println("g(x)=" + result_Num);
五、运行结果
作者留言:距离除夕夜还有八天,目前自己还在留校,过几天也要回家了。提前祝能看到这的陌生人新年快乐,新的一年一起加油!奥利给!