/*******************************************************************************
* 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 edac;
import java.util.BitSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bits.BinaryMessage;
/**
* 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 (LSB)
0x1375, //Function 1
0xBE0B, //Function 2
0xE8B4, //Function 3 (MSB)
0x745A, //Address 0 (LSB)
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 (MSB)
//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 FUNCTION AND ADDRESS bits pass the LJ CRC checksum
* using a lookup table of CRC checksum values derived from the CRC-16 value
*/
public static CRC checkAndCorrect( BinaryMessage message )
{
int 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 ];
}
int checksum = getChecksum( message );
if( calculated == checksum )
{
return CRC.PASSED;
}
else
{
int[] errors = findBitErrors( calculated ^ checksum );
if( errors != null )
{
for( int error: errors )
{
message.flip( MESSAGE_START + error );
}
return CRC.CORRECTED;
}
}
return CRC.FAILED_CRC;
}
/**
* Returns the integer value of the 16 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;
}
/**
* Identifies 1 or 2 bit error positions that match the checksum error value.
*/
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;
}
public static void main( String[] args )
{
BinaryMessage msg = BinaryMessage.load( "01010101101010101010000100011000000000100001000110000000001000010001100000000010" );
CRC crc = checkAndCorrect( msg );
mLog.debug( "CRC:" + crc.getDisplayText() );
}
}