From 412f1ac9f718cb12de9725175fa133ddec130a23 Mon Sep 17 00:00:00 2001 From: "dennis.sheirer" Date: Fri, 5 Dec 2014 12:59:16 +0000 Subject: [PATCH] Adds CRC checksum validation to LJ1200 messages (using Eric Cottrell's CRC polynomial) --- src/crc/CRC.java | 12 +- src/crc/CRCLJ.java | 205 +++++++++++++++++++++++++++ src/decode/lj1200/LJ1200Message.java | 62 ++++---- 3 files changed, 240 insertions(+), 39 deletions(-) create mode 100644 src/crc/CRCLJ.java diff --git a/src/crc/CRC.java b/src/crc/CRC.java index 509081f2..b11e9083 100644 --- a/src/crc/CRC.java +++ b/src/crc/CRC.java @@ -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; diff --git a/src/crc/CRCLJ.java b/src/crc/CRCLJ.java new file mode 100644 index 00000000..9da069e0 --- /dev/null +++ b/src/crc/CRCLJ.java @@ -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 + ******************************************************************************/ +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; + } +} diff --git a/src/decode/lj1200/LJ1200Message.java b/src/decode/lj1200/LJ1200Message.java index 6ac1eb67..fde954a3 100644 --- a/src/decode/lj1200/LJ1200Message.java +++ b/src/decode/lj1200/LJ1200Message.java @@ -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