Updates pull request from Roman Tsypuk to convert the build system from

ant to gradle.  Removes linux and windows application launch scripts and
produces an executable jar with all resources included.
This commit is contained in:
Denny
2017-12-17 07:28:03 -05:00
parent a1535c4157
commit 9316599dec
981 changed files with 8368 additions and 9049 deletions

View File

@@ -0,0 +1,106 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*******************************************************************************
* 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/>
******************************************************************************/
public class BCH_63_16_11 extends BerlekempMassey_63
{
private final static Logger mLog = LoggerFactory.getLogger( BCH_63_16_11.class );
/**
* BCH( 63,16,11) decoder
*/
public BCH_63_16_11()
{
/* TT = 11 = maximum correctable bit errors */
super( 11 );
}
/**
* Performs error detection and correction on the first 63 bits of the
* message argument. If the message is correctable, only the first 16 bits
* (information bits) are corrected.
*
* @return - true = success, false = failure
*/
public BinaryMessage correctNID( BinaryMessage message )
{
CRC status = CRC.PASSED;
int[] original = message.toReverseIntegerArray( 0, 62 );
int[] corrected = new int[ 63 ];
boolean irrecoverableErrors = decode( original, corrected );
if( irrecoverableErrors )
{
message.setCRC( CRC.FAILED_CRC );
return message;
}
else
{
for( int x = 0; x < 16; x++ )
{
int index = 63 - x - 1;
if( corrected[ index ] != original[ index ] )
{
status = CRC.CORRECTED;
if( corrected[ index ] == 1 )
{
message.set( x );
}
else
{
message.clear( x );
}
}
}
}
message.setCRC( status );
return message;
}
public static void main( String[] args )
{
String orig = "0010011000000011010010100000000110000111110011101010001010110000";
String error = "0001010100000011010010100000000110000111100011001010001010110000";
BinaryMessage errorMessage = BinaryMessage.load( error );
BCH_63_16_11 bch = new BCH_63_16_11();
mLog.debug( "ORIG:" + orig );
mLog.debug( " ERR:" + errorMessage.toString() );
mLog.debug( " " );
errorMessage = bch.correctNID( errorMessage );
mLog.debug( " CRC: " + errorMessage.getCRC().name() );
mLog.debug( "CORR:" + errorMessage.toString() );
mLog.debug( "ORIG:" + orig );
}
}

View File

@@ -0,0 +1,546 @@
package io.github.dsheirer.edac;
/*******************************************************************************
* 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/>
*
* --------------------------------------------------------------------
* Based on Ed Fuentetaja's DSD RS decoder, May 2014, at:
* https://github.com/szechyjs/dsd/blob/master/ReedSolomon.hpp
* --------------------------------------------------------------------
* Adapted from Mr. Simon Rockliff's version at: http://www.eccpage.com/rs.c
*
* Simon Rockliff, University of Adelaide 21/9/89
* 26/6/91 Slight modifications to remove a compiler dependent bug which
* hadn't previously surfaced. A few extra comments added for clarity.
* Appears to all work fine, ready for posting to net!
*
* Notice
* --------
* This program may be freely modified and/or given to whoever wants it.
* A condition of such distribution is that the author's contribution be
* acknowledged by his name being left in the comments heading the program,
* however no responsibility is accepted for any financial or other loss which
* may result from some unforseen errors or malfunctioning of the program
* during use.
* Simon Rockliff, 26th June 1991
******************************************************************************/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Berlekemp Massey decoder for 63-bit primitive RS/BCH block codes
*/
public class BerlekempMassey_63
{
public final static Logger mLog = LoggerFactory.getLogger( BerlekempMassey_63.class );
/* Golay field size GF( 2 ** MM ) */
private static final int MM = 6;
/* Codeword Length: NN = 2 ** MM - 1 */
private static final int NN = 63;
/* Hamming distance between codewords: NN - KK + 1 = 2 * TT + 1 */
private static int KK;
/* Maximum number of errors that can be corrected */
int TT;
int[] alpha_to;
int[] index_of;
int[] gg;
public BerlekempMassey_63( int tt )
{
TT = tt;
KK = NN - 2 * TT;
alpha_to = new int[ NN + 1 ];
index_of = new int[ NN + 1 ];
gg = new int[ NN - KK + 1 ];
/* P25 generator polynomial */
int generator_polinomial[] = { 1, 1, 0, 0, 0, 0, 1 };
generate_gf( generator_polinomial );
gen_poly();
}
/**
* Generates the Golay Field.
*
* Generates a GF( 2**mm ) from the irreducible polynomial
* p(X) in pp[0]..pp[mm]
*
* Lookup tables:
* index_of[] = polynomial form
* alpha_to[] = contains j=alpha**i;
*
* Polynomial form -> Index form index_of[j=alpha**i] = i
*
* alpha_to = 2 is the primitive element of GF( 2**mm )
*
* @param generator_polynomial
*/
private void generate_gf( int[] generator_polynomial )
{
int i;
int mask = 1;
alpha_to[ MM ] = 0;
for( i = 0; i < MM; i++ )
{
alpha_to[ i ] = mask;
index_of[ alpha_to[ i ] ] = i;
if( generator_polynomial[ i ] != 0 )
{
alpha_to[ MM ] ^= mask;
}
mask <<= 1;
}
index_of[ alpha_to[ MM ] ] = MM;
mask >>= 1;
for ( i = MM + 1; i < NN; i++ )
{
if( alpha_to[ i - 1 ] >= mask )
{
alpha_to[ i ] = alpha_to[ MM ] ^ ( ( alpha_to[ i - 1 ] ^ mask ) << 1 );
}
else
{
alpha_to[ i ] = alpha_to[ i - 1 ] << 1;
}
index_of[ alpha_to[ i ] ] = i;
}
index_of[ 0 ] = -1;
}
/**
* Generates the polynomial for a TT-error correction code.
*
* Length NN = ( 2 ** MM -1 ) Reed Solomon code from the product of
* (X+alpha**i), i=1..2*tt
*/
private void gen_poly()
{
int i, j;
gg[ 0 ] = 2; /* primitive element alpha = 2 for GF(2**mm) */
gg[ 1 ] = 1; /* g(x) = (X+alpha) initially */
for( i = 2; i <= NN - KK; i++ )
{
gg[ i ] = 1;
for( j = i - 1; j > 0; j-- )
{
if( gg[ j ] != 0 )
{
gg[ j ] = gg[ j - 1 ] ^ alpha_to[ ( index_of[ gg[ j ] ] + i ) % NN ];
}
else
{
gg[ j ] = gg[ j - 1 ];
}
}
/* gg[0] can never be zero */
gg[ 0 ] = alpha_to[ ( index_of[ gg[ 0 ] ] + i ) % NN ];
}
/* convert gg[] to index form for quicker encoding */
for( i = 0; i <= NN - KK; i++ )
{
gg[ i ] = index_of[ gg[ i ] ];
}
}
/**
* Decodes
* @param input
* @param output
* @return
*/
/* assume we have received bits grouped into mm-bit symbols in recd[i],
i=0..(nn-1), and recd[i] is polynomial form.
We first compute the 2*tt syndromes by substituting alpha**i into rec(X) and
evaluating, storing the syndromes in s[i], i=1..2tt (leave s[0] zero) .
Then we use the Berlekamp iteration to find the error location polynomial
elp[i]. If the degree of the elp is >tt, we cannot correct all the errors
and hence just put out the information symbols uncorrected. If the degree of
elp is <=tt, we substitute alpha**i , i=1..n into the elp to get the roots,
hence the inverse roots, the error location numbers. If the number of errors
located does not equal the degree of the elp, we have more than tt errors
and cannot correct them. Otherwise, we then solve for the error value at
the error location and correct the error. The procedure is that found in
Lin and Costello. For the cases where the number of errors is known to be too
large to correct, the information symbols as received are output (the
advantage of systematic encoding is that hopefully some of the information
symbols will be okay and that if we are in luck, the errors are in the
parity part of the transmitted codeword). Of course, these insoluble cases
can be returned as error flags to the calling routine if desired. */
public boolean decode( final int[] input, int[] output ) //input, output
{
int u, q;
int[][] elp = new int[ NN - KK + 2 ][ NN - KK ];
int[] d = new int[ NN - KK + 2 ];
int[] l = new int[ NN - KK + 2 ];
int[] u_lu = new int[ NN - KK + 2 ];
int[] s = new int[ NN - KK + 1 ];
int count = 0;
boolean syn_error = false;
int[] root = new int[ TT ];
int[] loc = new int[ TT ];
int[] z = new int[ TT + 1 ];
int[] err = new int[ NN ];
int[] reg = new int[ TT + 1 ];
boolean irrecoverable_error = false;
/* put recd[i] into index form (ie as powers of alpha) */
for( int i = 0; i < NN; i++ )
{
output[ i ] = index_of[ input[ i ] ];
}
/* first form the syndromes */
for( int i = 1; i <= NN - KK; i++ )
{
s[ i ] = 0;
for( int j = 0; j < NN; j++ )
{
if( output[ j ] != -1 )
{
/* recd[j] in index form */
s[ i ] ^= alpha_to[ ( output[ j ] + i * j ) % NN ];
}
}
/* convert syndrome from polynomial form to index form */
if( s[ i ] != 0 )
{
/* set flag if non-zero syndrome => error */
syn_error = true;
}
s[ i ] = index_of[ s[ i ] ];
}
if( syn_error ) /* if errors, try and correct */
{
/* compute the error location polynomial via the Berlekamp iterative algorithm,
following the terminology of Lin and Costello : d[u] is the 'mu'th
discrepancy, where u='mu'+1 and 'mu' (the Greek letter!) is the step number
ranging from -1 to 2*tt (see L&C), l[u] is the
degree of the elp at that step, and u_l[u] is the difference between the
step number and the degree of the elp.
*/
/* initialise table entries */
d[ 0 ] = 0; /* index form */
d[ 1 ] = s[ 1 ]; /* index form */
elp[ 0 ][ 0 ] = 0; /* index form */
elp[ 1 ][ 0 ] = 1; /* polynomial form */
for( int i = 1; i < NN - KK; i++ )
{
elp[ 0 ][ i ] = -1; /* index form */
elp[ 1 ][ i ] = 0; /* polynomial form */
}
l[ 0 ] = 0;
l[ 1 ] = 0;
u_lu[ 0 ] = -1;
u_lu[ 1 ] = 0;
u = 0;
do
{
u++;
if( d[ u ] == -1 )
{
l[ u + 1 ] = l[ u ];
for( int i = 0; i <= l[ u ]; i++ )
{
elp[ u + 1 ][ i ] = elp[ u ][ i ];
elp[ u ][ i ] = index_of[ elp[ u ][ i ] ];
}
}
else
/* search for words with greatest u_lu[q] for which d[q]!=0 */
{
q = u - 1;
while( ( d[ q ] == -1 ) && ( q > 0 ) )
{
q--;
}
/* have found first non-zero d[q] */
if( q > 0 )
{
int j = q;
do
{
j--;
if( ( d[ j ] != -1 ) && ( u_lu[ q ] < u_lu[ j ] ) )
{
q = j;
}
}
while( j > 0 );
};
/* have now found q such that d[u]!=0 and u_lu[q] is maximum */
/* store degree of new elp polynomial */
if( l[ u ] > l[ q ] + u - q )
{
l[ u + 1 ] = l[ u ];
}
else
{
l[ u + 1 ] = l[ q ] + u - q;
}
/* form new elp(x) */
for( int i = 0; i < NN - KK; i++ )
{
elp[ u + 1 ][ i ] = 0;
}
for( int i = 0; i <= l[q]; i++ )
{
if( elp[ q ][ i ] != -1 )
{
elp[ u + 1 ][ i + u - q ] =
alpha_to[ ( d[ u ] + NN - d[ q ]
+ elp[ q ][ i ]) % NN ];
}
}
for( int i = 0; i <= l[u]; i++ )
{
elp[ u + 1 ][ i ] ^= elp[ u ][ i ];
elp[ u ][ i ] = index_of[ elp[ u ][ i ] ]; /*convert old elp value to index*/
}
}
u_lu[ u + 1 ] = u - l[ u + 1 ];
/* form (u+1)th discrepancy */
if( u < NN - KK ) /* no discrepancy computed on last iteration */
{
if ( s[ u + 1 ] != -1 )
{
d[ u + 1 ] = alpha_to[ s[ u + 1 ] ];
}
else
{
d[ u + 1 ] = 0;
}
for( int i = 1; i <= l[ u + 1 ]; i++ )
{
if( ( s[ u + 1 - i ] != -1 ) && ( elp[ u + 1 ][ i] != 0 ) )
{
d[ u + 1 ] ^= alpha_to[ ( s[ u + 1 - i ]
+ index_of[ elp[ u + 1 ][ i ] ] ) % NN ];
}
}
d[ u + 1 ] = index_of[ d[ u + 1 ] ]; /* put d[u+1] into index form */
}
}
while( ( u < NN - KK ) && ( l[ u + 1 ] <= TT) );
u++;
if( l[ u ] <= TT ) /* can correct error */
{
/* put elp into index form */
for( int i = 0; i <= l[u]; i++ )
{
elp[ u ][ i ] = index_of[ elp[ u ][ i ] ];
}
/* find roots of the error location polynomial */
for( int i = 1; i <= l[u]; i++ )
{
reg[ i ] = elp[ u ][ i ];
}
count = 0;
for( int i = 1; i <= NN; i++ )
{
q = 1;
for( int j = 1; j <= l[u]; j++ )
{
if( reg[ j ] != -1 )
{
reg[ j ] = ( reg[ j ] + j ) % NN;
q ^= alpha_to[ reg[ j ] ];
};
}
if( q == 0 ) /* store root and error location number indices */
{
root[ count ] = i;
loc[ count ] = NN - i;
count++;
};
};
if( count == l[ u ] ) /* no. roots = degree of elp hence <= tt errors */
{
/* form polynomial z(x) */
for( int i = 1; i <= l[ u ]; i++ ) /* Z[0] = 1 always - do not need */
{
if( ( s[ i ] != -1 ) && ( elp[ u ][ i ] != -1 ) )
{
z[ i ] = alpha_to[ s[ i ] ] ^ alpha_to[ elp[ u ][ i ] ];
}
else if( ( s[ i ] != -1 ) && ( elp[ u ][ i ] == -1 ) )
{
z[ i ] = alpha_to[ s[ i ] ];
}
else if( ( s[ i ] == -1 ) && ( elp[ u ][ i ] != -1 ) )
{
z[ i ] = alpha_to[ elp[ u ][ i ] ];
}
else
{
z[ i ] = 0;
}
for( int j = 1; j < i; j++ )
{
if( ( s[ j ] != -1 ) && ( elp[ u ][ i - j ] != -1 ) )
{
z[ i ] ^= alpha_to[ ( elp[ u ][ i - j ] + s[ j ] ) % NN ];
}
}
z[ i ] = index_of[ z[ i ] ]; /* put into index form */
};
/* evaluate errors at locations given by error location numbers loc[i] */
for( int i = 0; i < NN; i++ )
{
err[ i ] = 0;
if( output[ i ] != -1 ) /* convert recd[] to polynomial form */
{
output[ i ] = alpha_to[ output[ i ] ];
}
else
{
output[ i ] = 0;
}
}
for( int i = 0; i < l[ u ]; i++ ) /* compute numerator of error term first */
{
err[ loc[ i ] ] = 1; /* accounts for z[0] */
for( int j = 1; j <= l[ u ]; j++ )
{
if( z[ j ] != -1 )
{
err[ loc[ i ] ] ^= alpha_to[ ( z[ j ] + j * root[ i ] ) % NN ];
}
}
if( err[ loc[ i ] ] != 0 )
{
err[ loc[ i ] ] = index_of[ err[ loc[ i ] ] ];
q = 0; /* form denominator of error term */
for (int j = 0; j < l[u]; j++)
{
if (j != i)
{
q += index_of[1 ^ alpha_to[(loc[j] + root[i]) % NN]];
}
}
q = q % NN;
err[loc[i]] = alpha_to[(err[loc[i]] - q + NN) % NN];
output[loc[i]] ^= err[loc[i]]; /*recd[i] must be in polynomial form */
}
}
}
else
{
/* no. roots != degree of elp => >tt errors and cannot solve */
irrecoverable_error = true;
}
}
else
{
/* elp has degree >tt hence cannot solve */
irrecoverable_error = true;
}
}
else
{
/* no non-zero syndromes => no errors: output received codeword */
for (int i = 0; i < NN; i++)
{
if (output[i] != -1) /* convert recd[] to polynomial form */
{
output[i] = alpha_to[output[i]];
}
else
{
output[i] = 0;
}
}
}
if( irrecoverable_error )
{
for (int i = 0; i < NN; i++) /* could return error flag if desired */
{
if (output[i] != -1) /* convert recd[] to polynomial form */
{
output[i] = alpha_to[output[i]];
}
else
{
output[i] = 0; /* just output received codeword as is */
}
}
}
return irrecoverable_error;
}
}

View File

@@ -0,0 +1,73 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
public enum CRC
{
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;
CRC( String abbreviation, String displayText )
{
mAbbreviation = abbreviation;
mDisplayText = displayText;
}
public String getAbbreviation()
{
return mAbbreviation;
}
public String getDisplayText()
{
return mDisplayText;
}
public static String format( CRC[] checks )
{
if( checks == null )
{
return CRC.UNKNOWN.getAbbreviation();
}
StringBuilder sb = new StringBuilder();
for( int x = 0; x < checks.length; x++ )
{
CRC check = checks[ x ];
if( check != null )
{
sb.append( check.getAbbreviation() );
}
else
{
sb.append( CRC.UNKNOWN.getAbbreviation() );
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,207 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
import java.util.BitSet;
/**
* Fleetsync CRC checksum utility
*
* Fleetsync message blocks are 64 bits in length as follows:
* 0 - 47: message bits
* 48 - 62: CRC-15 check bits
* 63: even parity bit
*
* Fleetsync uses a CRC-15 (0x6815) with an initial fill of 0x0001, coupled with
* an even parity checkbit.
*
* CRC-15 Generating Polynomial: x15 + x14 + x13 + x11 + x4 + x2 + 1 (0x6815)
*/
public class CRCFleetsync
{
private static short[] sCHECKSUMS = new short[] {
0x740A, // 111010000001010 Bit 0
0x3A05, // 011101000000101 Bit 1
0x6908, // 110100100001000 Bit 2
0x3484, // 011010010000100 Bit 3
0x1A42, // 001101001000010 Bit 4
0x0D21, // 000110100100001 Bit 5
0x729A, // 111001010011010 Bit 6
0x394D, // 011100101001101 Bit 7
0x68AC, // 110100010101100 Bit 8
0x3456, // 011010001010110 Bit 9
0x1A2B, // 001101000101011 Bit 10
0x791F, // 111100100011111 Bit 11
0x4885, // 100100010000101 Bit 12
0x5048, // 101000001001000 Bit 13
0x2824, // 010100000100100 Bit 14
0x1412, // 001010000010010 Bit 15
0x0A09, // 000101000001001 Bit 16
0x710E, // 111000100001110 Bit 17
0x3887, // 011100010000111 Bit 18
0x6849, // 110100001001001 Bit 19
0x402E, // 100000000101110 Bit 20
0x2017, // 010000000010111 Bit 21
0x6401, // 110010000000001 Bit 22
0x460A, // 100011000001010 Bit 23
0x2305, // 010001100000101 Bit 24
0x6588, // 110010110001000 Bit 25
0x32C4, // 011001011000100 Bit 26
0x1962, // 001100101100010 Bit 27
0x0CB1, // 000110010110001 Bit 28
0x7252, // 111001001010010 Bit 29
0x3929, // 011100100101001 Bit 30
0x689E, // 110100010011110 Bit 31
0x344F, // 011010001001111 Bit 32
0x6E2D, // 110111000101101 Bit 33
0x431C, // 100001100011100 Bit 34
0x218E, // 010000110001110 Bit 35
0x10C7, // 001000011000111 Bit 36
0x7C69, // 111110001101001 Bit 37
0x4A3E, // 100101000111110 Bit 38
0x251F, // 010010100011111 Bit 39
0x6685, // 110011010000101 Bit 40
0x4748, // 100011101001000 Bit 41
0x23A4, // 010001110100100 Bit 42
0x11D2, // 001000111010010 Bit 43
0x08E9, // 000100011101001 Bit 44
0x707E, // 111000001111110 Bit 45
0x383F, // 011100000111111 Bit 46
0x6815, // 110100000010101 Bit 47
//Single bit errors in the CRC Checksum
0x4000, //Bit 48
0x2000, //Bit 49
0x1000, //Bit 50
0x0800, //Bit 51
0x0400, //Bit 52
0x0200, //Bit 53
0x0100, //Bit 54
0x0080, //Bit 55
0x0040, //Bit 56
0x0020, //Bit 57
0x0010, //Bit 58
0x0008, //Bit 59
0x0004, //Bit 60
0x0002, //Bit 61
0x0001 //Bit 62
};
/**
* Determines if message bits 0 - 47 pass the Fleetsync CRC checksum
* contained in bits 48 - 63, using a lookup table of CRC checksum values
* derived from the CRC-15 value, and verifies the message has even parity
*/
public static CRC check( BitSet msg )
{
CRC crc = CRC.UNKNOWN;
int calculated = 1; //Starting value
//Check even parity
if( msg.cardinality() % 2 == 0 )
{
//Iterate bits that are set and XOR running checksum with lookup value
for (int i = msg.nextSetBit( 0 ); i >= 0 && i < 48; i = msg.nextSetBit( i+1 ) )
{
calculated ^= sCHECKSUMS[ i ];
}
if( calculated == getChecksum( msg ) )
{
crc = CRC.PASSED;
}
else
{
crc = CRC.FAILED_CRC;
}
}
else
{
crc = CRC.FAILED_PARITY;
}
return 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 < 15; x++ )
{
if( msg.get( x + 48 ) )
{
retVal += 1<<( 14 - x );
}
}
return retVal;
}
/**
* Determines the errant bit positions and returns them in an array of
* integer bit positions.
*
* Note: currently only detects single-bit errors
*
* @param msg to be checked for errors
* @return - array of integer positions of bits that need flipped
*/
public static int[] findBitErrors( BitSet msg )
{
int[] retVal = null;
int checksum = getChecksum( msg );
//Remove the initial fill value (1)
checksum ^= 1;
//Iterate set message bits, removing their respective checksum value
//from the transmitted checksum, to arrive at the remainder
for (int i = msg.nextSetBit( 0 ); i >= 0 && i < 48; i = msg.nextSetBit( i+1 ) )
{
checksum ^= sCHECKSUMS[ i ];
}
//If at this point the checksum is 0, then we have a parity bit error
if( checksum == 0 )
{
retVal = new int[ 1 ];
retVal[ 0 ] = 63;
}
//Otherwise, try to lookup the syndrome for a single bit error
else
{
for( int x = 0; x < 63; x++ )
{
if( checksum == sCHECKSUMS[ x ] )
{
//return this bit position
retVal = new int[ 1 ];
retVal[ 0 ] = x;
}
}
}
return retVal;
}
}

View File

@@ -0,0 +1,193 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.BitSet;
/**
* 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 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;
}
}
return null;
}
public static void main( String[] args )
{
BinaryMessage msg = BinaryMessage.load( "01010101101010101010000100011000000000100001000110000000001000010001100000000010" );
CRC crc = checkAndCorrect( msg );
mLog.debug( "CRC:" + crc.getDisplayText() );
}
}

View File

@@ -0,0 +1,185 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
import io.github.dsheirer.message.MessageDirection;
import java.util.BitSet;
/**
* LTR CRC checksum utility
*
* LTR message blocks are 41 bits in length as follows:
* 0 - 9: Sync
* 10 - 34: message bits
* 35 - 41: CRC-7 check bits
*
* LTR uses a CRC-7 (0xFD) with an initial fill of 0x00.
*
* CRC-7 Generating Polynomial: x7 + x6 + x5 + x4 + x3 + x2 + 1 (0xFD)
*/
public class CRCLTR
{
private static byte[] sCHECKSUMS = new byte[]
{
0x38, //Area
0x1C, //Channel 4
0x0E, //Channel 3
0x46, //Channel 2
0x23, //Channel 1
0x51, //Channel 0
0x68, //Home 4
0x75, //Home 3
0x7A, //Home 2
0x3D, //Home 1
0x1F, //Home 0
0x4F, //Group 7
0x26, //Group 6
0x52, //Group 5
0x29, //Group 4
0x15, //Group 3
0x0B, //Group 2
0x45, //Group 1
0x62, //Group 0
0x31, //Free 4
0x19, //Free 3
0x0D, //Free 2
0x07, //Free 1
0x43 //Free 0
};
/**
* Determines if message bits 10 - 34 pass the LTR CRC checksum
* contained in bits 35 - 41, using a lookup table of CRC checksum values
* derived from the CRC-7 value.
*/
public static CRC check( BitSet msg, MessageDirection direction )
{
CRC crc = CRC.UNKNOWN;
int calculated = getCalculatedChecksum( msg );
int transmitted = getTransmittedChecksum( msg );
/* Normal CRC only for Outbound Status Word */
if( direction == MessageDirection.OSW )
{
if( calculated == transmitted )
{
return CRC.PASSED;
}
}
/* Normal or Inverted CRC for Inbound Status Word */
else
{
if( ( calculated ^ 127 ) == transmitted )
{
return CRC.PASSED_INV;
}
else if( calculated == transmitted )
{
return CRC.PASSED;
}
}
return CRC.FAILED_CRC;
}
public static byte[] getChecks()
{
return sCHECKSUMS;
}
public static int getCalculatedChecksum( BitSet msg )
{
int calculated = 0;
//Iterate bits that are set and XOR running checksum with lookup value
for (int i = msg.nextSetBit( 9 ); i >= 9 && i < 33; i = msg.nextSetBit( i+1 ) )
{
calculated ^= sCHECKSUMS[ i - 9 ];
}
return calculated;
}
/**
* Returns the integer value of the 7 bit crc checksum
*/
public static int getTransmittedChecksum( BitSet msg )
{
int retVal = 0;
for( int x = 0; x < 7; x++ )
{
if( msg.get( x + 33 ) )
{
retVal += 1<<( 6 - x );
}
}
return retVal;
}
/**
* Determines the errant bit positions and returns them in an array of
* integer bit positions.
*
* Note: currently only detects single-bit errors
*
* @param msg to be checked for errors
* @return - array of integer positions of bits that need flipped
*/
public static int[] findBitErrors( BitSet msg )
{
int[] retVal = null;
int checksum = getTransmittedChecksum( msg );
//Remove the initial fill value (1)
checksum ^= 1;
//Iterate set message bits, removing their respective checksum value
//from the transmitted checksum, to arrive at the remainder
for (int i = msg.nextSetBit( 0 ); i >= 0 && i < 47; i = msg.nextSetBit( i+1 ) )
{
checksum ^= sCHECKSUMS[ i ];
}
//If at this point the checksum is 0, then the errant bit is the parity
//bit
if( checksum == 0 )
{
msg.flip( 62 );
}
//Otherwise, try to lookup the syndrome for a single bit error
else
{
for( int x = 0; x < 47; x++ )
{
if( checksum == sCHECKSUMS[ x ] )
{
//return this bit position
retVal = new int[ 1 ];
retVal[ 0 ] = x;
}
}
}
return retVal;
}
}

View File

@@ -0,0 +1,470 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* P25 CRC check/correction methods
*/
public class CRCP25
{
private final static Logger mLog = LoggerFactory.getLogger( CRCP25.class );
/**
* CRC-CCITT 16-bit checksums for a message length of 80 bits plus 16
* additional checksums representing CRC checksum bit errors
*
* Generated by:
* CRCUtil.generate( 80, 16, 0x11021l, 0xFFFFl, true );
*/
public static final int[] CCITT_80_CHECKSUMS = new int[]
{
0x1BCB, 0x8DE5, 0xC6F2, 0x6B69, 0xB5B4, 0x52CA, 0x2175, 0x90BA, 0x404D,
0xA026, 0x5803, 0xAC01, 0xD600, 0x6310, 0x3998, 0x14DC, 0x27E, 0x92F,
0x8497, 0xC24B, 0xE125, 0xF092, 0x7059, 0xB82C, 0x5406, 0x2213, 0x9109,
0xC884, 0x6C52, 0x3E39, 0x9F1C, 0x479E, 0x2BDF, 0x95EF, 0xCAF7, 0xE57B,
0xF2BD, 0xF95E, 0x74BF, 0xBA5F, 0xDD2F, 0xEE97, 0xF74B, 0xFBA5, 0xFDD2,
0x76F9, 0xBB7C, 0x55AE, 0x22C7, 0x9163, 0xC8B1, 0xE458, 0x7A3C, 0x350E,
0x1297, 0x894B, 0xC4A5, 0xE252, 0x7939, 0xBC9C, 0x565E, 0x233F, 0x919F,
0xC8CF, 0xE467, 0xF233, 0xF919, 0xFC8C, 0x7656, 0x333B, 0x999D, 0xCCCE,
0x6E77, 0xB73B, 0xDB9D, 0xEDCE, 0x7EF7, 0xBF7B, 0xDFBD, 0xEFDE, 0x0001,
0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200,
0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000
};
/**
* Confirmed Packet Data Unit CRC-9 checksums, generated by:
*
* long[] table = generate( 135, 9, 0x259l, 0x1FF, false, Parity.NONE );
*/
public static final int[] CRC9_CHECKSUMS = new int[]
{
0x1E7, 0x1F3, 0x1F9, 0x1FC, 0x0D2, 0x045, 0x122, 0x0BD, 0x15E, 0x083,
0x141, 0x1A0, 0x0FC, 0x052, 0x005, 0x102, 0x0AD, 0x156, 0x087, 0x143,
0x1A1, 0x1D0, 0x0C4, 0x04E, 0x00B, 0x105, 0x182, 0x0ED, 0x176, 0x097,
0x14B, 0x1A5, 0x1D2, 0x0C5, 0x162, 0x09D, 0x14E, 0x08B, 0x145, 0x1A2,
0x0FD, 0x17E, 0x093, 0x149, 0x1A4, 0x0FE, 0x053, 0x129, 0x194, 0x0E6,
0x05F, 0x12F, 0x197, 0x1CB, 0x1E5, 0x1F2, 0x0D5, 0x16A, 0x099, 0x14C,
0x08A, 0x069, 0x134, 0x0B6, 0x077, 0x13B, 0x19D, 0x1CE, 0x0CB, 0x165,
0x1B2, 0x0F5, 0x17A, 0x091, 0x148, 0x088, 0x068, 0x018, 0x020, 0x03C,
0x302, 0x035, 0x11A, 0x0A1, 0x150, 0x084, 0x06E, 0x01B, 0x10D, 0x186,
0x0EF, 0x177, 0x1BB, 0x1DD, 0x1EE, 0x0DB, 0x16D, 0x1B6, 0x0F7, 0x17B,
0x1BD, 0x1DE, 0x0C3, 0x161, 0x1B0, 0x0F4, 0x056, 0x007, 0x103, 0x181,
0x1C0, 0x0CC, 0x04A, 0x009, 0x104, 0x0AE, 0x07B, 0x13D, 0x19E, 0x0E3,
0x171, 0x1B8, 0x0F0, 0x054, 0x006, 0x02F, 0x117, 0x18B, 0x1C5, 0x1E2,
0x0DD, 0x16E, 0x09B, 0x14D, 0x1A6
};
/**
* CRC-32 checksums for PDU1 ( HEADER + 1 Block ) messages, generated by:
*
* CRCUtil.generate( 64, 32, 0x104C11DB7l, 0xFFFFFFFFl, true );
*/
public static final long[] PDU1_CHECKSUMS = new long[]
{
0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl, 0x52275834l, 0x2B7322C1l,
0x95B99160l, 0x48BC466Bl, 0xA45E2335l, 0xD22F119Al, 0x6B770616l,
0x37DB0DD0l, 0x198D0833l, 0x8CC68419l, 0xC663420Cl, 0x61512FDDl,
0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl, 0x97BD7626l, 0x49BE35C8l,
0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl, 0xE4D7F287l, 0xF26BF943l,
0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l, 0xBE16F8F9l, 0xDF0B7C7Cl,
0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l, 0x2EEC6FAAl, 0x1516B90El,
0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl, 0x43E5D726l, 0x23926548l,
0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl, 0xE275378Fl, 0xF13A9BC7l,
0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l, 0x7D732767l, 0xBEB993B3l,
0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl, 0xBADBDE56l, 0x5F0D61F0l,
0x2DE63E23l, 0x96F31F11l, 0xCB798F88l, 0x67DC491Fl, 0xB3EE248Fl,
0xD9F71247l, 0xECFB8923l, 0xF67DC491l, 0xFB3EE248l, 0x00000001l,
0x00000002l, 0x00000004l, 0x00000008l, 0x00000010l, 0x00000020l,
0x00000040l, 0x00000080l, 0x00000100l, 0x00000200l, 0x00000400l,
0x00000800l, 0x00001000l, 0x00002000l, 0x00004000l, 0x00008000l,
0x00010000l, 0x00020000l, 0x00040000l, 0x00080000l, 0x00100000l,
0x00200000l, 0x00400000l, 0x00800000l, 0x01000000l, 0x02000000l,
0x04000000l, 0x08000000l, 0x10000000l, 0x20000000l, 0x40000000l,
0x80000000l
};
/**
* CRC-32 checksums for PDU2 ( HEADER + 2 Blocks ) messages, generated by:
*
* CRCUtil.generate( 160, 32, 0x104C11DB7l, 0xFFFFFFFFl, true );
*/
public static final long[] PDU2_CHECKSUMS = new long[]
{
0x9D231959l, 0xCE918CACl, 0x6528488Dl, 0xB2942446l, 0x5B2A9CF8l,
0x2FF5C0A7l, 0x97FAE053l, 0xCBFD7029l, 0xE5FEB814l, 0x709FD2D1l,
0xB84FE968l, 0x5E477A6Fl, 0xAF23BD37l, 0xD791DE9Bl, 0xEBC8EF4Dl,
0xF5E477A6l, 0x7892B508l, 0x3E29D45Fl, 0x9F14EA2Fl, 0xCF8A7517l,
0xE7C53A8Bl, 0xF3E29D45l, 0xF9F14EA2l, 0x7E98298Al, 0x3D2C9A1El,
0x1CF6C3D4l, 0x0C1BEF31l, 0x860DF798l, 0x41667517l, 0xA0B33A8Bl,
0xD0599D45l, 0xE82CCEA2l, 0x7676E98Al, 0x395BFA1El, 0x1ECD73D4l,
0x0D063731l, 0x86831B98l, 0x41210317l, 0xA090818Bl, 0xD04840C5l,
0xE8242062l, 0x76729EEAl, 0x3959C1AEl, 0x1ECC6E0Cl, 0x0D06B9DDl,
0x86835CEEl, 0x412120ACl, 0x22F01E8Dl, 0x91780F46l, 0x4ADC8978l,
0x270ECA67l, 0x93876533l, 0xC9C3B299l, 0xE4E1D94Cl, 0x7010627Dl,
0xB808313El, 0x5E649644l, 0x2D52C5F9l, 0x96A962FCl, 0x49343FA5l,
0xA49A1FD2l, 0x502D8132l, 0x2A764E42l, 0x175BA9FAl, 0x09CD5A26l,
0x068623C8l, 0x01239F3Fl, 0x8091CF9Fl, 0xC048E7CFl, 0xE02473E7l,
0xF01239F3l, 0xF8091CF9l, 0xFC048E7Cl, 0x7C62C9E5l, 0xBE3164F2l,
0x5D783CA2l, 0x2CDC908Al, 0x140EC69El, 0x0867ED94l, 0x06537811l,
0x8329BC08l, 0x43F450DFl, 0xA1FA286Fl, 0xD0FD1437l, 0xE87E8A1Bl,
0xF43F450Dl, 0xFA1FA286l, 0x7F6F5F98l, 0x3DD72117l, 0x9EEB908Bl,
0xCF75C845l, 0xE7BAE422l, 0x71BDFCCAl, 0x3ABE70BEl, 0x1F3FB684l,
0x0DFF5599l, 0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl, 0x52275834l,
0x2B7322C1l, 0x95B99160l, 0x48BC466Bl, 0xA45E2335l, 0xD22F119Al,
0x6B770616l, 0x37DB0DD0l, 0x198D0833l, 0x8CC68419l, 0xC663420Cl,
0x61512FDDl, 0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl, 0x97BD7626l,
0x49BE35C8l, 0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl, 0xE4D7F287l,
0xF26BF943l, 0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l, 0xBE16F8F9l,
0xDF0B7C7Cl, 0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l, 0x2EEC6FAAl,
0x1516B90El, 0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl, 0x43E5D726l,
0x23926548l, 0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl, 0xE275378Fl,
0xF13A9BC7l, 0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l, 0x7D732767l,
0xBEB993B3l, 0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl, 0xBADBDE56l,
0x5F0D61F0l, 0x2DE63E23l, 0x96F31F11l, 0xCB798F88l, 0x67DC491Fl,
0xB3EE248Fl, 0xD9F71247l, 0xECFB8923l, 0xF67DC491l, 0xFB3EE248l,
0x00000001l, 0x00000002l, 0x00000004l, 0x00000008l, 0x00000010l,
0x00000020l, 0x00000040l, 0x00000080l, 0x00000100l, 0x00000200l,
0x00000400l, 0x00000800l, 0x00001000l, 0x00002000l, 0x00004000l,
0x00008000l, 0x00010000l, 0x00020000l, 0x00040000l, 0x00080000l,
0x00100000l, 0x00200000l, 0x00400000l, 0x00800000l, 0x01000000l,
0x02000000l, 0x04000000l, 0x08000000l, 0x10000000l, 0x20000000l,
0x40000000l, 0x80000000l
};
/**
* CRC-32 checksums for PDU3 ( HEADER + 3 Blocks ) messages, generated by:
*
* CRCUtil.generate( 256, 32, 0x104C11DB7l, 0xFFFFFFFFl, true );
*/
public static final long[] PDU3_CHECKSUMS = new long[]
{
0xAA5FA470l, 0x574F5CE3l, 0xABA7AE71l, 0xD5D3D738l, 0x68896547l,
0xB444B2A3l, 0xDA225951l, 0xED112CA8l, 0x74E8188Fl, 0xBA740C47l,
0xDD3A0623l, 0xEE9D0311l, 0xF74E8188l, 0x79C7CE1Fl, 0xBCE3E70Fl,
0xDE71F387l, 0xEF38F9C3l, 0xF79C7CE1l, 0xFBCE3E70l, 0x7F8791E3l,
0xBFC3C8F1l, 0xDFE1E478l, 0x6D907CE7l, 0xB6C83E73l, 0xDB641F39l,
0xEDB20F9Cl, 0x74B98915l, 0xBA5CC48Al, 0x5F4EEC9El, 0x2DC7F894l,
0x14837291l, 0x8A41B948l, 0x4740527Fl, 0xA3A0293Fl, 0xD1D0149Fl,
0xE8E80A4Fl, 0xF4740527l, 0xFA3A0293l, 0xFD1D0149l, 0xFE8E80A4l,
0x7D27CE89l, 0xBE93E744l, 0x5D297D79l, 0xAE94BEBCl, 0x552AD185l,
0xAA9568C2l, 0x572A3ABAl, 0x29F59386l, 0x169A4718l, 0x092DAD57l,
0x8496D6ABl, 0xC24B6B55l, 0xE125B5AAl, 0x72F2540El, 0x3B19A4DCl,
0x1FEC5CB5l, 0x8FF62E5Al, 0x459B99F6l, 0x20AD4220l, 0x12362FCBl,
0x891B17E5l, 0xC48D8BF2l, 0x60264B22l, 0x3273AB4Al, 0x1B595B7El,
0x0FCC2364l, 0x05869F69l, 0x82C34FB4l, 0x43012901l, 0xA1809480l,
0x52A0C49Bl, 0xA950624Dl, 0xD4A83126l, 0x68349648l, 0x367AC5FFl,
0x9B3D62FFl, 0xCD9EB17Fl, 0xE6CF58BFl, 0xF367AC5Fl, 0xF9B3D62Fl,
0xFCD9EB17l, 0xFE6CF58Bl, 0xFF367AC5l, 0xFF9B3D62l, 0x7DAD106Al,
0x3CB606EEl, 0x1C3B8DACl, 0x0C7D480Dl, 0x863EA406l, 0x417FDCD8l,
0x22DF60B7l, 0x916FB05Bl, 0xC8B7D82Dl, 0xE45BEC16l, 0x704D78D0l,
0x3A4632B3l, 0x9D231959l, 0xCE918CACl, 0x6528488Dl, 0xB2942446l,
0x5B2A9CF8l, 0x2FF5C0A7l, 0x97FAE053l, 0xCBFD7029l, 0xE5FEB814l,
0x709FD2D1l, 0xB84FE968l, 0x5E477A6Fl, 0xAF23BD37l, 0xD791DE9Bl,
0xEBC8EF4Dl, 0xF5E477A6l, 0x7892B508l, 0x3E29D45Fl, 0x9F14EA2Fl,
0xCF8A7517l, 0xE7C53A8Bl, 0xF3E29D45l, 0xF9F14EA2l, 0x7E98298Al,
0x3D2C9A1El, 0x1CF6C3D4l, 0x0C1BEF31l, 0x860DF798l, 0x41667517l,
0xA0B33A8Bl, 0xD0599D45l, 0xE82CCEA2l, 0x7676E98Al, 0x395BFA1El,
0x1ECD73D4l, 0x0D063731l, 0x86831B98l, 0x41210317l, 0xA090818Bl,
0xD04840C5l, 0xE8242062l, 0x76729EEAl, 0x3959C1AEl, 0x1ECC6E0Cl,
0x0D06B9DDl, 0x86835CEEl, 0x412120ACl, 0x22F01E8Dl, 0x91780F46l,
0x4ADC8978l, 0x270ECA67l, 0x93876533l, 0xC9C3B299l, 0xE4E1D94Cl,
0x7010627Dl, 0xB808313El, 0x5E649644l, 0x2D52C5F9l, 0x96A962FCl,
0x49343FA5l, 0xA49A1FD2l, 0x502D8132l, 0x2A764E42l, 0x175BA9FAl,
0x09CD5A26l, 0x068623C8l, 0x01239F3Fl, 0x8091CF9Fl, 0xC048E7CFl,
0xE02473E7l, 0xF01239F3l, 0xF8091CF9l, 0xFC048E7Cl, 0x7C62C9E5l,
0xBE3164F2l, 0x5D783CA2l, 0x2CDC908Al, 0x140EC69El, 0x0867ED94l,
0x06537811l, 0x8329BC08l, 0x43F450DFl, 0xA1FA286Fl, 0xD0FD1437l,
0xE87E8A1Bl, 0xF43F450Dl, 0xFA1FA286l, 0x7F6F5F98l, 0x3DD72117l,
0x9EEB908Bl, 0xCF75C845l, 0xE7BAE422l, 0x71BDFCCAl, 0x3ABE70BEl,
0x1F3FB684l, 0x0DFF5599l, 0x86FFAACCl, 0x411F5BBDl, 0xA08FADDEl,
0x52275834l, 0x2B7322C1l, 0x95B99160l, 0x48BC466Bl, 0xA45E2335l,
0xD22F119Al, 0x6B770616l, 0x37DB0DD0l, 0x198D0833l, 0x8CC68419l,
0xC663420Cl, 0x61512FDDl, 0xB0A897EEl, 0x5A34C52Cl, 0x2F7AEC4Dl,
0x97BD7626l, 0x49BE35C8l, 0x26BF943Fl, 0x935FCA1Fl, 0xC9AFE50Fl,
0xE4D7F287l, 0xF26BF943l, 0xF935FCA1l, 0xFC9AFE50l, 0x7C2DF1F3l,
0xBE16F8F9l, 0xDF0B7C7Cl, 0x6DE530E5l, 0xB6F29872l, 0x5919C2E2l,
0x2EEC6FAAl, 0x1516B90El, 0x08EBD25Cl, 0x061567F5l, 0x830AB3FAl,
0x43E5D726l, 0x23926548l, 0x13A9BC7Fl, 0x89D4DE3Fl, 0xC4EA6F1Fl,
0xE275378Fl, 0xF13A9BC7l, 0xF89D4DE3l, 0xFC4EA6F1l, 0xFE275378l,
0x7D732767l, 0xBEB993B3l, 0xDF5CC9D9l, 0xEFAE64ECl, 0x75B7BCADl,
0xBADBDE56l, 0x5F0D61F0l, 0x2DE63E23l, 0x96F31F11l, 0xCB798F88l,
0x67DC491Fl, 0xB3EE248Fl, 0xD9F71247l, 0xECFB8923l, 0xF67DC491l,
0xFB3EE248l, 0x00000001l, 0x00000002l, 0x00000004l, 0x00000008l,
0x00000010l, 0x00000020l, 0x00000040l, 0x00000080l, 0x00000100l,
0x00000200l, 0x00000400l, 0x00000800l, 0x00001000l, 0x00002000l,
0x00004000l, 0x00008000l, 0x00010000l, 0x00020000l, 0x00040000l,
0x00080000l, 0x00100000l, 0x00200000l, 0x00400000l, 0x00800000l,
0x01000000l, 0x02000000l, 0x04000000l, 0x08000000l, 0x10000000l,
0x20000000l, 0x40000000l, 0x80000000l
};
/**
* Performs error detection and single-bit error correction against the
* data blocks of a PDU1 message.
*/
public static BinaryMessage correctPDU1( BinaryMessage message )
{
return correctPDU( message, PDU1_CHECKSUMS, 224 );
}
/**
* Performs error detection and single-bit error correction against the
* data blocks of a PDU2 message.
*/
public static BinaryMessage correctPDU2( BinaryMessage message )
{
return correctPDU( message, PDU2_CHECKSUMS, 320 );
}
/**
* Performs error detection and single-bit error correction against the
* data blocks of a PDU3 message.
*/
public static BinaryMessage correctPDU3( BinaryMessage message )
{
return correctPDU( message, PDU3_CHECKSUMS, 416 );
}
public static BinaryMessage correctPDU( BinaryMessage message, long[] checksums, int crcStart )
{
long calculated = 0; //Starting value
int messageStart = 160;
/* Iterate the set bits and XOR running checksum with lookup value */
for (int i = message.nextSetBit( messageStart );
i >= messageStart && i < crcStart;
i = message.nextSetBit( i+1 ) )
{
calculated ^= checksums[ i - messageStart ];
}
long checksum = getLongChecksum( message, crcStart, 32 );
long error = calculated ^ checksum;
if( error == 0 || error == 0xFFFFFFFFl )
{
message.setCRC( CRC.PASSED );
return message;
}
else
{
int errorLocation = getBitError( error, checksums );
if( errorLocation >= 0 )
{
message.flip( errorLocation + messageStart );
message.setCRC( CRC.CORRECTED );
return message;
}
}
message.setCRC( CRC.FAILED_CRC );
return message;
}
/**
* Error detection and correction of single-bit errors for CCITT 16-bit
* CRC protected 80-bit messages.
*/
public static BinaryMessage correctCCITT80( BinaryMessage message,
int messageStart,
int crcStart )
{
int calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for (int i = message.nextSetBit( messageStart );
i >= messageStart && i < crcStart;
i = message.nextSetBit( i+1 ) )
{
calculated ^= CCITT_80_CHECKSUMS[ i - messageStart ];
}
int checksum = getIntChecksum( message, crcStart, 16 );
int residual = calculated ^ checksum;
if( residual == 0 || residual == 0xFFFF )
{
message.setCRC( CRC.PASSED );
return message;
}
else
{
int errorLocation = getBitError( residual, CCITT_80_CHECKSUMS );
if( errorLocation >= 0 )
{
message.flip( errorLocation + messageStart );
message.setCRC( CRC.CORRECTED );
return message;
}
}
message.setCRC( CRC.FAILED_CRC );
return message;
}
/**
* Error detection for CRC-9 protected Confirmed Packet Data blocks. These
* data blocks have a slightly complicated structure because the checksum
* is located between bits 7-15, within a 144 bit block. The checksums
* were generated assuming that the message is contiguous from 0 - 134 bits.
* No data correction is performed.
*/
public static CRC checkCRC9( BinaryMessage message, int messageStart )
{
int calculated = 0x0; //Initial fill of all ones
/* Iterate the set bits and XOR running checksum with lookup value */
for (int i = message.nextSetBit( messageStart );
i >= messageStart && i < messageStart + 144;
i = message.nextSetBit( i+1 ) )
{
/* message bits before the CRC */
if( i < ( messageStart + 7 ) )
{
calculated ^= CRC9_CHECKSUMS[ i - messageStart ];
}
/* message bits after the CRC */
else if( i > ( messageStart + 15 ) )
{
calculated ^= CRC9_CHECKSUMS[ i - messageStart - 9 ];
}
}
int checksum = message.getInt( messageStart + 7, messageStart + 15 );
int residual = calculated ^ checksum;
// mLog.debug( "CALC:" + calculated + " CHECK:" + checksum + " RESID:" + residual );
if( residual == 0 || residual == 0x1FF )
{
return CRC.PASSED;
}
return CRC.FAILED_CRC;
}
/**
* Performs Galois 24/12/7 error detection and correction against the 12
* encoded 24-bit message segments following the 64-bit NID in the message
*
* @return - true if all 12 segments of the message can be checked/corrected
*/
public static boolean correctGalois24( BinaryMessage tdulc )
{
boolean passes = true;
int x = 64;
while( x < tdulc.size() && passes )
{
tdulc = Golay24.checkAndCorrect( tdulc, x );
passes = tdulc.getCRC() == CRC.PASSED;
x += 24;
}
return passes;
}
/**
* Calculates the value of the message checksum as a long
*/
public static long getLongChecksum( BinaryMessage message,
int crcStart, int crcLength )
{
return message.getLong( crcStart, crcStart + crcLength - 1 );
}
/**
* Calculates the value of the message checksum as an integer
*/
public static int getIntChecksum( BinaryMessage message,
int crcStart, int crcLength )
{
return message.getInt( crcStart, crcStart + crcLength - 1 );
}
/**
* Identifies any single bit error position that matches the checksum error.
*/
public static int getBitError( long checksumError, long[] checksums )
{
for( int x = 0; x < checksums.length; x++ )
{
if( checksums[ x ] == checksumError )
{
return x;
}
}
return -1;
}
/**
* Identifies any single bit error position that matches the checksum error.
*/
public static int getBitError( int checksumError, int[] checksums )
{
for( int x = 0; x < checksums.length; x++ )
{
if( checksums[ x ] == checksumError )
{
return x;
}
}
return -1;
}
public static void main( String[] args )
{
String raw = "000000001000001100000001010001111011000100001010010001111100000000000101000000000000000001000000000000110000000000000001101010101010101010101010";
BinaryMessage message = BinaryMessage.load( raw );
mLog.debug( "MSG:" + message.toString() );
CRC results = checkCRC9( message, 0 );
mLog.debug( "COR:" + message.toString() );
mLog.debug( "Results: " + results.getDisplayText() );
}
}

View File

@@ -0,0 +1,215 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import java.util.BitSet;
/**
* Passport CRC checksum utility
*
* Passport message blocks are 68 bits in length as follows:
* 0 - 8: Sync
* 9 - 59: message bits
* 60 - 67: CRC-7 check bits plus 1 parity
*
* Passport uses a CRC-7 (0x44) with an initial fill of 0x44 plus parity set.
*
* CRC-7 Generating Polynomial: x6 + x2 + 1 (0x44)
*/
public class CRCPassport
{
private static final byte sFILL_00 = (byte)0x00;
private static byte[] sCHECKSUMS = new byte[]
{
(byte)0x6E, //DCC 1
(byte)0xBF, //DCC 0
(byte)0xD6, //LCN 10
(byte)0xE3, //LCN 9
(byte)0xF8, //LCN 8
(byte)0x7C, //LCN 7
(byte)0x3E, //LCN 6
(byte)0x97, //LCN 5
(byte)0xC2, //LCN 4
(byte)0xE9, //LCN 3
(byte)0x75, //LCN 2
(byte)0x3B, //LCN 1
(byte)0x94, //LCN 0
(byte)0x4A, //SITE 6
(byte)0xAD, //SITE 5
(byte)0x57, //SITE 4
(byte)0xA2, //SITE 3
(byte)0xD9, //SITE 2
(byte)0x6D, //SITE 1
(byte)0x37, //SITE 0
(byte)0x92, //GROUP 15
(byte)0xC1, //GROUP 14
(byte)0x61, //GROUP 13
(byte)0x31, //GROUP 12
(byte)0x19, //GROUP 11
(byte)0x0D, //GROUP 10
(byte)0x07, //GROUP 9
(byte)0x8A, //GROUP 8
(byte)0xCD, //GROUP 7
(byte)0x67, //GROUP 6
(byte)0xBA, //GROUP 5
(byte)0xD5, //GROUP 4
(byte)0x6B, //GROUP 3
(byte)0xBC, //GROUP 2
(byte)0x5E, //GROUP 1
(byte)0xA7, //GROUP 0
(byte)0xDA, //TYPE 3
(byte)0xE5, //TYPE 2
(byte)0x73, //TYPE 1
(byte)0xB0, //TYPE 0
(byte)0x58, //FREE 10
(byte)0x2C, //FREE 9
(byte)0x16, //FREE 8
(byte)0x83, //FREE 7
(byte)0xC8, //FREE 6
(byte)0x64, //FREE 5
(byte)0x32, //FREE 4
(byte)0x91, //FREE 3
(byte)0x49, //FREE 2
(byte)0x25, //FREE 1
(byte)0x13 //FREE 0
};
/**
* Determines if message bits 9 - 59 pass the CRC checksum
* contained in bits 60 - 68, using a lookup table of CRC checksum values
* derived from the CRC-7 value and the final parity bit
*/
public static CRC check( BitSet msg )
{
CRC crc = CRC.UNKNOWN;
byte calculated = 0x0; //Starting value for an OSW
//Iterate bits that are set and XOR running checksum with lookup value
for (int i = msg.nextSetBit( 9 ); i >= 9 && i <= 59; i = msg.nextSetBit( i+1 ) )
{
calculated ^= sCHECKSUMS[ i - 9 ];
}
//Apply the message checksum to derive the residual
calculated ^= getChecksum( msg );
switch( (byte)calculated )
{
case sFILL_00:
crc = CRC.PASSED;
break;
default:
crc = CRC.FAILED_CRC;
}
return crc;
}
public static byte getResidual( BitSet msg )
{
byte calculated = 0x0; //Initial fill of zero
//Iterate bits that are set and XOR running checksum with lookup value
for (int i = msg.nextSetBit( 9 ); i >= 9 && i <= 59; i = msg.nextSetBit( i+1 ) )
{
calculated ^= sCHECKSUMS[ i - 9 ];
}
calculated ^= getChecksum( msg );
return calculated;
}
public static byte[] getChecks()
{
return sCHECKSUMS;
}
/**
* Returns the integer value of the 7 bit crc checksum
*/
public static byte getChecksum( BitSet msg )
{
byte retVal = 0x0;
for( int x = 0; x < 8; x++ )
{
if( msg.get( x + 60 ) )
{
retVal ^= (byte)( 1<<( 7 - x ) );
}
}
return retVal;
}
/**
* Initial take on error correction. The majority of the errors detected
* are in the final 2 bits, owing to the forward looking soft bit detection
* used in the decoder. For right now, correct any messages where the
* residual indicates that the final 1 or 2 bits are bad.
*/
public static BinaryMessage correct( BinaryMessage msg )
{
int residual = (int)( 0xFF & getResidual( msg ) );
switch( residual )
{
case 1:
case 136:
msg.flip( 67 );
break;
case 3:
case 138:
msg.flip( 66 );
msg.flip( 67 );
break;
case 2:
case 139:
msg.flip( 66 );
break;
case 140:
msg.flip( 65 );
msg.flip( 67 );
break;
case 153:
msg.flip( 67 );
break;
case 142:
msg.flip( 65 );
msg.flip( 66 );
msg.flip( 67 );
break;
}
return msg;
}
/**
* Performs an even parity check on the bitset. If the number of bits that
* are set is an even number, then it passes the even parity check
*/
public static boolean isEvenParity( BinaryMessage bits )
{
return ( bits.cardinality() % 2 == 0 );
}
}

View File

@@ -0,0 +1,239 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CRCUtil
{
private final static Logger mLog = LoggerFactory.getLogger( CRCUtil.class );
public static long[] generate( int messageSize,
int crcSize,
long polynomial,
long initialFill,
boolean includeCRCBitErrors )
{
long[] crcTable = new long[ messageSize +
( includeCRCBitErrors ? crcSize : 0 ) ];
int[] checksumIndexes = new int[ crcSize ];
for( int x = 0; x < crcSize; x++ )
{
checksumIndexes[ x ] = messageSize + x;
}
for( int x = 0; x < messageSize; x++ )
{
BinaryMessage message = new BinaryMessage( messageSize + crcSize );
message.load( messageSize, crcSize, initialFill );
message.set( x );
message = decode( message, 0, messageSize, polynomial, crcSize );
long checksum = message.getLong( checksumIndexes );
crcTable[ x ] = checksum;
}
if( includeCRCBitErrors )
{
for( int x = 0; x < crcSize; x++ )
{
crcTable[ messageSize + x ] = Long.rotateLeft( 1, x );
}
}
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
* in index 0 to index messageLength - 1, leaving the results in the
* checksum filed following the message.
*
* IN: MMMMMMMMMMMMMMMMCCCCCCCCCCC
* OUT: 0000000000000000RRRRRRRRRRR
*
* M: Message
* C: Checksum
* R: Remainder
*
* @param message - message and crc
* @param polynomial - crc polynomial - must be 1 bit longer than the crc width
* @param messageSize - message length and start of the checksum
*
* @return message with all message bits zeroed out, and the remainder
* placed in the crc field which starts at index messageLength
*/
public static BinaryMessage decode( BinaryMessage message,
int messageStart,
int messageSize,
long polynomial,
int crcSize )
{
for (int i = message.nextSetBit( messageStart );
i >= messageStart && i < messageSize;
i = message.nextSetBit( i+1 ) )
{
BinaryMessage polySet = new BinaryMessage( crcSize + i + 1 );
polySet.load( i, crcSize + 1, polynomial );
message.xor( polySet );
}
return message;
}
public static void main( String[] args )
{
mLog.debug( "Starting" );
// String raw = "000000001010001000000000100010100000000000000000010000000011001001011100000000000000000011111110001000101001011100101110000101000110011101101001";
// BitSetBuffer message = BitSetBuffer.load( raw );
//
// long polynomial = 0x259l;
//
// decode( message, 0, 135, polynomial, 9 );
//
// mLog.debug( message.toString() );
long[] table = generate( 135, 9, 0x259l, 0, false, Parity.NONE );
mLog.debug( toCodeArray( table ) );
mLog.debug( "Finished" );
}
public static String toCodeArray( long[] values )
{
boolean integerArray = true;
/* Determine the correct primitive type: int or long */
for( long value: values )
{
if( value > Integer.MAX_VALUE || value < Integer.MIN_VALUE )
{
integerArray = false;
break;
}
}
StringBuilder sb = new StringBuilder();
if( integerArray )
{
sb.append( "\npublic static final int[] CHECKSUMS = new int[]\n" );
}
else
{
sb.append( "\npublic static final long[] CHECKSUMS = new long[]\n" );
}
sb.append( "{\n" );
StringBuilder row = new StringBuilder();
row.append( " " );
for( long value: values )
{
StringBuilder element = new StringBuilder();
element.append( "0x" );
element.append( Long.toHexString( value ).toUpperCase() );
if( !integerArray )
{
element.append( "l" );
}
element.append( ", " );
if( row.length() + element.length() <= 80 )
{
row.append( element.toString() );
}
else
{
sb.append( row.toString() );
sb.append( "\n" );
row = new StringBuilder();
row.append( " " );
row.append( element.toString() );
}
}
if( row.length() > 4 )
{
sb.append( row.toString() );
sb.append( "\n" );
}
sb.append( "};\n" );
return sb.toString();
}
}

View File

@@ -0,0 +1,52 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
import java.util.BitSet;
/**
* Calculates a 7-bit checksum using an array of xor values from the
* ChecksumType class
*/
public class Checksum
{
public static boolean validate7BitChecksum( BitSet message,
byte messageChecksum,
ChecksumType checksumType )
{
return ( messageChecksum & get7BitChecksum( message, checksumType ) ) == 0;
}
public static byte get7BitChecksum( BitSet message,
ChecksumType checksumType )
{
byte checksum = 0x00;
short[] checkValues = checksumType.getValues();
for( int x = 0; x < checkValues.length; x++ )
{
if( message.get( x ) )
{
checksum ^= checkValues[ x ];
}
}
return checksum;
}
}

View File

@@ -0,0 +1,96 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
import java.util.BitSet;
public class ChecksumTest
{
/**
* @param args
*/
public static void main( String[] args )
{
log( "START!" );
String msg = "111000001000010010001110";
log( msg + " check:" + String.format( "%02X", Checksum.get7BitChecksum( load( msg ), ChecksumType.LTR ) ) );
log( "DONE!" );
}
public static void log( String message )
{
System.out.println( message );
}
public static String getString( BitSet bitset, int length )
{
StringBuilder sb = new StringBuilder();
for( int x = 0; x < length; x++ )
{
if( bitset.get( x ) )
{
sb.append( "1" );
}
else
{
sb.append( "0" );
}
}
return sb.toString();
}
public static String getStringReverse( BitSet bitset, int length )
{
StringBuilder sb = new StringBuilder();
for( int x = length - 1; x > -1; x-- )
{
if( bitset.get( x ) )
{
sb.append( "1" );
}
else
{
sb.append( "0" );
}
}
return sb.toString();
}
public static BitSet load( String message )
{
BitSet retVal = new BitSet();
for( int x = 0; x < message.length(); x++ )
{
if( message.substring( x, x + 1 ).contentEquals( "1" ) )
{
retVal.set( x );
}
}
return retVal;
}
}

View File

@@ -0,0 +1,223 @@
/*******************************************************************************
* 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 io.github.dsheirer.edac;
public enum ChecksumType
{
FLEETSYNC_ANI( new short[]
{
0x0000, //Unknown Always One
0x0000, //Status Msg Flag
0x14C3, //Status Message 6 001010011000011
0x6908, //Status Message 5 110100100001000
0x3484, //Status Message 4 011010010000100
0x1A42, //Status Message 3 001101001000010
0x0D21, //Status Message 2 000110100100001
0x729A, //Status Message 1 111001010011010
0x394D, //Status Message 0 011100101001101
0x0000, //Type 7
0x0000, //Type 6
0x0000, //Type 5
0x0000, //Type 4
0x0000, //Type 3
0x0000, //Type 2
0x0000, //Type 1
0x0000, //Type 0
0x0000, //Fleet From 7
0x0000, //Fleet From 6
0x0000, //Fleet From 5
0x0000, //Fleet From 4
0x402E, //Fleet From 3 100000000101110
0x2017, //Fleet From 2 010000000010111
0x6401, //Fleet From 1 110010000000001
0x460A, //Fleet From 0 100011000001010
0x0000, //Ident From 11
0x0000, //Ident From 10
0x0000, //Ident From 9
0x0000, //Ident From 8
0x0CB1, //Ident From 7 000110010110001
0x7252, //Ident From 6 111001001010010
0x3929, //Ident From 5 011100100101001
0x689E, //Ident From 4 110100010011110
0x344F, //Ident From 3 011010001001111
0x6E2D, //Ident From 2 110111000101101
0x431C, //Ident From 1 100001100011100
0x218E, //Ident From 0 010000110001110
0x0000, //Ident To 11
0x0000, //Ident To 10
0x0000, //Ident To 9
0x0000, //Ident To 8
0x6685, //Ident To 7 110011010000101
0x4748, //Ident To 6 100011101001000
0x23A4, //Ident To 5 010001110100100
0x11D2, //Ident To 4 001000111010010
0x08E9, //Ident To 3 000100011101001
0x707E, //Ident To 2 111000001111110
0x383F, //Ident To 1 011100000111111
0x6815, //Ident To 0 110100000010101
}),
LTR( new short[]
{ 0x0038, //Area
0x001C, //Channel 4
0x000E, //Channel 3
0x0046, //Channel 2
0x0023, //Channel 1
0x0051, //Channel 0
0x0068, //Home 4
0x0075, //Home 3
0x007A, //Home 2
0x003D, //Home 1
0x001F, //Home 0
0x004F, //Group 7
0x0026, //Group 6
0x0052, //Group 5
0x0029, //Group 4
0x0015, //Group 3
0x000B, //Group 2
0x0045, //Group 1
0x0062, //Group 0
0x0031, //Free 4
0x0019, //Free 3
0x000D, //Free 2
0x0007, //Free 1
0x0043 //Free 0
} ),
PASSPORT( new short[]
{
// 0x6e,
// 0xbf,
// 0xd6,
// 0xe3,
// 0xf8,
// 0x7c,
// 0x3e,
// 0x97,
// 0xc2,
// 0xe9,
// 0x75,
// 0x3b,
// 0x94,
// 0x4a,
// 0xad,
// 0x57,
// 0xa2,
// 0xd9,
// 0x6d,
// 0x37,
// 0x92,
// 0xc1,
// 0x61,
// 0x31,
// 0x19,
// 0x0d,
// 0x07,
// 0x8a,
// 0xcd,
// 0x67,
// 0xba,
// 0xd5,
// 0x6b, // 0111110
// 0xbc, // 1111100
// 0x5e, // 1101001
// 0xa7, // 1000011
// 0xda, // 0010111
// 0xe5, // 0101110
// 0x73, // 1011100
// 0xb0, // 0101001
// 0x58, // 1010010
// 0x2c, // 0110101 Bit 43
// 0x16, // 1101010
// 0x83, // 1000101 Bit 45
// 0xc8, // 0011011 Bit 46
// 0x64, // 0110110
// 0x32, // 1101100
// 0x91, // 1001001
// 0x49,
// 0x25,
// 0x13,
} ),
MULTINET( new short[]
{
// 0x96, // 0010110
// 0xAC, // 0101100
// 0xd8, // 1011000
// 0x21, // 0100001
// 0x42, // 1000010
// 0x95, // 0010101
// 0xaa, // 0101010
// 0xd4, // 1010100
// 0x39, // 0111001
// 0x72, // 1110010
// 0xf5, // 1110101 Bit 10
// 0x7b, // 1111011
// 0xe7, // 1100111
// 0x5f, // 1011111
// 0xaf, // 0101111
// 0xde, // 1011110
// 0x2d, // 0101101
// 0x5a, // 1011010
// 0xa5, // 0100101
// 0xca, // 1001010
// 0x05, // 0000101
// 0x0a, // 0001010
// 0x14, // 0010100
// 0x28, // 0101000
// 0x50, // 1010000
// 0xb1, // 0110001
// 0xe2, // 1100010
// 0x55, // 1010101
// 0xbb, // 0111011
// 0xf6, // 1110110
// 0x7d, // 1111101
// 0xeb, // 1101011
// 0x47, // 1000111
// 0x9f, // 0011111 Bit 33
// 0xbe, // 0111110
// 0xfc, // 1111100
// 0x69, // 1101001
// 0xc3, // 1000011
// 0x17, // 0010111
// 0x2e, // 0101110
// 0x5c, // 1011100
// 0xA9, // 0101001
// 0xD2, // 1010010
// 0x35, // 0110101 Bit 43
// 0x6A, // 1101010
// 0xC5, // 1000101 Bit 45
// 0x1B, // 0011011 Bit 46
// 0x36, // 0110110
// 0x6C, // 1101100
// 0xC9 // 1001001
});
private short[] mValues;
ChecksumType( short[] values )
{
mValues = values;
}
public short[] getValues()
{
return mValues;
}
}

View File

@@ -0,0 +1,53 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*******************************************************************************
* 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
******************************************************************************/
/**
* Golay 18/6/8 decoder. Uses the Golay24 edac class to perform edac on Golay
* 18 messages by treating the Golay 18 message as a Golay 24 message with the
* left-most 6 bits set to zero.
*/
public class Golay18
{
private final static Logger mLog = LoggerFactory.getLogger( Golay18.class );
/**
* Performs error detection and correction.
*/
public static void checkAndCorrect( BinaryMessage message, int startIndex )
{
int value = message.getInt( startIndex, startIndex + 17 );
BinaryMessage temp = new BinaryMessage( 24 );
temp.load( 6, 18, value );
BinaryMessage corrected = Golay24.checkAndCorrect( temp, 0 );
int correctedValue = corrected.getInt( 6, 23 );
message.load( startIndex, 18, correctedValue );
}
}

View File

@@ -0,0 +1,128 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
public class Golay23
{
public static final int MAX_CORRECTABLE_ERRORS = 3;
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
};
/**
* Implements Golay(23,12,7) error detection and correction. Returns the
* number of detected errors. If the error count is less than or equal to
* the max correctable errors (3), then the error bits are corrected.
* Otherwise the message is left intact and an error count greater than 3
* is returned.
*
* @param frame - message frame bitset
* @param startIndex - first bit index of the golay protected bit sequence
*
* @return - number of detected errors
*/
public static int checkAndCorrect( BinaryMessage frame, int startIndex )
{
int syndrome = getSyndrome( frame, startIndex );
/* No errors */
if( syndrome == 0 )
{
return 0;
}
BinaryMessage copy = frame.getSubMessage( startIndex, startIndex + 23 );
int index = -1;
int syndromeWeight = MAX_CORRECTABLE_ERRORS;
int errors = 0;
while( index < 23 )
{
if( index != -1 )
{
/* restore the previous flipped bit */
if( index > 0 )
{
copy.flip( index - 1 );
}
copy.flip( index );
syndromeWeight = MAX_CORRECTABLE_ERRORS - 1;
}
syndrome = getSyndrome( copy, 0 );
if( syndrome > 0 )
{
for( int i = 0; i < 23; i++ )
{
errors = Integer.bitCount( syndrome );
if( errors <= syndromeWeight )
{
copy.xor( 12, 11, syndrome );
copy.rotateRight( i, 0, 22 );
if( index >= 0 )
{
errors ++;
}
int corrected = copy.getInt( 0, 22 );
int original = frame.getInt( startIndex, startIndex + 22 );
int errorCount = Integer.bitCount( original ^ corrected );
if( errorCount <= 3 )
{
frame.load( startIndex, 23, corrected );
}
return errorCount;
}
else
{
copy.rotateLeft( 0, 22 );
syndrome = getSyndrome( copy, 0 );
}
}
index++;
}
}
/* Return an error count greater than 3 to indicate failed correction attempt */
return 4;
}
private static int getSyndrome( BinaryMessage frame, int startIndex )
{
int calculated = calculateChecksum( frame, startIndex );
int checksum = frame.getInt( startIndex + 12, startIndex + 22 );
return ( checksum ^ calculated );
}
private static int calculateChecksum( BinaryMessage frame, int startIndex )
{
int calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for (int i = frame.nextSetBit( startIndex );
i >= startIndex && i < startIndex + 12;
i = frame.nextSetBit( i+1 ) )
{
calculated ^= CHECKSUMS[ i - startIndex ];
}
return calculated;
}
}

View File

@@ -0,0 +1,167 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*******************************************************************************
* 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 Golay24
{
private final static Logger mLog = LoggerFactory.getLogger( Golay24.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( BinaryMessage 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;
}
/**
* Performs error detection and returns a corrected copy of the 24-bit
* message that starts at the start index.
* @param message - source message containing startIndex + 24 bits length
* @param startIndex - start of the 24-bit galois 24 protected bit set
* @return - corrected 24-bit galois value
*/
public static BinaryMessage checkAndCorrect( BinaryMessage 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 );
}
message.setCRC( CRC.PASSED );
return message;
}
/* Get original message value */
int original = message.getInt( 0, 22 );
int index = -1;
int syndromeWeight = 3;
int errors = 0;
while( index < 23 )
{
if( index != -1 )
{
/* restore the previous flipped bit */
if( index > 0 )
{
message.flip( index - 1 );
}
message.flip( index );
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 )
{
message.setCRC( CRC.FAILED_CRC );
return message;
}
message.setCRC( CRC.PASSED );
return message;
}
else
{
message.rotateLeft( startIndex, startIndex + 22 );
syndrome = getSyndrome( message, startIndex );
}
}
index++;
}
}
message.setCRC( CRC.FAILED_CRC );
return message;
}
private static int getSyndrome( BinaryMessage message, int startIndex )
{
int calculated = calculateChecksum( message, startIndex );
int checksum = message.getInt( startIndex + 12, startIndex + 22 );
return ( checksum ^ calculated );
}
}

View File

@@ -0,0 +1,115 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Hamming10
{
private final static Logger mLog = LoggerFactory.getLogger( Hamming10.class );
private static int[] CHECKSUMS = new int[] { 0xE,0xD,0xB,0X7,0x3,0xC };
/**
* Performs error detection and correction of any single-bit errors. This
* is a truncated version of the Hamming15 class.
*
* @param frame - binary frame containing a Hamming(10,6,3) protected field
*
* @param startIndex - offset to the first bit of the field
*
* @return - 0 = no errors
* 1 = a single-bit error was corrected
*/
public static int checkAndCorrect( BinaryMessage frame, int startIndex )
{
int syndrome = getSyndrome( frame, startIndex );
switch( syndrome )
{
case 0:
return 0;
case 1:
frame.flip( startIndex + 9 ); //Parity 1
return 1;
case 2:
frame.flip( startIndex + 8 ); //Parity 2
return 1;
case 3:
frame.flip( startIndex + 4 ); //Data 2
return 1;
case 4:
frame.flip( startIndex + 7 ); //Parity 4
return 1;
case 5:
return 2;
case 6:
return 2;
case 7:
frame.flip( startIndex + 3 ); //Data 3
return 1;
case 8:
frame.flip( startIndex + 6 ); //Parity 8
return 1;
case 9:
return 2;
case 10:
return 2;
case 11:
frame.flip( startIndex + 2 ); //Data 4
return 1;
case 12:
frame.flip( startIndex + 5 ); //Data 1
return 1;
case 13:
frame.flip( startIndex + 1 ); //Data 5
return 1;
case 14:
frame.flip( startIndex + 0 ); //Data 6
return 1;
case 15:
return 2;
}
/* We'll never get to here */
return 2;
}
/**
* Calculates the checksum (Parity 8,4,2,1) for data (6 <> 1 ) bits.
* @param frame - frame containing hamming(10) protected word
* @param startIndex - start bit index of the hamming protected word
* @return parity value, 0 - 15
*/
private static int calculateChecksum( BinaryMessage frame, int startIndex )
{
int calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for (int i = frame.nextSetBit( startIndex );
i >= startIndex && i < startIndex + 6;
i = frame.nextSetBit( i+1 ) )
{
calculated ^= CHECKSUMS[ i - startIndex ];
}
return calculated;
}
/**
* Calculates the syndrome - xor of the calculated checksum and the actual
* checksum.
*
* @param frame - binary frame containing a hamming(15,11,4) protected word
* @param startIndex - of bit 0 of the hamming protected word
* @return - 0 (no errors) or 1 (single bit error corrected)
*/
private static int getSyndrome( BinaryMessage frame, int startIndex )
{
int calculated = calculateChecksum( frame, startIndex );
int checksum = frame.getInt( startIndex + 6, startIndex + 9 );
return ( checksum ^ calculated );
}
}

View File

@@ -0,0 +1,117 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
public class Hamming15
{
private static int[] CHECKSUMS = new int[] { 0xF,0xE,0xD,0xC,0xB,0XA,0x9,
0x7,0x6,0x5,0x3 };
/**
* Performs error detection and correction of any single-bit errors.
*
* @param frame - binary frame containing a hamming(15,11,4) protected field
*
* @param startIndex - offset to the first bit of the field
*
* @return - 0 = no errors
* 1 = a single-bit error was corrected
* 2 = two or more errors detected - no corrections made
*/
public static int checkAndCorrect( BinaryMessage frame, int startIndex )
{
int syndrome = getSyndrome( frame, startIndex );
switch( syndrome )
{
case 0:
return 0;
case 1:
frame.flip( startIndex + 14 ); //Parity 1
return 1;
case 2:
frame.flip( startIndex + 13 ); //Parity 2
return 1;
case 3:
frame.flip( startIndex + 10 ); //Data 1
return 1;
case 4:
frame.flip( startIndex + 12 ); //Parity 4
return 1;
case 5:
frame.flip( startIndex + 9 ); //Data 2
return 1;
case 6:
frame.flip( startIndex + 8 ); //Data 3
return 1;
case 7:
frame.flip( startIndex + 7 ); //Data 4
return 1;
case 8:
frame.flip( startIndex + 11 ); //Parity 8
return 1;
case 9:
frame.flip( startIndex + 6 ); //Data 5
return 1;
case 10:
frame.flip( startIndex + 5 ); //Data 6
return 1;
case 11:
frame.flip( startIndex + 4 ); //Data 7
return 1;
case 12:
frame.flip( startIndex + 3 ); //Data 8
return 1;
case 13:
frame.flip( startIndex + 2 ); //Data 9
return 1;
case 14:
frame.flip( startIndex + 1 ); //Data 10
return 1;
case 15:
frame.flip( startIndex ); //Data 11
return 1;
}
/* We'll never get to here */
return 2;
}
/**
* Calculates the checksum (Parity 8,4,2,1) for data (11 <> 1 ) bits.
* @param frame - frame containing hamming(15) protected word
* @param startIndex - start bit index of the hamming protected word
* @return parity value, 0 - 15
*/
private static int calculateChecksum( BinaryMessage frame, int startIndex )
{
int calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for (int i = frame.nextSetBit( startIndex );
i >= startIndex && i < startIndex + 11;
i = frame.nextSetBit( i+1 ) )
{
calculated ^= CHECKSUMS[ i - startIndex ];
}
return calculated;
}
/**
* Calculates the syndrome - xor of the calculated checksum and the actual
* checksum.
*
* @param frame - binary frame containing a hamming(15,11,4) protected word
* @param startIndex - of bit 0 of the hamming protected word
* @return - 0 (no errors) or 1 (single bit error corrected)
*/
private static int getSyndrome( BinaryMessage frame, int startIndex )
{
int calculated = calculateChecksum( frame, startIndex );
int checksum = frame.getInt( startIndex + 11, startIndex + 14 );
return ( checksum ^ calculated );
}
}

View File

@@ -0,0 +1,38 @@
package io.github.dsheirer.edac;
/*******************************************************************************
* 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/>
******************************************************************************/
public class ReedSolomon_63_47_17 extends BerlekempMassey_63
{
/**
* Reed-Solomon RS(63,47,17) decoder. This can also be used for error detection
* and correction of the following RS codes:
*
* RS(36,20,17) - max 8 errors
* RS(24,16,9) - max 4 errors
* RS(24,12,13) - max 6 errors
*
* The maximum correctable errors is determined by (n-k)/2, or hamming
* distance divided by 2.
*/
public ReedSolomon_63_47_17( int maximumCorrectableErrors )
{
super( maximumCorrectableErrors );
}
}

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 io.github.dsheirer.edac.trellis;
public class DMRNode extends Node
{
//Hamming error values indicating the number of bits set in each indexed value, 0-15
public static final int[] HAMMING_ERROR_COUNT = new int[]{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
/**
* Transmitted quad-bit value, derived from ETSI TS 102-361-1, Table B.7 Trellis encoder state transition table
*
* Note: the B.7 table contains constellation point ID numbers that have to be converted into actual bit values
* where the bits represent the dibit pair for the constellation point.
*/
public static final int[][] DMR_TRANSITION_MATRIX = new int[][]
{
{2,13,14,1,7,8,11,4},
{14,1,7,8,11,4,2,13},
{10,5,6,9,15,0,3,12},
{6,9,15,0,3,12,10,5},
{15,0,3,12,10,5,6,9},
{3,12,10,5,6,9,15,0},
{7,8,11,4,2,13,14,1},
{11,4,2,13,14,1,7,8}
};
/**
* Trellis Coded Modulation (TCM) 3/4-rate trellis node for decoding DMR
*
* @param inputValue for the trellis time instant
* @param transmittedOutputValue the actual four-bit output value that was transmitted.
*/
public DMRNode(int inputValue, int transmittedOutputValue)
{
super(inputValue, transmittedOutputValue);
}
/**
* Calculates the error between the preceeding node and this node as the Hamming distance between the expected
* four-bit output value versus the actual transmitted output value.
*
* @param precedingNode that precedes this node
* @return error value for this node relative to the preceeding node argument
*/
@Override
public int getError(Node precedingNode)
{
if(precedingNode != null)
{
int expectedOutputValue = getOutputValue(precedingNode);
int errorMask = expectedOutputValue ^ getTransmittedOutputValue();
return HAMMING_ERROR_COUNT[errorMask];
}
return 0;
}
/**
* Expected output four-bit value when this node is preceeded by the argument node.
*
* @param precedingNode that precedes this node
* @return expected output value
*/
@Override
public int getOutputValue(Node precedingNode)
{
return DMR_TRANSITION_MATRIX[precedingNode.getInputValue()][getInputValue()];
}
}

View File

@@ -0,0 +1,77 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 io.github.dsheirer.edac.trellis;
public abstract class Node
{
private int mInputValue;
private int mTransmittedOutputValue;
/**
* Trellis Coded Modulation (TCM) node. A node represents a time instant on the trellis and contains an input
* value and the corresponding transmitted output value for the time instant. A node does not maintain the
* state at the time instant -- the state is determined by the input value of a preceeding node.
*
* @param inputValue for this node
* @param transmittedOutputValue for this node
*/
public Node(int inputValue, int transmittedOutputValue)
{
mInputValue = inputValue;
mTransmittedOutputValue = transmittedOutputValue;
}
/**
* Input value for this node.
*
* @return input value
*/
public int getInputValue()
{
return mInputValue;
}
/**
* Transmitted output value for this node
* @return transmitted output value
*/
public int getTransmittedOutputValue()
{
return mTransmittedOutputValue;
}
/**
* Calculates the error value for this node (time instant) by determining the expected output value that would have
* been transmitted using the state from the preceding node argument and this node's input value and comparing that
* expected value to the actual transmitted output value. This error is normally represented as the Hamming
* distance between the two values.
*
* @param precedingNode that precedes this node
* @return error value for this node relative to the preceeding node
*/
public abstract int getError(Node precedingNode);
/**
* Expected output value when this node is preceeded by the node specified in the argument.
*
* @param precedingNode that precedes this node
* @return expected output value
*/
public abstract int getOutputValue(Node precedingNode);
}

View File

@@ -0,0 +1,131 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 io.github.dsheirer.edac.trellis;
import java.util.ArrayList;
import java.util.List;
public class Path
{
private List<Node> mNodes = new ArrayList<>();
private int mError = 0;
/**
* Trellis Coded Modulation (TCM) path. Represents a sequence of nodes that traverse various time instants and
* states across a trellis.
*
* Note: each node can be shared across multiple paths. This class does not assume that any nodes are unique.
* @param startingNode to start the path. This node normally contains an input value of zero, but is implementation
* specific.
*/
public Path(Node startingNode)
{
add(startingNode);
}
/**
* Trellis Coded Modulation (TCM) path. Represents a sequence of nodes that traverse various time instants and
* states across a trellis.
*
* Note: each node can be shared across multiple paths. This class does not assume that any nodes are unique.
*
* @param original path to create a copy from. Each of the nodes in the original will be added to this path and
* after construction both this path and the original will contain the exact same set of nodes.
*/
private Path(Path original)
{
for(Node node: original.getNodes())
{
add(node);
}
}
/**
* Cumulative error across all nodes contained within this path. Error value is updated as each new node is added
* to this path.
*
* @return cumulative error value
*/
public int getError()
{
return mError;
}
/**
* Length or number of nodes contained within this path. Note: this value includes the starting node and may
* also include a flushing node. The values contained in the starting and flushing nodes are normally not used
* when decoding a path.
*
* @return number of nodes in this path
*/
public int getLength()
{
return mNodes.size() - 1;
}
/**
* The set of nodes contained in this path.
* @return nodes
*/
public List<Node> getNodes()
{
return mNodes;
}
/**
* Adds the node to this path and updates the cumulative error value for this path by adding the node's error
* value to the running error total.
*
* @param node to add to this path
*/
public void add(Node node)
{
if(!mNodes.isEmpty())
{
mError += node.getError(getLastNode());
}
mNodes.add(node);
}
/**
* Last node in this path.
*
* @return last node
*/
public Node getLastNode()
{
if(!mNodes.isEmpty())
{
return mNodes.get(mNodes.size() - 1);
}
return null;
}
/**
* Utility method to create an exact copy of this path.
*
* @return a new copy of this path.
*/
public Path copyOf()
{
return new Path(this);
}
}

View File

@@ -0,0 +1,226 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 io.github.dsheirer.edac.trellis;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public abstract class ViterbiDecoder
{
private int mInputBitLength;
private int mInputValueCount;
private int mOutputBitLength;
private int mOutputValueCount;
/**
* Viterbi decoder for trellis coded modulation (TCM) encoded binary sequences.
*
* @param inputBitLength for coding (e.g. 3/4 rate input bit length is 3)
* @param outputBitLength for coding (e.g. 3/4 rate output bit length is 4)
*/
public ViterbiDecoder(int inputBitLength, int outputBitLength)
{
mInputBitLength = inputBitLength;
mOutputBitLength = outputBitLength;
mInputValueCount = (int)Math.pow(2.0, mInputBitLength);
mOutputValueCount = (int)Math.pow(2.0, mOutputBitLength);
}
/**
* Creates a node that represents an input value and the actual/transmitted output value.
*
* Note: this method must be implemented by a sub-class since the node requires a specific state transition
* table/matrix implementation.
*
* @param inputValue for the specific trellis node
* @param transmittedOutputValue the actual transmitted output value
* @return node for decoding
*/
protected abstract Node createNode(int inputValue, int transmittedOutputValue);
/**
* Creates a flushing or final node that represents an actual/transmitted output value and a flushing input value.
*
* The final flushing input value is implementation-specific, therefore this will be implemented in any subclass
* of this decoder. A normal flushing input value is zero.
*
* @param transmittedOutputValue the actual transmitted output value
* @return flushing node
*/
protected abstract Node createFlushingNode(int transmittedOutputValue);
/**
* Creates a starting node that represents a starting state and notional transmitted output value.
*
* The starting input value is implementation-specific, therefore this will be implemented in any subclass
* of this decoder. A normal starting input value is zero.
*
* @return starting node
*/
protected abstract Node createStartingNode();
/**
* Size in bits of the input and state values (e.g. 3/4 rate input size is 3)
*
* @return input bit size
*/
public int getInputBitLength()
{
return mInputBitLength;
}
/**
* Count of input or state values where the set of values is in the range of 0 <> count - 1
*
* @return input or state values count
*/
public int getInputValueCount()
{
return mInputValueCount;
}
/**
* Size in bits of the output constellation or transmitted symbol value (e.g. 3/4 rate output size is 4)
*
* @return output size in bits
*/
public int getOutputBitLength()
{
return mOutputBitLength;
}
/**
* Count of input or state values where the set of values is in the range of 0 <> count - 1
*
* @return input or state values count
*/
public int getOutputValueCount()
{
return mOutputValueCount;
}
/**
* Decodes the TCM encoded transmitted output values and returns a path the represents the most likely transmitted
* sequence of nodes.
*
* @param transmittedOutputValues from the encoded message
* @return most likely path representing the transmitted values
*/
public Path decode(int[] transmittedOutputValues)
{
Collection<Path> survivingPaths = new ArrayList<>();
survivingPaths.add(new Path(createStartingNode()));
//Add all but the last transmitted value to the starting path, creating a set of survivors at each iteration.
for(int x = 0; x < transmittedOutputValues.length - 1; x++)
{
survivingPaths = add(survivingPaths, transmittedOutputValues[x]);
}
//Flush the survivors with the final transmitted value and return the lone surviving path
return flush(survivingPaths, transmittedOutputValues[transmittedOutputValues.length - 1]);
}
/**
* Creates a set of new nodes representing all possible input values and the transmitted output value argument
* and iteratively adds the set of nodes to a copy of each of the surviving paths. A set of surviving paths is
* returned that represents each path that terminates at a distinct input/state value that has the least error
* value.
*
* @param survivingPaths from the previous time instant to evaluate for the next time instant in the trellis
* using the transmitted output value.
*
* @param transmittedOutputValue that will be decoded for this time instant.
*
* @return paths that have survived (ie have the lowest error value) after adding nodes for the transmitted
* output value
*
*/
public Collection<Path> add(Collection<Path> survivingPaths, int transmittedOutputValue)
{
Map<Integer,Path> survivingPathMap = new HashMap<>();
for(int x = 0; x < getInputValueCount(); x++)
{
//Create a single/unique node for each possible input value combined with the transmitted output value
Node node = createNode(x, transmittedOutputValue);
//Evaluate each surviving path by creating a copy and adding the node to it and see if it survives
for(Path path: survivingPaths)
{
Path pathToEvaluate = path.copyOf();
pathToEvaluate.add(node);
if(survivingPathMap.containsKey(node.getInputValue()))
{
Path survivingPath = survivingPathMap.get(node.getInputValue());
//Replace the current survivor path if this evaluate path has a lower error value
if(pathToEvaluate.getError() < survivingPath.getError())
{
survivingPathMap.put(node.getInputValue(), pathToEvaluate);
}
}
else
{
//No survivor yet -- set this evaluate path as the first survivor
survivingPathMap.put(node.getInputValue(), pathToEvaluate);
}
}
}
return survivingPathMap.values();
}
/**
* Flushes each of the surviving paths with a final/flushing node using the transmitted symbol. A final flushing
* input value or state is implementation-specific, but normally uses a value of zero. Since all paths will be
* evaluated against each other as terminating at the same state, only the path with the lowest error value will
* be returned from this method.
*
* @param survivingPaths to flush with the final flushing node
* @param transmittedOutputValue to use for the final flushing node
*
* @return the single survivor path that remains after adding the flushing node to each of the candidate argument
* survivor paths.
*/
public Path flush(Collection<Path> survivingPaths, int transmittedOutputValue)
{
Node flushingNode = createFlushingNode(transmittedOutputValue);
Path bestPath = null;
for(Path survivingPath: survivingPaths)
{
survivingPath.add(flushingNode);
if(bestPath == null || (survivingPath.getError() < bestPath.getError()))
{
bestPath = survivingPath;
}
}
return bestPath;
}
}

View File

@@ -0,0 +1,159 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 io.github.dsheirer.edac.trellis;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import java.util.List;
public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
{
/**
* Viterbi decoder for Digital Mobile Radio (DMR) 3/4 rate Trellis Coded Modulation (TCM) encoded messages.
*/
public ViterbiDecoder_3_4_DMR()
{
super(3,4);
}
/**
* Decodes a 3/4 rate trellis coded modulation (TCM) encoded DMR binary message containing 196 bits that have
* already been deinterleaved.
*
* @param encodedMessage to decode that has already been deinterleaved.
* @return decoded message
*/
public BinaryMessage decode(BinaryMessage encodedMessage)
{
int[] symbols = getSymbols(encodedMessage);
Path mostLikelyPath = decode(symbols);
return getMessage(mostLikelyPath);
}
/**
* Extracts the decoded/corrected message from the surviving path argument.
*
* @param path to extract a message from
* @return corrected binary message.
*/
private static CorrectedBinaryMessage getMessage(Path path)
{
List<Node> mNodes = path.getNodes();
//Each node contains an input value excluding the starting(0) and final flushing(0) nodes.
CorrectedBinaryMessage message = new CorrectedBinaryMessage((mNodes.size() - 2) * 3);
if(mNodes.size() > 2)
{
for(int x = 1; x < mNodes.size() - 1; x++)
{
int inputValue = mNodes.get(x).getInputValue();
int messageOffset = 3 * (x - 1);
if((inputValue & 4) == 4)
{
message.set(messageOffset);
}
if((inputValue & 2) == 2)
{
message.set(messageOffset + 1);
}
if((inputValue & 1) == 1)
{
message.set(messageOffset + 2);
}
}
}
//Transfer the corrected error count to the message
message.setCorrectedBitCount(path.getError());
return message;
}
/**
* Extracts (49) four-bit symbols (196 bits / 4) from the deinterleaved DMR binary message
*
* @param encodedMessage that deinterleaved and contains 196-bits of 3/4 TCM encoding
* @return symbols
*/
public int[] getSymbols(BinaryMessage encodedMessage)
{
//Ensure we have an integral number of transmitted symbols (nibbles) in the message
if(encodedMessage.size() % getOutputBitLength() != 0)
{
throw new IllegalArgumentException("Encoded message must contain an integral number of 4-bit symbols -" +
" message size: " + encodedMessage.size());
}
int[] symbols = new int[encodedMessage.size() / getOutputBitLength()];
int index = 0;
int pointer = 0;
while(index < encodedMessage.size())
{
int symbol = encodedMessage.getInt(index, index + (getOutputBitLength() - 1));
symbols[pointer++] = symbol;
index += getOutputBitLength();
}
return symbols;
}
/**
* Creates a DMR node
* @param inputValue for the specific trellis node
* @param transmittedOutputValue the actual transmitted output value
* @return created node
*/
@Override
protected Node createNode(int inputValue, int transmittedOutputValue)
{
return new DMRNode(inputValue, transmittedOutputValue);
}
/**
* Creates a DMR flushing node. DMR uses an input value of zero to flush the final output value out of the
* encoder.
*
* @param transmittedOutputValue the actual transmitted output value
* @return
*/
@Override
protected Node createFlushingNode(int transmittedOutputValue)
{
return new DMRNode(0, transmittedOutputValue);
}
/**
* Creates a DMR starting node. DMR uses a starting input value of zero for the initial node.
*/
@Override
protected Node createStartingNode()
{
return new DMRNode(0,0);
}
}