diff --git a/src/bits/BitSetBuffer.java b/src/bits/BitSetBuffer.java index 3c8ef021..c5951313 100644 --- a/src/bits/BitSetBuffer.java +++ b/src/bits/BitSetBuffer.java @@ -19,9 +19,14 @@ package bits; import java.util.BitSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class BitSetBuffer extends BitSet { - private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( BitSetBuffer.class ); + + private static final long serialVersionUID = 1L; /** * Logical (ie constructed) size of this bitset, despite the actual size of @@ -524,4 +529,99 @@ public class BitSetBuffer extends BitSet return buffer; } + + /** + * Left rotates the bits between start and end indices, number of places. + */ + public void rotateLeft( int places, int startIndex, int endIndex ) + { + for( int x = 0; x < places; x++ ) + { + rotateLeft( startIndex, endIndex ); + } + } + + /** + * Left rotates the bits between start and end and wraps the left-most + * bit around to the end. + */ + public void rotateLeft( int startIndex, int endIndex ) + { + boolean wrapBit = get( startIndex ); + + for( int x = startIndex; x < endIndex; x++ ) + { + if( get( x + 1 ) ) + { + set( x ); + } + else + { + clear( x ); + } + } + + if( wrapBit ) + { + set( endIndex ); + } + else + { + clear( endIndex ); + } + } + + /** + * Right rotates the bits between start and end indices, number of places. + */ + public void rotateRight( int places, int startIndex, int endIndex ) + { + for( int x = 0; x < places; x++ ) + { + rotateRight( startIndex, endIndex ); + } + } + + /** + * Right rotates the bits between start and end and wraps the right-most + * bit around to the start. + */ + public void rotateRight( int startIndex, int endIndex ) + { + boolean wrapBit = get( endIndex ); + + for( int x = endIndex - 1; x >= startIndex; x-- ) + { + if( get( x ) ) + { + set( x + 1 ); + } + else + { + clear( x + 1 ); + } + } + + if( wrapBit ) + { + set( startIndex ); + } + else + { + clear( startIndex ); + } + } + + /** + * Performs exclusive or of the value against this bitset starting at the + * offset position using width bits from the value. + */ + public void xor( int offset, int width, int value ) + { + BitSetBuffer mask = new BitSetBuffer( this.size() ); + + mask.load( offset, width, value ); + + this.xor( mask ); + } } diff --git a/src/controller/activity/MessageActivityModel.java b/src/controller/activity/MessageActivityModel.java index 40a134bd..7ee2e987 100644 --- a/src/controller/activity/MessageActivityModel.java +++ b/src/controller/activity/MessageActivityModel.java @@ -52,9 +52,9 @@ public class MessageActivityModel extends AbstractTableModel private boolean mNewMessagesFirst = true; - private FilterSet mMessageFilter; + private FilterSet mMessageFilter; - public MessageActivityModel( FilterSet messageFilter ) + public MessageActivityModel( FilterSet messageFilter ) { mMessageFilter = messageFilter; } @@ -82,7 +82,7 @@ public class MessageActivityModel extends AbstractTableModel }); } - public FilterSet getMessageFilter() + public FilterSet getMessageFilter() { return mMessageFilter; } diff --git a/src/controller/activity/MessageActivityPanel.java b/src/controller/activity/MessageActivityPanel.java index 8ce74cb2..6a216e1f 100644 --- a/src/controller/activity/MessageActivityPanel.java +++ b/src/controller/activity/MessageActivityPanel.java @@ -273,7 +273,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener "[grow,fill][][]" ) ); @SuppressWarnings( "unchecked" ) - FilterSet filter = + FilterSet filter = (FilterSet) mDisplayedChannel .getProcessingChain().getChannelState() .getMessageActivityModel().getMessageFilter(); diff --git a/src/crc/CRCP25.java b/src/crc/CRCP25.java index 2560f99d..8d4a407d 100644 --- a/src/crc/CRCP25.java +++ b/src/crc/CRCP25.java @@ -375,10 +375,7 @@ public class CRCP25 public static long getLongChecksum( BitSetBuffer message, int crcStart, int crcLength ) { - int[] checksumIndexes = BitSetBuffer - .getFieldIndexes( crcStart, crcLength, false ); - - return message.getLong( checksumIndexes ); + return message.getLong( crcStart, crcStart + crcLength ); } /** @@ -387,10 +384,7 @@ public class CRCP25 public static int getIntChecksum( BitSetBuffer message, int crcStart, int crcLength ) { - int[] checksumIndexes = BitSetBuffer - .getFieldIndexes( crcStart, crcLength, false ); - - return message.getInt( checksumIndexes ); + return message.getInt( crcStart, crcStart + crcLength ); } /** diff --git a/src/crc/CRCUtil.java b/src/crc/CRCUtil.java index 589382f4..3d33f037 100644 --- a/src/crc/CRCUtil.java +++ b/src/crc/CRCUtil.java @@ -50,12 +50,10 @@ public class CRCUtil message.set( x ); - message = decode( message, messageSize, polynomial, crcSize ); + message = decode( message, 0, messageSize, polynomial, crcSize ); long checksum = message.getLong( checksumIndexes ); - mLog.debug( "Index " + x + " Checksum:" + Long.toHexString( checksum ) ); - crcTable[ x ] = checksum; } @@ -70,6 +68,48 @@ public class CRCUtil return crcTable; } + /** + * Generates the checksum table and treats the final bit of each check value + * as a parity bit that complies with the request parity argument. + */ + public static long[] generate( int messageSize, + int crcSize, + long polynomial, + long initialFill, + boolean includeCRCBitErrors, + Parity parity ) + { + long[] table = generate( messageSize, crcSize, polynomial, initialFill, + includeCRCBitErrors ); + + if( parity == Parity.NONE ) + { + return table; + } + else + { + for( int x = 0; x < table.length; x++ ) + { + table[ x ] <<= 1; + + if( parity( table[ x ] ) != parity ) + { + table[ x ] ^= 0x1; + } + } + + return table; + } + } + + public enum Parity{ EVEN, ODD, NONE }; + + /* Determines the parity (number of 1 bits) for the value */ + public static Parity parity( long value ) + { + return Long.bitCount( value ) % 2 == 0 ? Parity.EVEN : Parity.ODD; + } + /** * Performs binary division of the polynomial into the message for bits @@ -91,14 +131,13 @@ public class CRCUtil * placed in the crc field which starts at index messageLength */ public static BitSetBuffer decode( BitSetBuffer message, + int messageStart, int messageSize, long polynomial, int crcSize ) { - int MESSAGE_START = 0; - - for (int i = message.nextSetBit( MESSAGE_START ); - i >= MESSAGE_START && i < messageSize; + for (int i = message.nextSetBit( messageStart ); + i >= messageStart && i < messageSize; i = message.nextSetBit( i+1 ) ) { BitSetBuffer polySet = new BitSetBuffer( crcSize + i + 1 ); @@ -115,17 +154,9 @@ public class CRCUtil { mLog.debug( "Starting" ); - long polynomial = 0x104C11DB7l; + long polynomial = 0xC75l; -// String raw = "101111101110000000000000001101111110001100111100001110000111000011111111101010111011000011100100"; -// BitSetBuffer buffer = BitSetBuffer.load( raw ); -// mLog.debug( "ORIG:" + buffer.toString() ); -// BitSetBuffer processed = decode( buffer, 64, polynomial, 32 ); -// mLog.debug( "PROC:" + processed.toString() ); - - long[] table = generate( 256, 32, 0x104C11DB7l, 0xFFFFFFFFl, true ); - -// long[] table = generate( 80, 16, 0x11021l, 0xFFFFl, true ); + long[] table = generate( 12, 11, polynomial, 0x0, true, Parity.NONE ); mLog.debug( toCodeArray( table ) ); mLog.debug( "Finished" ); } diff --git a/src/crc/Galois24.java b/src/crc/Galois24.java new file mode 100644 index 00000000..e84bc70d --- /dev/null +++ b/src/crc/Galois24.java @@ -0,0 +1,174 @@ +package crc; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import bits.BitSetBuffer; + +/******************************************************************************* + * 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 + * ----------------------------------------------------------------------- + * Galois24 decoder based on Hank Wallace's tutorial/algorithm located at: + * http://www.aqdi.com/golay.htm + ******************************************************************************/ + +/** + * Galois 24/12/7 decoder + */ +public class Galois24 +{ + private final static Logger mLog = LoggerFactory.getLogger( Galois24.class ); + + /** + * Galois 24/12 checksums generated by: + * + * CRCUtil.generate( 12, 11, 0xC75, 0x0, true ); + */ + public static final int[] CHECKSUMS = new int[] + { + 0x63A, 0x31D, 0x7B4, 0x3DA, 0x1ED, 0x6CC, 0x366, 0x1B3, + 0x6E3, 0x54B, 0x49F, 0x475, 0x400, 0x200, 0x100, 0x080, + 0x040, 0x020, 0x010, 0x008, 0x004, 0x002, 0x001 + }; + + private static int calculateChecksum( BitSetBuffer message, int startIndex ) + { + int calculated = 0; //Starting value + + /* Iterate the set bits and XOR running checksum with lookup value */ + for (int i = message.nextSetBit( startIndex ); + i >= startIndex && i < startIndex + 12; + i = message.nextSetBit( i+1 ) ) + { + calculated ^= CHECKSUMS[ i - startIndex ]; + } + + return calculated; + } + + public static boolean checkAndCorrect( BitSetBuffer message, int startIndex ) + { + boolean parityError = message.cardinality() % 2 != 0; + + int syndrome = getSyndrome( message, startIndex ); + + /* No errors */ + if( syndrome == 0 ) + { + if( parityError ) + { + message.flip( startIndex + 23 ); + } + + return true; + } + + /* Get original message value */ + int original = message.getInt( 0, 22 ); + + int index = -1; + int syndromeWeight = 3; + int maskBit = 0; + int errors = 0; + + while( index < 23 ) + { + if( index != -1 ) + { + /* restore the previous flipped bit */ + if( index > 0 ) + { + message.flip( maskBit - 1 ); + } + + message.flip( maskBit ); + + syndromeWeight = 2; + } + + syndrome = getSyndrome( message, startIndex ); + + if( syndrome > 0 ) + { + for( int i = 0; i < 23; i++ ) + { + + errors = Integer.bitCount( syndrome ); + + if( errors <= syndromeWeight ) + { + message.xor( 12, 11, syndrome ); + + message.rotateRight( i, startIndex, startIndex + 22 ); + + if( index >= 0 ) + { + errors ++; + } + + int corrected = message.getInt( 0, 22 ); + + if( Integer.bitCount( original ^ corrected ) > 3 ) + { + return false; + } + + return true; + } + else + { + message.rotateLeft( startIndex, startIndex + 22 ); + syndrome = getSyndrome( message, startIndex ); + } + } + + index++; + } + } + + return false; + } + + private static int getSyndrome( BitSetBuffer message, int startIndex ) + { + int calculated = calculateChecksum( message, startIndex ); + + int checksum = message.getInt( startIndex + 12, startIndex + 22 ); + + return ( checksum ^ calculated ); + } + + public static void main( String[] args ) + { + /* NID + TDULC message */ + String msg = "0010011000001111100100110011000100111010000111010001000000000000000000000000000000000000000000000100101010010111000000000000000000000000000010010111101111111111000000000000000000000000000000000000000000000000000110001011100001111101010110111001001011010010101111010010000010110011101111111100100000010001111100011010010100001101111001111001100000111000"; + + BitSetBuffer message = BitSetBuffer.load( msg ); + + mLog.debug( "MSG: " + message.toString() ); + + int x = 64; + + while( x < msg.length() ) + { + boolean passes = checkAndCorrect( message, x ); + + mLog.debug( "X:" + x + " Pass:" + passes ); + x += 24; + } + } +}