mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 09:37:36 -05:00
Adds Galois 24/12/7 error correction and detection for use with P25 decoder.
This commit is contained in:
@@ -19,9 +19,14 @@ package bits;
|
|||||||
|
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class BitSetBuffer extends BitSet
|
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
|
* Logical (ie constructed) size of this bitset, despite the actual size of
|
||||||
@@ -524,4 +529,99 @@ public class BitSetBuffer extends BitSet
|
|||||||
|
|
||||||
return buffer;
|
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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ public class MessageActivityModel extends AbstractTableModel
|
|||||||
|
|
||||||
private boolean mNewMessagesFirst = true;
|
private boolean mNewMessagesFirst = true;
|
||||||
|
|
||||||
private FilterSet mMessageFilter;
|
private FilterSet<Message> mMessageFilter;
|
||||||
|
|
||||||
public MessageActivityModel( FilterSet messageFilter )
|
public MessageActivityModel( FilterSet<Message> messageFilter )
|
||||||
{
|
{
|
||||||
mMessageFilter = messageFilter;
|
mMessageFilter = messageFilter;
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ public class MessageActivityModel extends AbstractTableModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterSet getMessageFilter()
|
public FilterSet<Message> getMessageFilter()
|
||||||
{
|
{
|
||||||
return mMessageFilter;
|
return mMessageFilter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener
|
|||||||
"[grow,fill][][]" ) );
|
"[grow,fill][][]" ) );
|
||||||
|
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
FilterSet<Message> filter =
|
FilterSet<Message> filter = (FilterSet<Message>)
|
||||||
mDisplayedChannel
|
mDisplayedChannel
|
||||||
.getProcessingChain().getChannelState()
|
.getProcessingChain().getChannelState()
|
||||||
.getMessageActivityModel().getMessageFilter();
|
.getMessageActivityModel().getMessageFilter();
|
||||||
|
|||||||
@@ -375,10 +375,7 @@ public class CRCP25
|
|||||||
public static long getLongChecksum( BitSetBuffer message,
|
public static long getLongChecksum( BitSetBuffer message,
|
||||||
int crcStart, int crcLength )
|
int crcStart, int crcLength )
|
||||||
{
|
{
|
||||||
int[] checksumIndexes = BitSetBuffer
|
return message.getLong( crcStart, crcStart + crcLength );
|
||||||
.getFieldIndexes( crcStart, crcLength, false );
|
|
||||||
|
|
||||||
return message.getLong( checksumIndexes );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -387,10 +384,7 @@ public class CRCP25
|
|||||||
public static int getIntChecksum( BitSetBuffer message,
|
public static int getIntChecksum( BitSetBuffer message,
|
||||||
int crcStart, int crcLength )
|
int crcStart, int crcLength )
|
||||||
{
|
{
|
||||||
int[] checksumIndexes = BitSetBuffer
|
return message.getInt( crcStart, crcStart + crcLength );
|
||||||
.getFieldIndexes( crcStart, crcLength, false );
|
|
||||||
|
|
||||||
return message.getInt( checksumIndexes );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -50,12 +50,10 @@ public class CRCUtil
|
|||||||
|
|
||||||
message.set( x );
|
message.set( x );
|
||||||
|
|
||||||
message = decode( message, messageSize, polynomial, crcSize );
|
message = decode( message, 0, messageSize, polynomial, crcSize );
|
||||||
|
|
||||||
long checksum = message.getLong( checksumIndexes );
|
long checksum = message.getLong( checksumIndexes );
|
||||||
|
|
||||||
mLog.debug( "Index " + x + " Checksum:" + Long.toHexString( checksum ) );
|
|
||||||
|
|
||||||
crcTable[ x ] = checksum;
|
crcTable[ x ] = checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +68,48 @@ public class CRCUtil
|
|||||||
return crcTable;
|
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
|
* 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
|
* placed in the crc field which starts at index messageLength
|
||||||
*/
|
*/
|
||||||
public static BitSetBuffer decode( BitSetBuffer message,
|
public static BitSetBuffer decode( BitSetBuffer message,
|
||||||
|
int messageStart,
|
||||||
int messageSize,
|
int messageSize,
|
||||||
long polynomial,
|
long polynomial,
|
||||||
int crcSize )
|
int crcSize )
|
||||||
{
|
{
|
||||||
int MESSAGE_START = 0;
|
for (int i = message.nextSetBit( messageStart );
|
||||||
|
i >= messageStart && i < messageSize;
|
||||||
for (int i = message.nextSetBit( MESSAGE_START );
|
|
||||||
i >= MESSAGE_START && i < messageSize;
|
|
||||||
i = message.nextSetBit( i+1 ) )
|
i = message.nextSetBit( i+1 ) )
|
||||||
{
|
{
|
||||||
BitSetBuffer polySet = new BitSetBuffer( crcSize + i + 1 );
|
BitSetBuffer polySet = new BitSetBuffer( crcSize + i + 1 );
|
||||||
@@ -115,17 +154,9 @@ public class CRCUtil
|
|||||||
{
|
{
|
||||||
mLog.debug( "Starting" );
|
mLog.debug( "Starting" );
|
||||||
|
|
||||||
long polynomial = 0x104C11DB7l;
|
long polynomial = 0xC75l;
|
||||||
|
|
||||||
// String raw = "101111101110000000000000001101111110001100111100001110000111000011111111101010111011000011100100";
|
long[] table = generate( 12, 11, polynomial, 0x0, true, Parity.NONE );
|
||||||
// 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 );
|
|
||||||
mLog.debug( toCodeArray( table ) );
|
mLog.debug( toCodeArray( table ) );
|
||||||
mLog.debug( "Finished" );
|
mLog.debug( "Finished" );
|
||||||
}
|
}
|
||||||
|
|||||||
174
src/crc/Galois24.java
Normal file
174
src/crc/Galois24.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||||
|
* -----------------------------------------------------------------------
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user