mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
P25 work in progress - adds CRC-CCITT error checking to TSBK messages and shows CRC correction state in the messages tab
This commit is contained in:
@@ -199,7 +199,8 @@ public class BitSetBuffer extends BitSet
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BitSetFullException( "bitset is full -- contains " + ( mPointer + 1 ) + " bits" );
|
||||
throw new BitSetFullException( "bitset is full -- contains " +
|
||||
( mPointer + 1 ) + " bits" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,24 +269,25 @@ public class BitSetBuffer extends BitSet
|
||||
*/
|
||||
public int getInt( int[] bits )
|
||||
{
|
||||
if( bits.length > 31 )
|
||||
if( bits.length > 32 )
|
||||
{
|
||||
throw new IllegalArgumentException( "BitSetBuffer getInt() "
|
||||
+ "array length must be less than 32 or the value exceeds "
|
||||
+ "integer size" );
|
||||
throw new IllegalArgumentException( "Overflow - must be 32 bits "
|
||||
+ "or less to fit into a primitive integer value" );
|
||||
}
|
||||
|
||||
int retVal = 0;
|
||||
|
||||
for( int x = 0; x < bits.length; x++ )
|
||||
|
||||
int value = 0;
|
||||
|
||||
for( int index: bits )
|
||||
{
|
||||
if( get( bits[ x ] ) )
|
||||
value = Integer.rotateLeft( value, 1 );
|
||||
|
||||
if( get( index ) )
|
||||
{
|
||||
retVal += 1<<( bits.length - 1 - x );
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,25 +299,28 @@ public class BitSetBuffer extends BitSet
|
||||
*/
|
||||
public long getLong( int[] bits )
|
||||
{
|
||||
if( bits.length > 63 )
|
||||
if( bits.length > 64 )
|
||||
{
|
||||
throw new IllegalArgumentException( "BitSetBuffer getLong() "
|
||||
+ "array length must be less than 64 or the value exceeds "
|
||||
+ "a primitive long size" );
|
||||
throw new IllegalArgumentException( "Overflow - must be 64 bits "
|
||||
+ "or less to fit into a primitive long value" );
|
||||
}
|
||||
|
||||
long retVal = 0;
|
||||
|
||||
for( int x = 0; x < bits.length; x++ )
|
||||
long value = 0;
|
||||
|
||||
for( int index: bits )
|
||||
{
|
||||
if( get( bits[ x ] ) )
|
||||
value = Long.rotateLeft( value, 1 );
|
||||
|
||||
if( get( index ) )
|
||||
{
|
||||
retVal += 1<<( bits.length - 1 - x );
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Converts up to 63 bits from the bit array into an integer and then
|
||||
@@ -328,13 +333,13 @@ public class BitSetBuffer extends BitSet
|
||||
*/
|
||||
public String getHex( int[] bits, int digitDisplayCount )
|
||||
{
|
||||
if( bits.length <= 31 )
|
||||
if( bits.length <= 32 )
|
||||
{
|
||||
int value = getInt( bits );
|
||||
|
||||
return String.format( "%0" + digitDisplayCount + "X", value );
|
||||
}
|
||||
else if( bits.length <= 63 )
|
||||
else if( bits.length <= 64 )
|
||||
{
|
||||
long value = getLong( bits );
|
||||
|
||||
@@ -351,13 +356,13 @@ public class BitSetBuffer extends BitSet
|
||||
{
|
||||
int length = lsb - msb;
|
||||
|
||||
if( length <= 31 )
|
||||
if( length <= 32 )
|
||||
{
|
||||
int value = getInt( msb, lsb );
|
||||
|
||||
return String.format( "%0" + digitDisplayCount + "X", value );
|
||||
}
|
||||
else if( length <= 63 )
|
||||
else if( length <= 64 )
|
||||
{
|
||||
long value = getLong( msb, lsb );
|
||||
|
||||
@@ -366,7 +371,7 @@ public class BitSetBuffer extends BitSet
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException( "BitSetBuffer.getHex() "
|
||||
+ "maximum array length is 63 bits" );
|
||||
+ "maximum array length is 64 bits" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,22 +385,23 @@ public class BitSetBuffer extends BitSet
|
||||
{
|
||||
if( end - start > 32 )
|
||||
{
|
||||
throw new IllegalArgumentException( "BitSetBuffer - requested bit "
|
||||
+ "length [" + ( end - start ) + "] is larger than an "
|
||||
+ "integer (32 bits)" );
|
||||
throw new IllegalArgumentException( "Overflow - must be 32 bits "
|
||||
+ "or less to fit into a primitive integer value" );
|
||||
}
|
||||
|
||||
int retVal = 0;
|
||||
int value = 0;
|
||||
|
||||
for( int x = start; x <= end; x++ )
|
||||
{
|
||||
value = Integer.rotateLeft( value, 1 );
|
||||
|
||||
if( get( x ) )
|
||||
{
|
||||
retVal += 1<<( end - x );
|
||||
value++;;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,16 +412,116 @@ public class BitSetBuffer extends BitSet
|
||||
*/
|
||||
public long getLong( int start, int end )
|
||||
{
|
||||
long retVal = 0;
|
||||
if( end - start > 64 )
|
||||
{
|
||||
throw new IllegalArgumentException( "Overflow - must be 64 bits "
|
||||
+ "or less to fit into a primitive long value" );
|
||||
}
|
||||
|
||||
long value = 0;
|
||||
|
||||
for( int x = start; x <= end; x++ )
|
||||
{
|
||||
value = Long.rotateLeft( value, 1 );
|
||||
|
||||
if( get( x ) )
|
||||
{
|
||||
retVal += 1<<( end - x );
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a buffer of size=width and fills the buffer with the fill value
|
||||
* @param width - size of the buffer
|
||||
* @param fill - initial fill value
|
||||
* @return - filled buffer
|
||||
*/
|
||||
public static BitSetBuffer getBuffer( int width, long fill )
|
||||
{
|
||||
BitSetBuffer buffer = new BitSetBuffer( width );
|
||||
|
||||
buffer.load( 0, width, fill );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the value into the buffer starting at the offset index, and
|
||||
* assuming that the value represents (width) number of bits. The MSB of
|
||||
* the value will be located at the offset and the LSB of the value will
|
||||
* be located at ( offset + width ).
|
||||
*
|
||||
* @param offset - starting bit index for the MSB of the value
|
||||
* @param width - representative bit width of the value
|
||||
* @param value - value to be loaded into the buffer
|
||||
*/
|
||||
public void load( int offset, int width, long value )
|
||||
{
|
||||
for( int x = 0; x < width; x++ )
|
||||
{
|
||||
long mask = Long.rotateLeft( 1, width - x - 1 );
|
||||
|
||||
if( ( mask & value ) == mask )
|
||||
{
|
||||
set( offset + x );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of message bit position indexes to support accessing
|
||||
* a contiguous field value
|
||||
*
|
||||
* @param start - starting bit position of the field
|
||||
* @param length - field length
|
||||
* @return - array of field indexes
|
||||
*/
|
||||
public static int[] getFieldIndexes( int start, int length, boolean bigEndian )
|
||||
{
|
||||
int[] checksumIndexes = new int[ length ];
|
||||
|
||||
for( int x = 0; x < length; x++ )
|
||||
{
|
||||
if( bigEndian )
|
||||
{
|
||||
checksumIndexes[ length - x - 1 ] = start + x;
|
||||
}
|
||||
else
|
||||
{
|
||||
checksumIndexes[ x ] = start + x;
|
||||
}
|
||||
}
|
||||
|
||||
return checksumIndexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bitsetbuffer loaded from a string of zeros and ones
|
||||
*
|
||||
* @param message - string containing only zeros and ones
|
||||
* @return - loaded buffer
|
||||
*/
|
||||
public static BitSetBuffer load( String message )
|
||||
{
|
||||
if( !message.matches( "[01]*" ) )
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"Message must contain only zeros and ones" );
|
||||
}
|
||||
|
||||
BitSetBuffer buffer = new BitSetBuffer( message.length() );
|
||||
|
||||
for( int x = 0; x < message.length(); x++ )
|
||||
{
|
||||
if( message.substring( x, x + 1 ).contentEquals( "1" ) )
|
||||
{
|
||||
buffer.set( x );
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,11 @@ public enum CRC
|
||||
|
||||
public static String format( CRC[] checks )
|
||||
{
|
||||
if( checks == null )
|
||||
{
|
||||
return CRC.UNKNOWN.getAbbreviation();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for( int x = 0; x < checks.length; x++ )
|
||||
|
||||
225
src/crc/CRCP25.java
Normal file
225
src/crc/CRCP25.java
Normal file
@@ -0,0 +1,225 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 crc;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
/**
|
||||
* P25 CRC check/correction methods
|
||||
*/
|
||||
public class CRCP25
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( CRCP25.class );
|
||||
|
||||
private static final int NID_MESSAGE_START = 0;
|
||||
private static final int NID_CRC_START = 16;
|
||||
private static final int NID_CRC_LENGTH = 48;
|
||||
|
||||
/**
|
||||
* Network ID and Data Unit ID checksums
|
||||
*/
|
||||
private static long[] NID_DUID_CHECKSUMS = new long[]
|
||||
{
|
||||
0xCD930BDD3B2Al, 0xAB5A8E33A6BEl, 0x983E4CC4E874l, 0x4C1F2662743Al,
|
||||
0xEB9C98EC0136l, 0xB85D47AB3BB0l, 0x5C2EA3D59DD8l, 0x2E1751EACEECl,
|
||||
0x170BA8F56776l, 0xC616DFA78890l, 0x630B6FD3C448l, 0x3185B7E9E224l,
|
||||
0x18C2DBF4F112l, 0xC1F2662743A2l, 0xAD6A38CE9AFBl, 0x9B2617BA7657l,
|
||||
0x800000000000l, 0x400000000000l, 0x200000000000l, 0x100000000000l,
|
||||
0x080000000000l, 0x040000000000l, 0x020000000000l, 0x010000000000l,
|
||||
0x008000000000l, 0x004000000000l, 0x002000000000l, 0x001000000000l,
|
||||
0x000800000000l, 0x000400000000l, 0x000200000000l, 0x000100000000l,
|
||||
0x000080000000l, 0x000040000000l, 0x000020000000l, 0x000010000000l,
|
||||
0x000008000000l, 0x000004000000l, 0x000002000000l, 0x000001000000l,
|
||||
0x000000800000l, 0x000000400000l, 0x000000200000l, 0x000000100000l,
|
||||
0x000000080000l, 0x000000040000l, 0x000000020000l, 0x000000010000l,
|
||||
0x000000008000l, 0x000000004000l, 0x000000002000l, 0x000000001000l,
|
||||
0x000000000800l, 0x000000000400l, 0x000000000200l, 0x000000000100l,
|
||||
0x000000000080l, 0x000000000040l, 0x000000000020l, 0x000000000010l,
|
||||
0x000000000008l, 0x000000000004l, 0x000000000002l, 0x000000000001l
|
||||
};
|
||||
|
||||
/**
|
||||
* CRC-CCITT 16-bit checksums for a message length of 80 bits plus 16
|
||||
* additional checksums representing CRC checksum bit errors
|
||||
*/
|
||||
public static final int[] CCITT_80_CHECKSUMS = new int[]
|
||||
{
|
||||
0x1BCB, 0x8DE5, 0xC6F2, 0x6B69, 0xB5B4, 0x52CA, 0x2175, 0x90BA, 0x404D,
|
||||
0xA026, 0x5803, 0xAC01, 0xD600, 0x6310, 0x3998, 0x14DC, 0x27E, 0x92F,
|
||||
0x8497, 0xC24B, 0xE125, 0xF092, 0x7059, 0xB82C, 0x5406, 0x2213, 0x9109,
|
||||
0xC884, 0x6C52, 0x3E39, 0x9F1C, 0x479E, 0x2BDF, 0x95EF, 0xCAF7, 0xE57B,
|
||||
0xF2BD, 0xF95E, 0x74BF, 0xBA5F, 0xDD2F, 0xEE97, 0xF74B, 0xFBA5, 0xFDD2,
|
||||
0x76F9, 0xBB7C, 0x55AE, 0x22C7, 0x9163, 0xC8B1, 0xE458, 0x7A3C, 0x350E,
|
||||
0x1297, 0x894B, 0xC4A5, 0xE252, 0x7939, 0xBC9C, 0x565E, 0x233F, 0x919F,
|
||||
0xC8CF, 0xE467, 0xF233, 0xF919, 0xFC8C, 0x7656, 0x333B, 0x999D, 0xCCCE,
|
||||
0x6E77, 0xB73B, 0xDB9D, 0xEDCE, 0x7EF7, 0xBF7B, 0xDFBD, 0xEFDE, 0x0001,
|
||||
0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200,
|
||||
0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs CRC check and corrects single-bit errors against the message.
|
||||
* @param message - bitsetbuffer containing a message of at least 64 bits.
|
||||
* @return - results of CRC check and/or correction attempt
|
||||
*/
|
||||
public static CRC correctNID( BitSetBuffer message )
|
||||
{
|
||||
long calculated = 0; //Starting value
|
||||
|
||||
/* Iterate the set bits and XOR running checksum with lookup value */
|
||||
for (int i = message.nextSetBit( NID_MESSAGE_START );
|
||||
i >= NID_MESSAGE_START && i < NID_CRC_START;
|
||||
i = message.nextSetBit( i+1 ) )
|
||||
{
|
||||
calculated ^= NID_DUID_CHECKSUMS[ i - NID_MESSAGE_START ];
|
||||
}
|
||||
|
||||
long checksum = getLongChecksum( message, NID_CRC_START, NID_CRC_LENGTH );
|
||||
|
||||
if( calculated == checksum )
|
||||
{
|
||||
return CRC.PASSED;
|
||||
}
|
||||
else
|
||||
{
|
||||
int errorLocation = getBitError( calculated ^ checksum,
|
||||
NID_DUID_CHECKSUMS );
|
||||
|
||||
if( errorLocation >= 0 )
|
||||
{
|
||||
message.flip( errorLocation );
|
||||
|
||||
return CRC.CORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return CRC.FAILED_CRC;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public static CRC correctCCITT80( BitSetBuffer 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 )
|
||||
{
|
||||
return CRC.PASSED;
|
||||
}
|
||||
else
|
||||
{
|
||||
int errorLocation = getBitError( residual, CCITT_80_CHECKSUMS );
|
||||
|
||||
if( errorLocation >= 0 )
|
||||
{
|
||||
message.flip( errorLocation );
|
||||
|
||||
return CRC.CORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return CRC.FAILED_CRC;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the value of the message checksum as a long
|
||||
*/
|
||||
public static long getLongChecksum( BitSetBuffer message,
|
||||
int crcStart, int crcLength )
|
||||
{
|
||||
int[] checksumIndexes = BitSetBuffer
|
||||
.getFieldIndexes( crcStart, crcLength, false );
|
||||
|
||||
return message.getLong( checksumIndexes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the value of the message checksum as an integer
|
||||
*/
|
||||
public static int getIntChecksum( BitSetBuffer message,
|
||||
int crcStart, int crcLength )
|
||||
{
|
||||
int[] checksumIndexes = BitSetBuffer
|
||||
.getFieldIndexes( crcStart, crcLength, false );
|
||||
|
||||
return message.getInt( checksumIndexes );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 void main( String[] args )
|
||||
{
|
||||
String raw = "0010011000000111100010111111001111100001111010011110000100010010000001011001000000000000000000000000000000000000000000000000000000000000000000001101000111000011";
|
||||
|
||||
BitSetBuffer message = BitSetBuffer.load( raw );
|
||||
|
||||
mLog.debug( "MSG:" + message.toString() );
|
||||
|
||||
CRC results = correctCCITT80( message, 64, 144 );
|
||||
|
||||
mLog.debug( "Results: " + results.getDisplayText() );
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 crc;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
/**
|
||||
* P25 Data Unit ID CRC check utility. Corrects any single bit errors in
|
||||
* either the message or the crc checksum.
|
||||
*/
|
||||
public class CRCP25NetworkID
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( CRCP25NetworkID.class );
|
||||
|
||||
private static final int MESSAGE_START = 0;
|
||||
private static final int CRC_START = 16;
|
||||
private static final int CRC_END = 64;
|
||||
|
||||
private static long[] CHECKSUMS = new long[]
|
||||
{
|
||||
0xCD930BDD3B2Al, //NAC Bit 11 (MSB)
|
||||
0xAB5A8E33A6BEl, //NAC Bit 10
|
||||
0x983E4CC4E874l, //NAC Bit 9
|
||||
0x4C1F2662743Al, //NAC Bit 8
|
||||
0xEB9C98EC0136l, //NAC Bit 7
|
||||
0xB85D47AB3BB0l, //NAC Bit 6
|
||||
0x5C2EA3D59DD8l, //NAC Bit 5
|
||||
0x2E1751EACEECl, //NAC Bit 4
|
||||
0x170BA8F56776l, //NAC Bit 3
|
||||
0xC616DFA78890l, //NAC Bit 2
|
||||
0x630B6FD3C448l, //NAC Bit 1
|
||||
0x3185B7E9E224l, //NAC Bit 0 (LSB)
|
||||
0x18C2DBF4F112l, //DUID Bit 3 (MSB)
|
||||
0xC1F2662743A2l, //DUID Bit 2
|
||||
0xAD6A38CE9AFBl, //DUID Bit 1
|
||||
0x9B2617BA7657l, //DUID Bit 0 (LSB)
|
||||
|
||||
//Single bit errors in the CRC Checksum
|
||||
0x800000000000l,
|
||||
0x400000000000l,
|
||||
0x200000000000l,
|
||||
0x100000000000l,
|
||||
0x080000000000l,
|
||||
0x040000000000l,
|
||||
0x020000000000l,
|
||||
0x010000000000l,
|
||||
0x008000000000l,
|
||||
0x004000000000l,
|
||||
0x002000000000l,
|
||||
0x001000000000l,
|
||||
0x000800000000l,
|
||||
0x000400000000l,
|
||||
0x000200000000l,
|
||||
0x000100000000l,
|
||||
0x000080000000l,
|
||||
0x000040000000l,
|
||||
0x000020000000l,
|
||||
0x000010000000l,
|
||||
0x000008000000l,
|
||||
0x000004000000l,
|
||||
0x000002000000l,
|
||||
0x000001000000l,
|
||||
0x000000800000l,
|
||||
0x000000400000l,
|
||||
0x000000200000l,
|
||||
0x000000100000l,
|
||||
0x000000080000l,
|
||||
0x000000040000l,
|
||||
0x000000020000l,
|
||||
0x000000010000l,
|
||||
0x000000008000l,
|
||||
0x000000004000l,
|
||||
0x000000002000l,
|
||||
0x000000001000l,
|
||||
0x000000000800l,
|
||||
0x000000000400l,
|
||||
0x000000000200l,
|
||||
0x000000000100l,
|
||||
0x000000000080l,
|
||||
0x000000000040l,
|
||||
0x000000000020l,
|
||||
0x000000000010l,
|
||||
0x000000000008l,
|
||||
0x000000000004l,
|
||||
0x000000000002l,
|
||||
0x000000000001l,
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static CRC checkAndCorrect( BitSetBuffer message )
|
||||
{
|
||||
long calculated = 0; //Starting value
|
||||
|
||||
/* Iterate the set bits and XOR running checksum with lookup value */
|
||||
for (int i = message.nextSetBit( MESSAGE_START );
|
||||
i >= MESSAGE_START && i < CRC_START;
|
||||
i = message.nextSetBit( i+1 ) )
|
||||
{
|
||||
calculated ^= CHECKSUMS[ i - MESSAGE_START ];
|
||||
}
|
||||
|
||||
long checksum = getChecksum( message );
|
||||
|
||||
if( calculated == checksum )
|
||||
{
|
||||
return CRC.PASSED;
|
||||
}
|
||||
else
|
||||
{
|
||||
int errorLocation = findBitError( calculated ^ checksum );
|
||||
|
||||
if( errorLocation >= 0 )
|
||||
{
|
||||
message.flip( errorLocation );
|
||||
|
||||
return CRC.CORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return CRC.FAILED_CRC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long value of the 48 bit crc checksum and parity bit
|
||||
*/
|
||||
public static long getChecksum( BitSet msg )
|
||||
{
|
||||
long checksum = 0;
|
||||
|
||||
for( int x = CRC_START; x < CRC_END; x++ )
|
||||
{
|
||||
if( msg.get( x ) )
|
||||
{
|
||||
checksum += Long.rotateLeft( 1, CRC_END - 1 - x );
|
||||
}
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies any single bit error position that matches the checksum error.
|
||||
*/
|
||||
public static int findBitError( long checksumError )
|
||||
{
|
||||
/* One bit errors */
|
||||
for( int x = 0; x < 64; x++ )
|
||||
{
|
||||
if( CHECKSUMS[ x ] == checksumError )
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -17,42 +17,182 @@
|
||||
******************************************************************************/
|
||||
package crc;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class CRCUtil
|
||||
{
|
||||
public static BitSet check( BitSet originalMessage,
|
||||
int messageLength,
|
||||
BitSet polynomial,
|
||||
int polynomialLength )
|
||||
{
|
||||
//Make a copy of the message, since we're going to modify it
|
||||
BitSet copy = originalMessage.get( 0, messageLength );
|
||||
private final static Logger mLog = LoggerFactory.getLogger( CRCUtil.class );
|
||||
|
||||
//Iterate through the message bits
|
||||
for( int x = 0; x < messageLength; x++ )
|
||||
{
|
||||
//If we're aligned against a starting 1 in the message, divide it
|
||||
if( copy.get( 0 ) )
|
||||
{
|
||||
copy.xor( polynomial );
|
||||
}
|
||||
|
||||
//Left shift the message
|
||||
copy = copy.get( 1, messageLength + polynomialLength - x );
|
||||
}
|
||||
|
||||
return copy.get( 0, polynomialLength );
|
||||
}
|
||||
|
||||
public static void log( String message )
|
||||
{
|
||||
System.out.println( message );
|
||||
}
|
||||
|
||||
public enum Status
|
||||
{
|
||||
PASS, FAIL, BITINVERTED, WORDINVERTED;
|
||||
}
|
||||
public static long[] generate( int messageSize,
|
||||
int crcSize,
|
||||
long polynomial,
|
||||
long initialFill,
|
||||
boolean includeCRCBitErrors )
|
||||
{
|
||||
long[] crcTable = new long[ messageSize +
|
||||
( includeCRCBitErrors ? crcSize : 0 ) ];
|
||||
|
||||
int[] checksumIndexes = new int[ crcSize ];
|
||||
|
||||
for( int x = 0; x < crcSize; x++ )
|
||||
{
|
||||
checksumIndexes[ x ] = messageSize + x;
|
||||
}
|
||||
|
||||
for( int x = 0; x < messageSize; x++ )
|
||||
{
|
||||
BitSetBuffer message = new BitSetBuffer( messageSize + crcSize );
|
||||
|
||||
message.load( messageSize, crcSize, initialFill );
|
||||
|
||||
message.set( x );
|
||||
|
||||
message = decode( message, polynomial, crcSize, messageSize );
|
||||
|
||||
long checksum = message.getLong( checksumIndexes );
|
||||
|
||||
mLog.debug( "Index " + x + " Checksum:" + Long.toHexString( checksum ) );
|
||||
|
||||
crcTable[ x ] = checksum;
|
||||
}
|
||||
|
||||
if( includeCRCBitErrors )
|
||||
{
|
||||
for( int x = 0; x < crcSize; x++ )
|
||||
{
|
||||
crcTable[ messageSize + x ] = Long.rotateLeft( 1, x );
|
||||
}
|
||||
}
|
||||
|
||||
return crcTable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs binary division of the polynomial into the message for bits
|
||||
* in index 0 to index messageLength - 1, leaving the results in the
|
||||
* checksum filed following the message.
|
||||
*
|
||||
* IN: MMMMMMMMMMMMMMMMCCCCCCCCCCC
|
||||
* OUT: 0000000000000000RRRRRRRRRRR
|
||||
*
|
||||
* M: Message
|
||||
* C: Checksum
|
||||
* R: Remainder
|
||||
*
|
||||
* @param message - message and crc
|
||||
* @param polynomial - crc polynomial - must be 1 bit longer than the crc width
|
||||
* @param messageSize - message length and start of the checksum
|
||||
*
|
||||
* @return message with all message bits zeroed out, and the remainder
|
||||
* placed in the crc field which starts at index messageLength
|
||||
*/
|
||||
public static BitSetBuffer decode( BitSetBuffer message,
|
||||
long polynomial,
|
||||
int crcSize,
|
||||
int messageSize )
|
||||
{
|
||||
int MESSAGE_START = 0;
|
||||
|
||||
for (int i = message.nextSetBit( MESSAGE_START );
|
||||
i >= MESSAGE_START && i < messageSize;
|
||||
i = message.nextSetBit( i+1 ) )
|
||||
{
|
||||
BitSetBuffer polySet = new BitSetBuffer( crcSize + i + 1 );
|
||||
|
||||
polySet.load( i, crcSize + 1, polynomial );
|
||||
|
||||
message.xor( polySet );
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static void main( String[] args )
|
||||
{
|
||||
long crcCCITT = 0x11021l;
|
||||
long initialFill = 0xFFFFl;
|
||||
|
||||
mLog.debug( "Starting" );
|
||||
|
||||
long[] table = generate( 80, 16, crcCCITT, initialFill, true );
|
||||
|
||||
mLog.debug( toCodeArray( table ) );
|
||||
mLog.debug( "Finished" );
|
||||
}
|
||||
|
||||
public static String toCodeArray( long[] values )
|
||||
{
|
||||
boolean integerArray = true;
|
||||
|
||||
/* Determine the correct primitive type: int or long */
|
||||
for( long value: values )
|
||||
{
|
||||
if( value > Integer.MAX_VALUE || value < Integer.MIN_VALUE )
|
||||
{
|
||||
integerArray = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if( integerArray )
|
||||
{
|
||||
sb.append( "\npublic static final int[] CHECKSUMS = new int[]\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "\npublic static final long[] CHECKSUMS = new long[]\n" );
|
||||
}
|
||||
sb.append( "{\n" );
|
||||
|
||||
StringBuilder row = new StringBuilder();
|
||||
row.append( " " );
|
||||
|
||||
for( long value: values )
|
||||
{
|
||||
StringBuilder element = new StringBuilder();
|
||||
|
||||
element.append( "0x" );
|
||||
element.append( Long.toHexString( value ).toUpperCase() );
|
||||
|
||||
if( !integerArray )
|
||||
{
|
||||
element.append( "l" );
|
||||
}
|
||||
|
||||
element.append( ", " );
|
||||
|
||||
if( row.length() + element.length() <= 80 )
|
||||
{
|
||||
row.append( element.toString() );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( row.toString() );
|
||||
sb.append( "\n" );
|
||||
row = new StringBuilder();
|
||||
row.append( " " );
|
||||
|
||||
row.append( element.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
if( row.length() > 4 )
|
||||
{
|
||||
sb.append( row.toString() );
|
||||
sb.append( "\n" );
|
||||
}
|
||||
|
||||
sb.append( "};\n" );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -131,7 +131,6 @@ public class P25ChannelState extends ChannelState
|
||||
if( alias != null )
|
||||
{
|
||||
mSiteAlias = alias.getName();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ import message.Message;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import crc.CRC;
|
||||
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
|
||||
import decode.p25.reference.DataUnitID;
|
||||
|
||||
@@ -28,6 +29,8 @@ public class P25Message extends Message
|
||||
protected DataUnitID mDUID;
|
||||
protected AliasList mAliasList;
|
||||
|
||||
protected CRC[] mCRC;
|
||||
|
||||
public P25Message( BitSetBuffer message,
|
||||
DataUnitID duid,
|
||||
AliasList aliasList )
|
||||
@@ -52,7 +55,12 @@ public class P25Message extends Message
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
return "UNK";
|
||||
if( mCRC == null )
|
||||
{
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
return CRC.format( mCRC );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,8 @@ package decode.p25.message.tsbk;
|
||||
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import crc.CRC;
|
||||
import crc.CRCP25;
|
||||
import decode.p25.message.P25Message;
|
||||
import decode.p25.message.tsbk.motorola.MotorolaOpcode;
|
||||
import decode.p25.message.tsbk.vendor.VendorOpcode;
|
||||
@@ -11,6 +13,7 @@ import decode.p25.reference.Vendor;
|
||||
|
||||
public class TSBKMessage extends P25Message
|
||||
{
|
||||
public static final int MESSAGE_START = 64;
|
||||
public static final int LAST_BLOCK_FLAG = 64;
|
||||
public static final int ENCRYPTED_FLAG = 65;
|
||||
public static final int[] OPCODE = { 66,67,68,69,70,71 };
|
||||
@@ -28,13 +31,20 @@ public class TSBKMessage extends P25Message
|
||||
public static final int[] BLOCK10 = { 136,137,138,139,140,141,142,143 };
|
||||
public static final int[] BLOCK11 = { 144,145,146,147,148,149,150,151 };
|
||||
public static final int[] BLOCK12 = { 152,153,154,155,156,157,158,159 };
|
||||
|
||||
public static final int[] CRC = { 144,145,146,147,148,149,150,151,152,153,
|
||||
154,155,156,157,158,159 };
|
||||
public static final int CRC_START = 144;
|
||||
|
||||
public TSBKMessage( BitSetBuffer message, DataUnitID duid, AliasList aliasList )
|
||||
{
|
||||
super( message, duid, aliasList );
|
||||
|
||||
checkCRC();
|
||||
}
|
||||
|
||||
protected void checkCRC()
|
||||
{
|
||||
mCRC = new CRC[ 1 ];
|
||||
|
||||
mCRC[ 0 ] = CRCP25.correctCCITT80( mMessage, MESSAGE_START, CRC_START );
|
||||
}
|
||||
|
||||
public boolean isLastBlock()
|
||||
|
||||
@@ -13,7 +13,7 @@ import bits.BitSetBuffer;
|
||||
import bits.BitSetFullException;
|
||||
import bits.SyncPatternMatcher;
|
||||
import crc.CRC;
|
||||
import crc.CRCP25NetworkID;
|
||||
import crc.CRCP25;
|
||||
import decode.p25.P25Interleave;
|
||||
import decode.p25.TrellisHalfRate;
|
||||
import decode.p25.message.P25Message;
|
||||
@@ -220,7 +220,7 @@ public class P25MessageFramer implements Listener<Dibit>
|
||||
switch( mDUID )
|
||||
{
|
||||
case NID:
|
||||
CRC crc = CRCP25NetworkID.checkAndCorrect( mMessage );
|
||||
CRC crc = CRCP25.correctNID( mMessage );
|
||||
|
||||
if( crc != CRC.FAILED_CRC )
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user