Algoritmos CRC en Java y Manejo de Datos Binarios en Puerto Serie

Fundamentos Previos

En Java, el tipo byte tiene un rango de -128 a 127, representado en memoria como valores hexadecimales de 0x00 a 0xFF. Esto corresponde a un byte de 8 bits.

Problema de Manejo de Datos

Al inicializar un array de bytes con valores hexadecimales que exceden el rango del byte, como se muestra a continuación, ocurre un comportamiento inesperado:


byte[] datosHex = new byte[] {0x01, 0x03, 0x04, 0x02, 0x1F, 0x01, 0x4E, 0x4B, (byte)0xE9 };

Java interpreta los literales como enteros y luego los convirete a byte. El valer 0xE9 (233 en decimal) excede el rango del byte, por lo que la conversión forzada causa un desbordamiento. En consecuencia, datosHex[8] se almacena como un valor negativo en memoria.

Solución

Para extraer el valor correcto sin signo, se utiliza la operación AND con 0xFF:


int valorCorrecto = datosHex[8] & 0xFF;

Explicación Técnica

El tipo byte en Java es con signo y utiliza complemanto a dos para la representación de negativos. Los números se interpretan inicialmente como enteros (int). Al convertir un entero que supera 127 a byte, ocurre un desbordamiento hacia valores negativos. Por ejemplo, 233 (0xE9) se convierte en -23 en byte, cuya representación hexadecimal es 0xFFFFFFE9 en un entero de 32 bits. Aplicar & 0xFF enmascara los bits superiores, obteniendo el byte sin signo.

Para byte en el rango [-128, -1], se necesita esta operación; para valores en [0, 127], no es necesario. Además, al operar con bytes, es recomendable usar tipos más amplios como int para evitar conversiones automáticas problemáticas.

Implementaciones de Algoritmos CRC

A continuación, se presentan diversas implementaciones de algoritmos CRC en Java. Se ha modificado la estructura, nombres de variables y lógica para mayor claridad, manteniendo la corrección. Se aplica & 0xFF dentro de las funciones para manejar correctamente los bytes.


public class CalculadoraCRC {

    public static byte crc4_ITU(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) != 0) {
                    checksum = (byte) (((checksum & 0xFF) >> 1) ^ 0x0C);
                } else {
                    checksum = (byte) ((checksum & 0xFF) >> 1);
                }
            }
        }
        return (byte) (checksum & 0x0F);
    }

    public static byte crc5_EPC(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0x48;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; i++) {
                if ((checksum & 0x80) != 0) {
                    checksum = (byte) ((checksum << 1) ^ 0x48);
                } else {
                    checksum <<= 1;
                }
            }
        }
        return (byte) ((checksum >> 3) & 0x1F);
    }

    public static byte crc5_ITU(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) == 0) {
                    checksum = (byte) ((checksum & 0xFF) >> 1);
                } else {
                    checksum = (byte) (((checksum & 0xFF) >> 1) ^ 0x15);
                }
            }
        }
        return (byte) (checksum & 0x1F);
    }

    public static byte crc5_USB(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0x1F;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) == 0) {
                    checksum = (byte) ((checksum & 0xFF) >> 1);
                } else {
                    checksum = (byte) (((checksum & 0xFF) >> 1) ^ 0x14);
                }
            }
        }
        return (byte) ((checksum ^ 0x1F) & 0x1F);
    }

    public static byte crc6_ITU(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) == 0) {
                    checksum = (byte) ((checksum & 0xFF) >> 1);
                } else {
                    checksum = (byte) (((checksum & 0xFF) >> 1) ^ 0x30);
                }
            }
        }
        return (byte) (checksum & 0x3F);
    }

    public static byte crc7_MMC(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; i++) {
                if ((checksum & 0x80) == 0) {
                    checksum <<= 1;
                } else {
                    checksum = (byte) ((checksum << 1) ^ 0x12);
                }
            }
        }
        return (byte) ((checksum >> 1) & 0x7F);
    }

    public static byte crc8(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; i++) {
                if ((checksum & 0x80) == 0) {
                    checksum <<= 1;
                } else {
                    checksum = (byte) ((checksum << 1) ^ 0x07);
                }
            }
        }
        return checksum;
    }

    public static byte crc8_ITU(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; i++) {
                if ((checksum & 0x80) == 0) {
                    checksum <<= 1;
                } else {
                    checksum = (byte) ((checksum << 1) ^ 0x07);
                }
            }
        }
        return (byte) (checksum ^ 0x55);
    }

    public static byte crc8_ROHC(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; i++) {
                if ((checksum & 0x80) == 0) {
                    checksum = (byte) ((checksum & 0xFF) >> 1);
                } else {
                    checksum = (byte) (((checksum & 0xFF) >> 1) ^ 0xE0);
                }
            }
        }
        return checksum;
    }

    public static byte crc8_MAXIM(byte[] datos, int inicio, int longitud) {
        byte i;
        byte checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; i++) {
                if ((checksum & 1) == 0) {
                    checksum = (byte) ((checksum & 0xFF) >> 1);
                } else {
                    checksum = (byte) (((checksum & 0xFF) >> 1) ^ 0x8C);
                }
            }
        }
        return checksum;
    }

    public static short crc16_IBM(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) == 0) {
                    checksum = (short) (checksum >> 1);
                } else {
                    checksum = (short) ((checksum >> 1) ^ 0xA001);
                }
            }
        }
        return checksum;
    }

    public static short crc16_MAXIM(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) == 0) {
                    checksum = (short) (checksum >> 1);
                } else {
                    checksum = (short) ((checksum >> 1) ^ 0xA001);
                }
            }
        }
        return (short) ~checksum;
    }

    public static short crc16_USB(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = (short) 0xFFFF;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) == 0) {
                    checksum = (short) (checksum >> 1);
                } else {
                    checksum = (short) ((checksum >> 1) ^ 0xA001);
                }
            }
        }
        return (short) ~checksum;
    }

    public static short crc16_MODBUS(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = (short) 0xFFFF;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) == 0) {
                    checksum = (short) ((checksum & 0xFFFF) >> 1);
                } else {
                    checksum = (short) (((checksum & 0xFFFF) >> 1) ^ 0xA001);
                }
            }
        }
        return checksum;
    }

    public static short crc16_CCITT(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) != 0) {
                    checksum = (short) ((checksum >> 1) ^ 0x8408);
                } else {
                    checksum = (short) (checksum >> 1);
                }
            }
        }
        return checksum;
    }

    public static short crc16_CCITT_FALSO(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = (short) 0xFFFF;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= (short) ((datos[idx] & 0xFF) << 8);
            for (i = 0; i < 8; ++i) {
                if ((checksum & 0x8000) != 0) {
                    checksum = (short) ((checksum << 1) ^ 0x1021);
                } else {
                    checksum <<= 1;
                }
            }
        }
        return checksum;
    }

    public static short crc16_X25(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = (short) 0xFFFF;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) != 0) {
                    checksum = (short) ((checksum >> 1) ^ 0x8408);
                } else {
                    checksum = (short) (checksum >> 1);
                }
            }
        }
        return (short) ~checksum;
    }

    public static short crc16_XMODEM(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= (short) ((datos[idx] & 0xFF) << 8);
            for (i = 0; i < 8; ++i) {
                if ((checksum & 0x8000) != 0) {
                    checksum = (short) ((checksum << 1) ^ 0x1021);
                } else {
                    checksum <<= 1;
                }
            }
        }
        return checksum;
    }

    public static short crc16_DNP(byte[] datos, int inicio, int longitud) {
        byte i;
        short checksum = 0;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) != 0) {
                    checksum = (short) ((checksum >> 1) ^ 0xA6BC);
                } else {
                    checksum = (short) (checksum >> 1);
                }
            }
        }
        return (short) ~checksum;
    }

    public static int crc32(byte[] datos, int inicio, int longitud) {
        byte i;
        int checksum = 0xFFFFFFFF;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= datos[idx] & 0xFF;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 1) != 0) {
                    checksum = (checksum >> 1) ^ 0xEDB88320;
                } else {
                    checksum >>= 1;
                }
            }
        }
        return ~checksum;
    }

    public static int crc32_MPEG2(byte[] datos, int inicio, int longitud) {
        byte i;
        int checksum = 0xFFFFFFFF;
        int fin = inicio + longitud;
        for (int idx = inicio; idx < fin; idx++) {
            checksum ^= (datos[idx] & 0xFF) << 24;
            for (i = 0; i < 8; ++i) {
                if ((checksum & 0x80000000) != 0) {
                    checksum = (checksum << 1) ^ 0x04C11DB7;
                } else {
                    checksum <<= 1;
                }
            }
        }
        return checksum;
    }
}

Etiquetas: java CRC byte serial communication binary data

Publicado el 6-8 01:06