mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Adds CRC checksum validation to LJ1200 messages (using Eric Cottrell's CRC polynomial)
This commit is contained in:
@@ -19,12 +19,12 @@ package crc;
|
||||
|
||||
public enum CRC
|
||||
{
|
||||
PASSED ( "*", "Pass" ),
|
||||
PASSED_INV ( "#", "Pass Invert" ),
|
||||
FAILED_CRC ( "f", "Fail CRC" ),
|
||||
FAILED_PARITY( "p", "Fail Parity" ),
|
||||
CORRECTED ( "C", "Corrected" ),
|
||||
UNKNOWN ( "-", "Unknown");
|
||||
PASSED ( "*", "PASS" ),
|
||||
PASSED_INV ( "#", "PASS INVERTED" ),
|
||||
FAILED_CRC ( "f", "FAIL CRC" ),
|
||||
FAILED_PARITY( "p", "FAIL PARITY" ),
|
||||
CORRECTED ( "C", "CORRECTED" ),
|
||||
UNKNOWN ( "-", "UNKNOWN");
|
||||
|
||||
private String mAbbreviation;
|
||||
private String mDisplayText;
|
||||
|
||||
205
src/crc/CRCLJ.java
Normal file
205
src/crc/CRCLJ.java
Normal file
@@ -0,0 +1,205 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* LJ CRC checksum utility
|
||||
*
|
||||
* LJ message blocks are 80 bits in length as follows:
|
||||
* 0 - 7: Bit reversals
|
||||
* 8 - 15: Sync pattern
|
||||
* 16 - 23: VRC
|
||||
* 24 - 31: LRC
|
||||
* 32 - 35: Function Code
|
||||
* 36 - 63: Address
|
||||
* 64 - 79: CRC
|
||||
*
|
||||
* Each field is transmitted in big-endian bit order, including the CRC. Only
|
||||
* the Function and Address fields are protected by the CRC.
|
||||
*
|
||||
* LJ uses a CRC-16 (0x6F63) with an initial fill of 0.
|
||||
*
|
||||
* Generating Polynomial: x14 + x13 + x11 + x10 + x9 + x8 + x6 + x5 + x1 + 1 (0x6F63)
|
||||
*/
|
||||
public class CRCLJ
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( CRCLJ.class );
|
||||
|
||||
private static final int MESSAGE_START = 32;
|
||||
private static final int CRC_START = 64;
|
||||
|
||||
private static int[] CHECKSUMS = new int[]
|
||||
{
|
||||
0x26EA, //Function 0 (MSB)
|
||||
0x1375, //Function 1
|
||||
0xBE0B, //Function 2
|
||||
0xE8B4, //Function 3 (LSB)
|
||||
0x745A, //Address 0 (MSB)
|
||||
0x3A2D, //Address 1
|
||||
0xAAA7, //Address 2
|
||||
0xE2E2, //Address 3
|
||||
0x7171, //Address 4
|
||||
0x8F09, //Address 5
|
||||
0xF035, //Address 6
|
||||
0xCFAB, //Address 7
|
||||
0xD064, //Address 8
|
||||
0x6832, //Address 9
|
||||
0x3419, //Address 10
|
||||
0xADBD, //Address 11
|
||||
0xE16F, //Address 12
|
||||
0xC706, //Address 13
|
||||
0x6383, //Address 14
|
||||
0x8670, //Address 15
|
||||
0x4338, //Address 16
|
||||
0x219C, //Address 17
|
||||
0x10CE, //Address 18
|
||||
0x0867, //Address 19
|
||||
0xB382, //Address 20
|
||||
0x59C1, //Address 21
|
||||
0x9B51, //Address 22
|
||||
0xFA19, //Address 23
|
||||
0xCABD, //Address 24
|
||||
0xD2EF, //Address 25
|
||||
0xDEC6, //Address 26
|
||||
0x6F63, //Address 27
|
||||
|
||||
//Single bit errors in the CRC Checksum
|
||||
0x8000, //CRC 0
|
||||
0x4000, //CRC 1
|
||||
0x2000, //CRC 2
|
||||
0x1000, //CRC 3
|
||||
0x0800, //CRC 4
|
||||
0x0400, //CRC 5
|
||||
0x0200, //CRC 6
|
||||
0x0100, //CRC 7
|
||||
0x0080, //CRC 8
|
||||
0x0040, //CRC 9
|
||||
0x0020, //CRC 10
|
||||
0x0010, //CRC 11
|
||||
0x0008, //CRC 12
|
||||
0x0004, //CRC 13
|
||||
0x0002, //CRC 14
|
||||
0x0001 //CRC 15
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if message bits 16 - 47 pass the LJ CRC checksum
|
||||
* contained in bits 48 - 63, using a lookup table of CRC checksum values
|
||||
* derived from the CRC-16 value
|
||||
*/
|
||||
public static CRC checkAndCorrect( BitSetBuffer msg )
|
||||
{
|
||||
int calculated = 0; //Starting value
|
||||
|
||||
/* Iterate the set bits and XOR running checksum with lookup value */
|
||||
for (int i = msg.nextSetBit( MESSAGE_START );
|
||||
i >= MESSAGE_START && i < CRC_START;
|
||||
i = msg.nextSetBit( i+1 ) )
|
||||
{
|
||||
calculated ^= CHECKSUMS[ i - MESSAGE_START ];
|
||||
}
|
||||
|
||||
int checksum = getChecksum( msg );
|
||||
|
||||
if( calculated == getChecksum( msg ) )
|
||||
{
|
||||
return CRC.PASSED;
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] errors = findBitErrors( calculated ^ checksum );
|
||||
|
||||
if( errors != null )
|
||||
{
|
||||
for( int error: errors )
|
||||
{
|
||||
msg.flip( MESSAGE_START + error );
|
||||
}
|
||||
|
||||
return CRC.CORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return CRC.FAILED_CRC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the integer value of the 15 bit crc checksum
|
||||
*/
|
||||
public static int getChecksum( BitSet msg )
|
||||
{
|
||||
int retVal = 0;
|
||||
|
||||
for( int x = 0; x < 16; x++ )
|
||||
{
|
||||
if( msg.get( x + CRC_START ) )
|
||||
{
|
||||
retVal += 1<<( 15 - x );
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bit position of any bit checksums that match the checksum
|
||||
* error pattern.
|
||||
*/
|
||||
public static int[] findBitErrors( int checksumError )
|
||||
{
|
||||
/* One bit errors */
|
||||
for( int x = 0; x < 48; x++ )
|
||||
{
|
||||
if( CHECKSUMS[ x ] == checksumError )
|
||||
{
|
||||
int[] errors = new int[ 1 ];
|
||||
|
||||
errors[ 0 ] = x;
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
/* Two bit errors */
|
||||
for( int x = 0; x < 48; x++ )
|
||||
{
|
||||
for( int y = 0; y < 48; y++ )
|
||||
{
|
||||
if( x != y &&
|
||||
( CHECKSUMS[ x ] ^ CHECKSUMS[ y ] ) == checksumError )
|
||||
{
|
||||
int[] errors = new int[ 2 ];
|
||||
|
||||
errors[ 0 ] = x;
|
||||
errors[ 1 ] = y;
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -18,18 +18,23 @@
|
||||
package decode.lj1200;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.BitSet;
|
||||
|
||||
import map.Plottable;
|
||||
import message.Message;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import crc.CRC;
|
||||
import crc.CRCFleetsync;
|
||||
import crc.CRCLJ;
|
||||
|
||||
public class LJ1200Message extends Message
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( LJ1200Message.class );
|
||||
|
||||
public static int[] SYNC = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
|
||||
|
||||
public static int[] VRC = { 23,22,21,20,19,18,17,16 };
|
||||
@@ -41,7 +46,7 @@ public class LJ1200Message extends Message
|
||||
public static int[] ADDRESS = { 63,62,61,60,59,58,57,56,55,54,53,52,51,50,
|
||||
49,48,47,46,45,44,43,42,41,40,39,38,37,36 };
|
||||
|
||||
public static int[] CRC = { 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79 };
|
||||
public static int[] MESSAGE_CRC = { 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79 };
|
||||
|
||||
private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" );
|
||||
|
||||
@@ -51,42 +56,33 @@ public class LJ1200Message extends Message
|
||||
|
||||
public LJ1200Message( BitSetBuffer message, AliasList list )
|
||||
{
|
||||
mMessage = message;
|
||||
mMessage = message;
|
||||
mAliasList = list;
|
||||
checkParity();
|
||||
|
||||
checkCRC();
|
||||
|
||||
switch( mCRC )
|
||||
{
|
||||
case CORRECTED:
|
||||
mLog.debug( "CORR:" + message.toString() );
|
||||
break;
|
||||
case FAILED_CRC:
|
||||
mLog.debug( "FAIL:" + message.toString() );
|
||||
break;
|
||||
case PASSED:
|
||||
mLog.debug( "PASS:" + message.toString() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkParity()
|
||||
private void checkCRC()
|
||||
{
|
||||
mCRC = detectAndCorrect( 16, 79 );
|
||||
}
|
||||
|
||||
private CRC detectAndCorrect( int start, int end )
|
||||
{
|
||||
BitSet original = mMessage.get( start, end );
|
||||
|
||||
CRC crc = CRCFleetsync.check( original );
|
||||
|
||||
if( crc == crc.FAILED_PARITY )
|
||||
{
|
||||
int[] errorBitPositions = CRCFleetsync.findBitErrors( original );
|
||||
|
||||
if( errorBitPositions != null )
|
||||
{
|
||||
for( int errorBitPosition: errorBitPositions )
|
||||
{
|
||||
mMessage.flip( start + errorBitPosition );
|
||||
}
|
||||
|
||||
crc = crc.CORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
mCRC = CRCLJ.checkAndCorrect( mMessage );
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
// return mCRC == CRC.PASSED || mCRC == CRC.CORRECTED;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -102,7 +98,7 @@ public class LJ1200Message extends Message
|
||||
|
||||
public String getCRC()
|
||||
{
|
||||
return mMessage.getHex( CRC, 4 );
|
||||
return mMessage.getHex( MESSAGE_CRC, 4 );
|
||||
}
|
||||
|
||||
public String getFunction()
|
||||
@@ -217,7 +213,7 @@ public class LJ1200Message extends Message
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
return "UNKNOWN";
|
||||
return mCRC.getDisplayText();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user