mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-11 08:07:24 -05:00
316 lines
11 KiB
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;
|
|
}
|
|
}
|