Files
sdrtrunk/src/main/java/io/github/dsheirer/edac/CRCDMR.java

316 lines
11 KiB
Java

/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DMR CRC check/correction methods
*/
public class CRCDMR
{
private final static Logger mLog = LoggerFactory.getLogger(CRCDMR.class);
/**
* CRC-CCITT 16-bit checksums for a message length of 80 bits plus 16
* additional checksums representing CRC checksum bit errors
*
* Generated by:
* CRCUtil.generate( 80, 16, 0x11021l, 0xFFFFl, true );
*/
public static final int[] CCITT_80_CHECKSUMS = new int[]
{
0xE434, 0x721A, 0x390D, 0x9496, 0x4A4B, 0xAD35, 0xDE8A, 0x6F45, 0xBFB2,
0x5FD9, 0xA7FC, 0x53FE, 0x29FF, 0x9CEF, 0xC667, 0xEB23, 0xFD81, 0xF6D0,
0x7B68, 0x3DB4, 0x1EDA, 0xF6D, 0x8FA6, 0x47D3, 0xABF9, 0xDDEC, 0x6EF6,
0x377B, 0x93AD, 0xC1C6, 0x60E3, 0xB861, 0xD420, 0x6A10, 0x3508, 0x1A84,
0xD42, 0x6A1, 0x8B40, 0x45A0, 0x22D0, 0x1168, 0x8B4, 0x45A, 0x22D, 0x8906,
0x4483, 0xAA51, 0xDD38, 0x6E9C, 0x374E, 0x1BA7, 0x85C3, 0xCAF1, 0xED68,
0x76B4, 0x3B5A, 0x1DAD, 0x86C6, 0x4363, 0xA9A1, 0xDCC0, 0x6E60, 0x3730,
0x1B98, 0xDCC, 0x6E6, 0x373, 0x89A9, 0xCCC4, 0x6662, 0x3331, 0x9188,
0x48C4, 0x2462, 0x1231, 0x8108, 0x4084, 0x2042, 0x1021, 0x1, 0x2, 0x4, 0x8,
0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000,
0x8000,
};
/**
* Confirmed Packet Data Unit CRC-9 checksums, generated by:
*
* long[] table = CRCUtil.generate( 135, 9, 0x259l, 0x1FF, false, Parity.NONE );
*/
public static final int[] CRC9_CHECKSUMS = new int[]
{
0x1E7, 0x1F3, 0x1F9, 0x1FC, 0x0D2, 0x045, 0x122, 0x0BD, 0x15E, 0x083,
0x141, 0x1A0, 0x0FC, 0x052, 0x005, 0x102, 0x0AD, 0x156, 0x087, 0x143,
0x1A1, 0x1D0, 0x0C4, 0x04E, 0x00B, 0x105, 0x182, 0x0ED, 0x176, 0x097,
0x14B, 0x1A5, 0x1D2, 0x0C5, 0x162, 0x09D, 0x14E, 0x08B, 0x145, 0x1A2,
0x0FD, 0x17E, 0x093, 0x149, 0x1A4, 0x0FE, 0x053, 0x129, 0x194, 0x0E6,
0x05F, 0x12F, 0x197, 0x1CB, 0x1E5, 0x1F2, 0x0D5, 0x16A, 0x099, 0x14C,
0x08A, 0x069, 0x134, 0x0B6, 0x077, 0x13B, 0x19D, 0x1CE, 0x0CB, 0x165,
0x1B2, 0x0F5, 0x17A, 0x091, 0x148, 0x088, 0x068, 0x018, 0x020, 0x03C,
0x032, 0x035, 0x11A, 0x0A1, 0x150, 0x084, 0x06E, 0x01B, 0x10D, 0x186,
0x0EF, 0x177, 0x1BB, 0x1DD, 0x1EE, 0x0DB, 0x16D, 0x1B6, 0x0F7, 0x17B,
0x1BD, 0x1DE, 0x0C3, 0x161, 0x1B0, 0x0F4, 0x056, 0x007, 0x103, 0x181,
0x1C0, 0x0CC, 0x04A, 0x009, 0x104, 0x0AE, 0x07B, 0x13D, 0x19E, 0x0E3,
0x171, 0x1B8, 0x0F0, 0x054, 0x006, 0x02F, 0x117, 0x18B, 0x1C5, 0x1E2,
0x0DD, 0x16E, 0x09B, 0x14D, 0x1A6
};
/**
* Error detection and correction of single-bit errors for CCITT 16-bit CRC protected 80-bit messages.
*/
public static BinaryMessage correctCCITT80(BinaryMessage message, int messageStart, int crcStart)
{
int calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(messageStart);
i >= messageStart && i < crcStart;
i = message.nextSetBit(i + 1))
{
calculated ^= CCITT_80_CHECKSUMS[i - messageStart];
}
int checksum = getIntChecksum(message, crcStart, 16);
int residual = calculated ^ checksum;
if(residual == 0 || residual == 0xFFFF)
{
message.setCRC(CRC.PASSED);
return message;
}
else
{
int errorLocation = getBitError(residual, CCITT_80_CHECKSUMS);
if(errorLocation >= 0)
{
message.flip(errorLocation + messageStart);
message.setCRC(CRC.CORRECTED);
return message;
}
}
message.setCRC(CRC.FAILED_CRC);
return message;
}
/**
* Calculates the CRC checksum for the specified message
* @param message to check
* @param start of the message
* @param length of the message (assumes the CRC checksum follows)
* @param checksums to use in calculating the message checksum
* @return calculated checksum
*/
public static int calculate(BinaryMessage message, int start, int length, long[] checksums)
{
int calculated = 0;
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(start); i >= start && i < length; i = message.nextSetBit(i + 1))
{
calculated ^= checksums[i - start];
}
return calculated;
}
/**
* Error detection and correction of single-bit errors for CCITT 16-bit CRC protected 80-bit messages.
*/
public static int correctCCITT80(CorrectedBinaryMessage message, int messageStart, int crcStart, int mask)
{
int calculated = mask; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(messageStart); i >= messageStart && i < crcStart; i = message.nextSetBit(i + 1))
{
calculated ^= CCITT_80_CHECKSUMS[i - messageStart];
}
int checksum = getIntChecksum(message, crcStart, 16);
int residual = calculated ^ checksum;
if(residual == 0 || residual == 0xFFFF)
{
return 0;
}
else
{
int errorLocation = getBitError(residual, CCITT_80_CHECKSUMS);
if(errorLocation >= 0)
{
message.flip(errorLocation + messageStart);
message.incrementCorrectedBitCount(1);
return 1;
}
}
//Message has at least 2 bit errors - ie uncorrectable
message.incrementCorrectedBitCount(2);
return 2;
}
/**
* Calculates the residual CRC value using a mask of zero which should identify the mask value when an alternate
* mask value is employed.
* @param message to be checked
* @param messageStart location
* @param crcStart location
* @return residual CRC check value.
*/
public static int calculateResidual(CorrectedBinaryMessage message, int messageStart, int crcStart)
{
int calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(messageStart); i >= messageStart && i < crcStart; i = message.nextSetBit(i + 1))
{
calculated ^= CCITT_80_CHECKSUMS[i - messageStart];
}
int checksum = getIntChecksum(message, crcStart, 16);
return calculated ^ checksum;
}
/**
* Error detection for CRC-9 protected Confirmed Packet Data blocks. These
* data blocks have a slightly complicated structure because the checksum
* is located between bits 7-15, within a 144 bit block. The checksums
* were generated assuming that the message is contiguous from 0 - 134 bits.
* No data correction is performed.
*/
public static CRC checkCRC9(BinaryMessage message, int messageStart)
{
int calculated = 0x0; //Initial fill of all ones
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(messageStart);
i >= messageStart && i < messageStart + 144;
i = message.nextSetBit(i + 1))
{
/* message bits before the CRC */
if(i < (messageStart + 7))
{
calculated ^= CRC9_CHECKSUMS[i - messageStart];
}
/* message bits after the CRC */
else if(i > (messageStart + 15))
{
calculated ^= CRC9_CHECKSUMS[i - messageStart - 9];
}
}
int checksum = message.getInt(messageStart + 7, messageStart + 15);
int residual = calculated ^ checksum;
if(residual == 0 || residual == 0x1FF)
{
return CRC.PASSED;
}
return CRC.FAILED_CRC;
}
/**
* Calculates the value of the message checksum as a long
*/
public static long getLongChecksum(BinaryMessage message,
int crcStart, int crcLength)
{
return message.getLong(crcStart, crcStart + crcLength - 1);
}
/**
* Calculates the value of the message checksum as an integer
*/
public static int getIntChecksum(BinaryMessage message,
int crcStart, int crcLength)
{
return message.getInt(crcStart, crcStart + crcLength - 1);
}
/**
* Identifies any single bit error position that matches the checksum error.
*/
public static int getBitError(long checksumError, long[] checksums)
{
for(int x = 0; x < checksums.length; x++)
{
if(checksums[x] == checksumError)
{
return x;
}
}
return -1;
}
/**
* Identifies any single bit error position that matches the checksum error.
*/
public static int getBitError(int checksumError, int[] checksums)
{
for(int x = 0; x < checksums.length; x++)
{
if(checksums[x] == checksumError)
{
return x;
}
}
return -1;
}
public static int crc8(BinaryMessage bits, int len) {
int crc=0;
final int K = 8;
final boolean[] poly = new boolean[]{true,false,false,false,false,false,true,true,true}; // crc8 poly
boolean[] buf = new boolean[256];
if (len+K > 256) {
return 0;
}
for (int i=0; i<len; i++){
buf[i] = bits.get(i);
}
for (int i=0; i<len; i++)
if (buf[i])
for (int j=0; j<K+1; j++)
buf[i+j] ^= poly[j];
for (int i=0; i<K; i++){
crc = (crc << 1) + (buf[len + i] ? 1:0);
}
return crc;
}
}