mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 00:57:31 -05:00
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:
106
src/main/java/io/github/dsheirer/edac/BCH_63_16_11.java
Normal file
106
src/main/java/io/github/dsheirer/edac/BCH_63_16_11.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
546
src/main/java/io/github/dsheirer/edac/BerlekempMassey_63.java
Normal file
546
src/main/java/io/github/dsheirer/edac/BerlekempMassey_63.java
Normal 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;
|
||||
}
|
||||
}
|
||||
73
src/main/java/io/github/dsheirer/edac/CRC.java
Normal file
73
src/main/java/io/github/dsheirer/edac/CRC.java
Normal 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();
|
||||
}
|
||||
}
|
||||
207
src/main/java/io/github/dsheirer/edac/CRCFleetsync.java
Normal file
207
src/main/java/io/github/dsheirer/edac/CRCFleetsync.java
Normal 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;
|
||||
}
|
||||
}
|
||||
193
src/main/java/io/github/dsheirer/edac/CRCLJ.java
Normal file
193
src/main/java/io/github/dsheirer/edac/CRCLJ.java
Normal 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() );
|
||||
}
|
||||
}
|
||||
185
src/main/java/io/github/dsheirer/edac/CRCLTR.java
Normal file
185
src/main/java/io/github/dsheirer/edac/CRCLTR.java
Normal 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;
|
||||
}
|
||||
}
|
||||
470
src/main/java/io/github/dsheirer/edac/CRCP25.java
Normal file
470
src/main/java/io/github/dsheirer/edac/CRCP25.java
Normal 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() );
|
||||
}
|
||||
}
|
||||
215
src/main/java/io/github/dsheirer/edac/CRCPassport.java
Normal file
215
src/main/java/io/github/dsheirer/edac/CRCPassport.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
239
src/main/java/io/github/dsheirer/edac/CRCUtil.java
Normal file
239
src/main/java/io/github/dsheirer/edac/CRCUtil.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
52
src/main/java/io/github/dsheirer/edac/Checksum.java
Normal file
52
src/main/java/io/github/dsheirer/edac/Checksum.java
Normal 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;
|
||||
}
|
||||
}
|
||||
96
src/main/java/io/github/dsheirer/edac/ChecksumTest.java
Normal file
96
src/main/java/io/github/dsheirer/edac/ChecksumTest.java
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
223
src/main/java/io/github/dsheirer/edac/ChecksumType.java
Normal file
223
src/main/java/io/github/dsheirer/edac/ChecksumType.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
53
src/main/java/io/github/dsheirer/edac/Golay18.java
Normal file
53
src/main/java/io/github/dsheirer/edac/Golay18.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
128
src/main/java/io/github/dsheirer/edac/Golay23.java
Normal file
128
src/main/java/io/github/dsheirer/edac/Golay23.java
Normal 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;
|
||||
}
|
||||
}
|
||||
167
src/main/java/io/github/dsheirer/edac/Golay24.java
Normal file
167
src/main/java/io/github/dsheirer/edac/Golay24.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
115
src/main/java/io/github/dsheirer/edac/Hamming10.java
Normal file
115
src/main/java/io/github/dsheirer/edac/Hamming10.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
117
src/main/java/io/github/dsheirer/edac/Hamming15.java
Normal file
117
src/main/java/io/github/dsheirer/edac/Hamming15.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
86
src/main/java/io/github/dsheirer/edac/trellis/DMRNode.java
Normal file
86
src/main/java/io/github/dsheirer/edac/trellis/DMRNode.java
Normal 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()];
|
||||
}
|
||||
}
|
||||
77
src/main/java/io/github/dsheirer/edac/trellis/Node.java
Normal file
77
src/main/java/io/github/dsheirer/edac/trellis/Node.java
Normal 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);
|
||||
}
|
||||
131
src/main/java/io/github/dsheirer/edac/trellis/Path.java
Normal file
131
src/main/java/io/github/dsheirer/edac/trellis/Path.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user