#6 DMR Decoder (#947)

* #6 DMR decoder feature - work in progress

* #6 DMR Decoder.

* #6 DMR Decoder.  Code review updates.

Co-authored-by: Zhenyu Mao <maozhenyu@foxmail.com>
Co-authored-by: Denny <denny@denny-desktop>
This commit is contained in:
Denny Sheirer
2020-10-12 04:57:59 -04:00
committed by GitHub
parent b061db1189
commit 7eec82deee
436 changed files with 36258 additions and 2409 deletions

View File

@@ -1,106 +1,106 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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;
/*******************************************************************************
* 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
public class BCH_63_16_11 extends ReedSolomon_63_P25
{
private final static Logger mLog = LoggerFactory.getLogger( BCH_63_16_11.class );
private final static Logger mLog = LoggerFactory.getLogger(BCH_63_16_11.class);
/**
* BCH( 63,16,11) decoder
*/
public BCH_63_16_11()
/**
* BCH( 63,16,11) decoder
*/
public BCH_63_16_11()
{
/* TT = 11 = maximum correctable bit errors */
super( 11 );
super(63, 41);
}
/**
* 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 );
/**
* 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;
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";
int[] original = message.toReverseIntegerArray(0, 62);
int[] corrected = new int[63];
BinaryMessage errorMessage = BinaryMessage.load( error );
boolean irrecoverableErrors = decode(original, corrected);
BCH_63_16_11 bch = new BCH_63_16_11();
if(irrecoverableErrors)
{
message.setCRC(CRC.FAILED_CRC);
mLog.debug( "ORIG:" + orig );
mLog.debug( " ERR:" + errorMessage.toString() );
mLog.debug( " " );
return message;
}
else
{
for(int x = 0; x < 16; x++)
{
int index = 63 - x - 1;
errorMessage = bch.correctNID( errorMessage );
mLog.debug( " CRC: " + errorMessage.getCRC().name() );
mLog.debug( "CORR:" + errorMessage.toString() );
mLog.debug( "ORIG:" + orig );
}
if(corrected[index] != original[index])
{
status = CRC.CORRECTED;
if(corrected[index] == 1)
{
message.set(x);
}
else
{
message.clear(x);
}
}
}
}
message.setCRC(status);
return message;
}
public static void main(String[] args)
{
String orig = "0010011000000011010010100000000110000111110011101010001010110000";
String error = "0001010100000011010010100000000110000111100011001010001010110000";
BinaryMessage errorMessage = BinaryMessage.load(error);
BCH_63_16_11 bch = new BCH_63_16_11();
mLog.debug("ORIG:" + orig);
mLog.debug(" ERR:" + errorMessage.toString());
mLog.debug(" ");
errorMessage = bch.correctNID(errorMessage);
mLog.debug(" CRC: " + errorMessage.getCRC().name());
mLog.debug("CORR:" + errorMessage.toString());
mLog.debug("ORIG:" + orig);
}
}

View File

@@ -0,0 +1,62 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
/*
Adopted from https://github.com/boatbod/op25/blob/e5e8fe457240fb358a8163bcf6fe7dda5e4d8392/op25/gr-op25_repeater/lib/hamming.cc
*/
public class BPTC_17_12_3 {
public static boolean decode17123(BinaryMessage d, int offset)
{
if(d==null) {
return false;
}
// Calculate the checksum this column should have
boolean c0 = d.get(0 + offset) ^ d.get(1 + offset) ^ d.get(2 + offset) ^ d.get(3 + offset) ^ d.get(6 + offset) ^ d.get(7 + offset) ^ d.get(9 + offset);
boolean c1 = d.get(0 + offset) ^ d.get(1 + offset) ^ d.get(2 + offset) ^ d.get(3 + offset) ^ d.get(4 + offset) ^ d.get(7 + offset) ^ d.get(8 + offset) ^ d.get(10 + offset);
boolean c2 = d.get(1 + offset) ^ d.get(2 + offset) ^ d.get(3 + offset) ^ d.get(4 + offset) ^ d.get(5 + offset) ^ d.get(8 + offset) ^ d.get(9 + offset) ^ d.get(11 + offset);
boolean c3 = d.get(0 + offset) ^ d.get(1 + offset) ^ d.get(4 + offset) ^ d.get(5 + offset) ^ d.get(7 + offset) ^ d.get(10 + offset);
boolean c4 = d.get(0 + offset) ^ d.get(1 + offset) ^ d.get(2 + offset) ^ d.get(5 + offset) ^ d.get(6 + offset) ^ d.get(8 + offset) ^ d.get(11 + offset);
// Compare these with the actual bits
byte n = 0x00;
n |= (c0 != d.get(12 + offset)) ? 0x01 : 0x00;
n |= (c1 != d.get(13 + offset)) ? 0x02 : 0x00;
n |= (c2 != d.get(14 + offset)) ? 0x04 : 0x00;
n |= (c3 != d.get(15 + offset)) ? 0x08 : 0x00;
n |= (c4 != d.get(16 + offset)) ? 0x10 : 0x00;
switch (n) {
// Parity bit errors
case 0x01: d.set(12 + offset, !d.get(12 + offset)); return true;
case 0x02: d.set(13 + offset, !d.get(13 + offset)); return true;
case 0x04: d.set(14 + offset, !d.get(14 + offset)); return true;
case 0x08: d.set(15 + offset, !d.get(15 + offset)); return true;
case 0x10: d.set(16 + offset, !d.get(16 + offset)); return true;
// Data bit errors
case 0x1B: d.set(0 + offset, !d.get(0 + offset)); return true;
case 0x1F: d.set(1 + offset, !d.get(1 + offset)); return true;
case 0x17: d.set(2 + offset, !d.get(2 + offset)); return true;
case 0x07: d.set(3 + offset, !d.get(3 + offset)); return true;
case 0x0E: d.set(4 + offset, !d.get(4 + offset)); return true;
case 0x1C: d.set(5 + offset, !d.get(5 + offset)); return true;
case 0x11: d.set(6 + offset, !d.get(6 + offset)); return true;
case 0x0B: d.set(7 + offset, !d.get(7 + offset)); return true;
case 0x16: d.set(8 + offset, !d.get(8 + offset)); return true;
case 0x05: d.set(9 + offset, !d.get(9 + offset)); return true;
case 0x0A: d.set(10 + offset,!d.get(10 + offset)); return true;
case 0x14: d.set(11 + offset,!d.get(11 + offset)); return true;
// No bit errors
case 0x00: return true;
// Unrecoverable errors
default: return false;
}
}
}

View File

@@ -0,0 +1,284 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.bits.BitSetFullException;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Block Product Turbo Code (BPTC) 196/96 Support
*/
public class BPTC_196_96
{
public static final int BPTC_LENGTH = 196;
public static final int EXTRACTED_LENGTH = 96;
public static final int MAX_ORIGINAL_INDEX = 136;
public static final int COLUMN_COUNT = 15;
public static final int MESSAGE_COLUMN_COUNT = 12; //Should be 11, but adjusted for the first pad bit
public static final int CHECKSUM_COLUMN_COUNT = 4;
public static final int MESSAGE_START_INDEX = 4;
/**
* DMR De-interleaving indices
*/
public static int[] BPTC_DEINTERLEAVE = new int[]{0, 181, 166, 151, 136, 121, 106, 91, 76, 61, 46, 31, 16, 1, 182,
167, 152, 137, 122, 107, 92, 77, 62, 47, 32, 17, 2, 183, 168, 153, 138, 123, 108, 93, 78, 63, 48, 33, 18, 3,
184, 169, 154, 139, 124, 109, 94, 79, 64, 49, 34, 19, 4, 185, 170, 155, 140, 125, 110, 95, 80, 65, 50, 35, 20,
5, 186, 171, 156, 141, 126, 111, 96, 81, 66, 51, 36, 21, 6, 187, 172, 157, 142, 127, 112, 97, 82, 67, 52, 37,
22, 7, 188, 173, 158, 143, 128, 113, 98, 83, 68, 53, 38, 23, 8, 189, 174, 159, 144, 129, 114, 99, 84, 69, 54,
39, 24, 9, 190, 175, 160, 145, 130, 115, 100, 85, 70, 55, 40, 25, 10, 191, 176, 161, 146, 131, 116, 101, 86, 71,
56, 41, 26, 11, 192, 177, 162, 147, 132, 117, 102, 87, 72, 57, 42, 27, 12, 193, 178, 163, 148, 133, 118, 103,
88, 73, 58, 43, 28, 13, 194, 179, 164, 149, 134, 119, 104, 89, 74, 59, 44, 29, 14, 195, 180, 165, 150, 135, 120,
105, 90, 75, 60, 45, 30, 15};
/**
* DMR Column Indices
*/
public static int[][] COLUMN_INDEXES = new int[][]{
{1, 16, 31, 46, 61, 76, 91, 106, 121, 136, 151, 166, 181},
{2, 17, 32, 47, 62, 77, 92, 107, 122, 137, 152, 167, 182},
{3, 18, 33, 48, 63, 78, 93, 108, 123, 138, 153, 168, 183},
{4, 19, 34, 49, 64, 79, 94, 109, 124, 139, 154, 169, 184},
{5, 20, 35, 50, 65, 80, 95, 110, 125, 140, 155, 170, 185},
{6, 21, 36, 51, 66, 81, 96, 111, 126, 141, 156, 171, 186},
{7, 22, 37, 52, 67, 82, 97, 112, 127, 142, 157, 172, 187},
{8, 23, 38, 53, 68, 83, 98, 113, 128, 143, 158, 173, 188},
{9, 24, 39, 54, 69, 84, 99, 114, 129, 144, 159, 174, 189},
{10, 25, 40, 55, 70, 85, 100, 115, 130, 145, 160, 175, 190},
{11, 26, 41, 56, 71, 86, 101, 116, 131, 146, 161, 176, 191},
{12, 27, 42, 57, 72, 87, 102, 117, 132, 147, 162, 177, 192},
{13, 28, 43, 58, 73, 88, 103, 118, 133, 148, 163, 178, 193},
{14, 29, 44, 59, 74, 89, 104, 119, 134, 149, 164, 179, 194},
{15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195}};
/**
* Performs de-interleave, error detect and correct and extracts a 96-bit message from the DMR BPTC(196,96)
* protected message
* @param original uncorrected BPTC(196,96) message
* @return error corrected and extracted 96-bit message
*/
public static CorrectedBinaryMessage extract(CorrectedBinaryMessage original)
{
//De-Interleave
CorrectedBinaryMessage message = deinterleave(original);
//Perform error detection and correction
correct(message);
//Extract the 96-bit message from the corrected 196 bits
CorrectedBinaryMessage extracted = new CorrectedBinaryMessage(EXTRACTED_LENGTH);
//Transfer the corrected bit count
extracted.setCorrectedBitCount(message.getCorrectedBitCount());
int index = MESSAGE_START_INDEX;
while(index < MAX_ORIGINAL_INDEX)
{
if((index % COLUMN_COUNT) < MESSAGE_COLUMN_COUNT)
{
try
{
extracted.add(message.get(index));
}
catch(BitSetFullException bsfe)
{
//Should never get to here
bsfe.printStackTrace();
return extracted;
}
index++;
}
else
{
index += CHECKSUM_COLUMN_COUNT;
}
}
return extracted;
}
/**
* De-interleaves the transmitted bits of a DMR BPTC(196,96) protected message
* @param message that contains interleaved (transmitted) message
* @return de-interleaved message
*/
private static CorrectedBinaryMessage deinterleave(CorrectedBinaryMessage message)
{
CorrectedBinaryMessage deinterleaved = new CorrectedBinaryMessage(BPTC_LENGTH);
for(int x = 0; x < BPTC_DEINTERLEAVE.length; x++)
{
deinterleaved.set(x, message.get(BPTC_DEINTERLEAVE[x]));
}
return deinterleaved;
}
/**
* Identifies the error indices from the row level Hamming(15,11,3) protected words
* @param message containing protected words in rows
* @return list of zero or more error indices
*/
private static List<Integer> getRowErrors(CorrectedBinaryMessage message)
{
List<Integer> rowErrors = new ArrayList<>();
int offset = 0;
int index = -1;
for(int row = 0; row < 13; row++)
{
//The first bit of the message is a pad/reserved bit and is ignored.
offset = (row * 15) + 1;
index = Hamming15.getErrorIndex(message, offset);
if(index >= 0)
{
rowErrors.add(index);
}
}
//Note: row errors should already be in order and don't need sorted
return rowErrors;
}
/**
* Identifies the error indices from the column level Hamming(13,9,3) protected words
* @param message containing protected words in columns
* @return list of zero or more error indices
*/
private static List<Integer> getColumnErrors(CorrectedBinaryMessage message)
{
List<Integer> columnErrors = new ArrayList<>();
int index = -1;
for(int column = 0; column < 15; column++)
{
index = Hamming13.getErrorIndex(message, COLUMN_INDEXES[column]);
if(index >= 0)
{
columnErrors.add(index);
}
}
Collections.sort(columnErrors);
return columnErrors;
}
/**
* Performs error detection and correction on the DMR BPTC(196,96) protected message
*
* @param message to process and extract
*/
private static void correct(CorrectedBinaryMessage message)
{
List<Integer> rowErrors = getRowErrors(message);
if(!rowErrors.isEmpty())
{
List<Integer> columnErrors = getColumnErrors(message);
if(matches(rowErrors, columnErrors))
{
for(Integer index: rowErrors)
{
message.flip(index);
}
message.setCorrectedBitCount(rowErrors.size());
}
else
{
//Step 1: correct row errors and recalculate column errors (should be zero)
for(Integer index: rowErrors)
{
message.flip(index);
}
List<Integer> columnErrorsPart2 = getColumnErrors(message);
if(columnErrorsPart2.isEmpty())
{
//Success
message.setCorrectedBitCount(rowErrors.size());
}
else
{
//Reverse the row error correction
for(Integer index: rowErrors)
{
message.flip(index);
}
//Step 2: correct column errors and recalculate row errors (should be zero)
for(Integer index: columnErrors)
{
message.flip(index);
}
List<Integer> rowErrorsPart2 = getRowErrors(message);
if(rowErrorsPart2.isEmpty())
{
//Success
message.setCorrectedBitCount(columnErrors.size());
}
else
{
//Reverse the column error correction
for(Integer index: columnErrors)
{
message.flip(index);
}
//Set the error count greater than the maximum correctable column errors (16)
message.setCorrectedBitCount(16);
}
}
}
}
}
/**
* Indicates if the errors detected by the row protected words match the errors detected by the column protected words
* @param rowErrors bit positions
* @param columnErrors bit positions
* @return true if they have the same quantity of bit position errors and the bit positions match
*/
private static boolean matches(List<Integer> rowErrors, List<Integer> columnErrors)
{
if(rowErrors.size() == columnErrors.size())
{
for(int x = 0; x < rowErrors.size(); x++)
{
if(!rowErrors.get(x).equals(columnErrors.get(0)))
{
return false;
}
}
return true;
}
else
{
return false;
}
}
public static void main(String[] args)
{
String raw = "0000000001100010100101100011100100110100110101000100000011101001110001001000100001001010010100001000011111011000000011110000111000111111100011000011100001100000000101001010000100010111010100111001";
System.out.println("RAW:" + raw);
CorrectedBinaryMessage message = new CorrectedBinaryMessage(BinaryMessage.load(raw));
System.out.println("LOD:" + message.toString());
CorrectedBinaryMessage extracted = extract(message);
System.out.println("EXT:" + extracted.toString());
}
}

View File

@@ -0,0 +1,559 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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 org.apache.commons.lang3.Validate;
import org.apache.commons.math3.util.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Berlekemp Massey decoder for primitive RS/BCH block codes
*
* Original C implementation by Simon Rockliff, 26 June 1991
*/
public class BerlekempMassey
{
public final static Logger mLog = LoggerFactory.getLogger(BerlekempMassey.class);
/* Golay field size GF( 2 ** MM ) */
private int MM;
/* Codeword Length: NN = 2 ** MM - 1 */
private int NN;
private int MAX_NN;
/* Hamming distance between codewords: NN - KK + 1 = 2 * TT + 1 */
private int KK;
/* Maximum number of errors that can be corrected */
int TT;
int[] alpha_to;
int[] index_of;
int[] gg;
/**
* Constructs an instance
*
* @param galoisFieldSize as a power of 2, e.g. for a GF(2^6) the parameter would be 6
* @param maxCorrectableErrors is Hamming Distance / 2. e.g. RS(24,16,9) would be (24 - 16) / 2, or floor(9/2).
* @param generatorPolynomial for the Galois Field represented in binary as 0/1 integers where the array is in
* big-endian format. For example: polynomial a6 + a1 + 1 is 1000011, as big endian: 1100001, and in array
* form as {1,1,0,0,0,0,1}
*/
public BerlekempMassey(int galoisFieldSize, int maxCorrectableErrors, int[] generatorPolynomial)
{
//TODO: fix this
MM = galoisFieldSize;
NN = (int)FastMath.pow(2, MM) - 1;
TT = maxCorrectableErrors;
KK = NN - 2 * TT;
}
public BerlekempMassey(int bitSize, int n, int k, int[] generatorPolynomial)
{
Validate.isTrue(bitSize == generatorPolynomial.length - 1,
"Generator polynomial length must be: bitSize + 1");
MM = bitSize;
NN = n;
KK = k;
TT = (int)Math.floor((NN - KK) / 2);
//Since this may be a shortened code, setup arrays to the max size of NN
int maxNN = (int)Math.pow(2, MM) - 1;
alpha_to = new int[maxNN + 1];
index_of = new int[maxNN + 1];
gg = new int[NN - KK + 1];
generate_gf(generatorPolynomial);
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 */
l[u + 1] = FastMath.max(l[u], 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 */
if(l[u] >= 0)
{
System.arraycopy(elp[u], 1, reg, 1, l[u]);
}
count = 0;
for(int i = 1; i <= NN; i++)
{
q = 1;
for(int j = 1; j <= l[u]; j++)
{
if(reg[j] != -1)
{
reg[j] = (reg[j] + j) % NN;
q ^= alpha_to[reg[j]];
}
;
}
if(q == 0) /* store root and error location number indices */
{
root[count] = i;
loc[count] = NN - i;
count++;
}
;
}
;
if(count == l[u]) /* no. roots = degree of elp hence <= tt errors */
{
/* form polynomial z(x) */
for(int i = 1; i <= l[u]; i++) /* Z[0] = 1 always - do not need */
{
if((s[i] != -1) && (elp[u][i] != -1))
{
z[i] = alpha_to[s[i]] ^ alpha_to[elp[u][i]];
}
else if((s[i] != -1) && (elp[u][i] == -1))
{
z[i] = alpha_to[s[i]];
}
else if((s[i] == -1) && (elp[u][i] != -1))
{
z[i] = alpha_to[elp[u][i]];
}
else
{
z[i] = 0;
}
for(int j = 1; j < i; j++)
{
if((s[j] != -1) && (elp[u][i - j] != -1))
{
z[i] ^= alpha_to[(elp[u][i - j] + s[j]) % NN];
}
}
z[i] = index_of[z[i]]; /* put into index form */
}
;
/* evaluate errors at locations given by error location numbers loc[i] */
for(int i = 0; i < NN; i++)
{
err[i] = 0;
if(output[i] != -1) /* convert recd[] to polynomial form */
{
output[i] = alpha_to[output[i]];
}
else
{
output[i] = 0;
}
}
for(int i = 0; i < l[u]; i++) /* compute numerator of error term first */
{
err[loc[i]] = 1; /* accounts for z[0] */
for(int j = 1; j <= l[u]; j++)
{
if(z[j] != -1)
{
err[loc[i]] ^= alpha_to[(z[j] + j * root[i]) % NN];
}
}
if(err[loc[i]] != 0)
{
err[loc[i]] = index_of[err[loc[i]]];
q = 0; /* form denominator of error term */
for(int j = 0; j < l[u]; j++)
{
if(j != i)
{
q += index_of[1 ^ alpha_to[(loc[j] + root[i]) % NN]];
}
}
q = q % NN;
err[loc[i]] = alpha_to[(err[loc[i]] - q + NN) % NN];
output[loc[i]] ^= err[loc[i]]; /*recd[i] must be in polynomial form */
}
}
}
else
{
/* no. roots != degree of elp => >tt errors and cannot solve */
irrecoverable_error = true;
}
}
else
{
/* elp has degree >tt hence cannot solve */
irrecoverable_error = true;
}
}
else
{
/* no non-zero syndromes => no errors: output received codeword */
for(int i = 0; i < NN; i++)
{
if(output[i] != -1) /* convert recd[] to polynomial form */
{
output[i] = alpha_to[output[i]];
}
else
{
output[i] = 0;
}
}
}
if(irrecoverable_error)
{
for(int i = 0; i < NN; i++) /* could return error flag if desired */
{
if(output[i] != -1) /* convert recd[] to polynomial form */
{
output[i] = alpha_to[output[i]];
}
else
{
output[i] = 0; /* just output received codeword as is */
}
}
}
return irrecoverable_error;
}
}

View File

@@ -1,521 +0,0 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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 org.apache.commons.math3.util.FastMath;
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 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 */
l[u + 1] = FastMath.max(l[u], 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 */
if (l[u] >= 0) System.arraycopy(elp[u], 1, reg, 1, l[u]);
count = 0;
for( int i = 1; i <= NN; i++ )
{
q = 1;
for( int j = 1; j <= l[u]; j++ )
{
if( reg[ j ] != -1 )
{
reg[ j ] = ( reg[ j ] + j ) % NN;
q ^= alpha_to[ reg[ j ] ];
};
}
if( q == 0 ) /* store root and error location number indices */
{
root[ count ] = i;
loc[ count ] = NN - i;
count++;
};
};
if( count == l[ u ] ) /* no. roots = degree of elp hence <= tt errors */
{
/* form polynomial z(x) */
for( int i = 1; i <= l[ u ]; i++ ) /* Z[0] = 1 always - do not need */
{
if( ( s[ i ] != -1 ) && ( elp[ u ][ i ] != -1 ) )
{
z[ i ] = alpha_to[ s[ i ] ] ^ alpha_to[ elp[ u ][ i ] ];
}
else if( ( s[ i ] != -1 ) && ( elp[ u ][ i ] == -1 ) )
{
z[ i ] = alpha_to[ s[ i ] ];
}
else if( ( s[ i ] == -1 ) && ( elp[ u ][ i ] != -1 ) )
{
z[ i ] = alpha_to[ elp[ u ][ i ] ];
}
else
{
z[ i ] = 0;
}
for( int j = 1; j < i; j++ )
{
if( ( s[ j ] != -1 ) && ( elp[ u ][ i - j ] != -1 ) )
{
z[ i ] ^= alpha_to[ ( elp[ u ][ i - j ] + s[ j ] ) % NN ];
}
}
z[ i ] = index_of[ z[ i ] ]; /* put into index form */
};
/* evaluate errors at locations given by error location numbers loc[i] */
for( int i = 0; i < NN; i++ )
{
err[ i ] = 0;
if( output[ i ] != -1 ) /* convert recd[] to polynomial form */
{
output[ i ] = alpha_to[ output[ i ] ];
}
else
{
output[ i ] = 0;
}
}
for( int i = 0; i < l[ u ]; i++ ) /* compute numerator of error term first */
{
err[ loc[ i ] ] = 1; /* accounts for z[0] */
for( int j = 1; j <= l[ u ]; j++ )
{
if( z[ j ] != -1 )
{
err[ loc[ i ] ] ^= alpha_to[ ( z[ j ] + j * root[ i ] ) % NN ];
}
}
if( err[ loc[ i ] ] != 0 )
{
err[ loc[ i ] ] = index_of[ err[ loc[ i ] ] ];
q = 0; /* form denominator of error term */
for (int j = 0; j < l[u]; j++)
{
if (j != i)
{
q += index_of[1 ^ alpha_to[(loc[j] + root[i]) % NN]];
}
}
q = q % NN;
err[loc[i]] = alpha_to[(err[loc[i]] - q + NN) % NN];
output[loc[i]] ^= err[loc[i]]; /*recd[i] must be in polynomial form */
}
}
}
else
{
/* no. roots != degree of elp => >tt errors and cannot solve */
irrecoverable_error = true;
}
}
else
{
/* elp has degree >tt hence cannot solve */
irrecoverable_error = true;
}
}
else
{
/* no non-zero syndromes => no errors: output received codeword */
for (int i = 0; i < NN; i++)
{
if (output[i] != -1) /* convert recd[] to polynomial form */
{
output[i] = alpha_to[output[i]];
}
else
{
output[i] = 0;
}
}
}
if( irrecoverable_error )
{
for (int i = 0; i < NN; i++) /* could return error flag if desired */
{
if (output[i] != -1) /* convert recd[] to polynomial form */
{
output[i] = alpha_to[output[i]];
}
else
{
output[i] = 0; /* just output received codeword as is */
}
}
}
return irrecoverable_error;
}
}

View File

@@ -0,0 +1,290 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 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 io.github.dsheirer.bits.CorrectedBinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DMR CRC check/correction methods
*/
public class CRCDMR
{
private final static Logger mLog = LoggerFactory.getLogger(CRCDMR.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[]
{
0xE434, 0x721A, 0x390D, 0x9496, 0x4A4B, 0xAD35, 0xDE8A, 0x6F45, 0xBFB2,
0x5FD9, 0xA7FC, 0x53FE, 0x29FF, 0x9CEF, 0xC667, 0xEB23, 0xFD81, 0xF6D0,
0x7B68, 0x3DB4, 0x1EDA, 0xF6D, 0x8FA6, 0x47D3, 0xABF9, 0xDDEC, 0x6EF6,
0x377B, 0x93AD, 0xC1C6, 0x60E3, 0xB861, 0xD420, 0x6A10, 0x3508, 0x1A84,
0xD42, 0x6A1, 0x8B40, 0x45A0, 0x22D0, 0x1168, 0x8B4, 0x45A, 0x22D, 0x8906,
0x4483, 0xAA51, 0xDD38, 0x6E9C, 0x374E, 0x1BA7, 0x85C3, 0xCAF1, 0xED68,
0x76B4, 0x3B5A, 0x1DAD, 0x86C6, 0x4363, 0xA9A1, 0xDCC0, 0x6E60, 0x3730,
0x1B98, 0xDCC, 0x6E6, 0x373, 0x89A9, 0xCCC4, 0x6662, 0x3331, 0x9188,
0x48C4, 0x2462, 0x1231, 0x8108, 0x4084, 0x2042, 0x1021, 0x1, 0x2, 0x4, 0x8,
0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000,
0x8000,
};
/**
* Confirmed Packet Data Unit CRC-9 checksums, generated by:
*
* long[] table = CRCUtil.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,
0x032, 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
};
/**
* 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;
}
/**
* Calculates the CRC checksum for the specified message
* @param message to check
* @param start of the message
* @param length of the message (assumes the CRC checksum follows)
* @param checksums to use in calculating the message checksum
* @return calculated checksum
*/
public static int calculate(BinaryMessage message, int start, int length, long[] checksums)
{
int calculated = 0;
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(start); i >= start && i < length; i = message.nextSetBit(i + 1))
{
calculated ^= checksums[i - start];
}
return calculated;
}
/**
* Error detection and correction of single-bit errors for CCITT 16-bit CRC protected 80-bit messages.
*/
public static int correctCCITT80(CorrectedBinaryMessage message, int messageStart, int crcStart, int mask)
{
int calculated = mask; //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)
{
return 0;
}
else
{
int errorLocation = getBitError(residual, CCITT_80_CHECKSUMS);
if(errorLocation >= 0)
{
message.flip(errorLocation + messageStart);
message.incrementCorrectedBitCount(1);
return 1;
}
}
//Message has at least 2 bit errors - ie uncorrectable
message.incrementCorrectedBitCount(2);
return 2;
}
/**
* 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;
if(residual == 0 || residual == 0x1FF)
{
return CRC.PASSED;
}
return CRC.FAILED_CRC;
}
/**
* 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 int crc8(BinaryMessage bits, int len) {
int crc=0;
final int K = 8;
final boolean[] poly = new boolean[]{true,false,false,false,false,false,true,true,true}; // crc8 poly
boolean[] buf = new boolean[256];
if (len+K > 256) {
return 0;
}
for (int i=0; i<len; i++){
buf[i] = bits.get(i);
}
for (int i=0; i<len; i++)
if (buf[i])
for (int j=0; j<K+1; j++)
buf[i+j] ^= poly[j];
for (int i=0; i<K; i++){
crc = (crc << 1) + (buf[len + i] ? 1:0);
}
return crc;
}
}

View File

@@ -1,17 +1,17 @@
/*******************************************************************************
* 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/>
******************************************************************************/
@@ -23,217 +23,219 @@ import org.slf4j.LoggerFactory;
public class CRCUtil
{
private final static Logger mLog = LoggerFactory.getLogger( CRCUtil.class );
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;
}
public static long[] generate(int messageSize,
int crcSize,
long polynomial,
long initialFill,
boolean includeCRCBitErrors)
{
long[] crcTable = new long[messageSize +
(includeCRCBitErrors ? crcSize : 0)];
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;
}
int[] checksumIndexes = new int[crcSize];
/**
* 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 );
for(int x = 0; x < crcSize; x++)
{
checksumIndexes[x] = messageSize + x;
}
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 };
for(int x = 0; x < messageSize; x++)
{
BinaryMessage message = new BinaryMessage(messageSize + crcSize);
/* 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;
}
message.load(messageSize, crcSize, initialFill);
/**
* 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() );
message.set(x);
long[] table = CRCUtil.generate( 135, 9, 0x259l, 0x1FF, false, Parity.NONE );
mLog.debug( toCodeArray( table ) );
mLog.debug( "Finished" );
}
public static String toCodeArray( long[] values )
{
boolean integerArray = true;
message = decode(message, 0, messageSize, polynomial, crcSize);
/* Determine the correct primitive type: int or long */
for( long value: values )
{
if( value > Integer.MAX_VALUE || value < Integer.MIN_VALUE )
{
integerArray = false;
break;
}
}
long checksum = message.getLong(checksumIndexes);
StringBuilder sb = new StringBuilder();
crcTable[x] = checksum;
}
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" );
if(includeCRCBitErrors)
{
for(int x = 0; x < crcSize; x++)
{
crcTable[messageSize + x] = Long.rotateLeft(1, x);
}
}
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 crcTable;
}
return sb.toString();
}
/**
* 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);
System.out.println(message.toString());
}
return message;
}
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();
}
public static void main(String[] args)
{
mLog.debug("Starting");
//DMR message
// String raw = "100110010001000000001001000000001111011011010000011001010000000000000000000000001100001000001011";
String raw = "101010000000000000110110100000101101011011010000011001111100000000000011100101001001111010001110";
mLog.debug(raw);
raw = "101010000000000000110110100000101101011011010000011001111100000000000011100101000000000000000000";
BinaryMessage message = BinaryMessage.load(raw);
mLog.debug(message.toString());
long polynomial = 0x11021l;
decode(message, 0, 80, polynomial, 16);
mLog.debug(message.toString());
mLog.debug("Finished");
}
}

View File

@@ -0,0 +1,129 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Implements Hamming(13,9,3) Error Detection algorithm
*/
public class Hamming13
{
//DMR Checksums from generator matrix TS 102 361-1 Table B.14
private static int[] CHECKSUMS = new int[]{0xF, 0xE, 0x7, 0xA, 0x5, 0XB, 0xC, 0x6, 0x3};
private static int[] ERROR_INDEX = new int[]{-1, 12, 11, 8, 10, 4, 7, 2, 9, -1, 3, 5, 6, -1, 1, 0, -1};
/**
* Calculates the bit error index of the Hamming(13,9,3) protected word that is contained in the binary message
* starting at the specified offset.
*
* @param message containing a Hamming protected word
* @param offset to the start of the protected word
* @return message index for an error bit or -1 if no errors are detected.
*/
public static int getErrorIndex(BinaryMessage message, int offset)
{
int syndrome = getSyndrome(message, offset);
if(syndrome > 0)
{
return offset + ERROR_INDEX[syndrome];
}
return -1;
}
/**
* Calculates the bit error index of the Hamming(13,9,3) protected word that is contained in the binary message
* at the specified indices.
*
* @param message containing the Hamming protected word
* @param indices to the word
* @return index of error bit or -1 if no errors are detected
*/
public static int getErrorIndex(BinaryMessage message, int[] indices)
{
int syndrome = getSyndrome(message, indices);
if(syndrome > 0)
{
int errorIndex = ERROR_INDEX[syndrome];
if(errorIndex >= 0 && errorIndex < indices.length)
{
return indices[errorIndex];
}
else
{
return 10000 + errorIndex;
}
}
return -1;
}
/**
* Calculates the parity checksum (Parity 8,4,2,1) for data (9 <> 1 ) bits.
*
* @param message containing Hamming(15) protected word
* @param offset to the Hamming protected word
* @return parity value, 0 - 15
*/
private static int calculateChecksum(BinaryMessage message, int offset)
{
int calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(offset); i >= offset && i < offset + 9; i = message.nextSetBit(i + 1))
{
calculated ^= CHECKSUMS[i - offset];
}
return calculated;
}
private static int calculateChecksum(BinaryMessage message, int[] indices)
{
int calculated = 0; //Starting value
for(int x = 0; x < 9; x++)
{
if(message.get(indices[x]))
{
calculated ^= CHECKSUMS[x];
}
}
return calculated;
}
private static int getSyndrome(BinaryMessage message, int[] indices)
{
int calculated = calculateChecksum(message, indices);
int checksum = 0;
for(int x = 9; x < 13; x++)
{
checksum = Integer.rotateLeft(checksum, 1);
if(message.get(indices[x]))
{
checksum++;
}
}
return calculated ^ checksum;
}
/**
* Calculates the syndrome as the xor of the calculated checksum and the actual checksum.
*
* @param message containing a hamming(15,11,3) protected word
* @param offset to bit 0 of the hamming protected word
* @return syndrome that can be used with the ERROR_INDEX error to find the index of the bit position error
*/
private static int getSyndrome(BinaryMessage message, int offset)
{
int calculated = calculateChecksum(message, offset);
int checksum = message.getInt(offset + 9, offset + 12);
return (checksum ^ calculated);
}
}

View File

@@ -2,116 +2,66 @@ package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Implements Hamming(15,11,3) Error Detection algorithm
*/
public class Hamming15
{
private static int[] CHECKSUMS = new int[] { 0xF,0xE,0xD,0xC,0xB,0XA,0x9,
0x7,0x6,0x5,0x3 };
//DMR Checksums from generator matrix TS 102 361-1 Table B.15
private static int[] CHECKSUMS = new int[]{0x9, 0xD, 0xF, 0xE, 0x7, 0XA, 0x5, 0xB, 0xC, 0x6, 0x3};
private static int[] ERROR_INDEX = new int[]{-1, 14, 13, 10, 12, 6, 9, 4, 11, 0, 5, 7, 8, 1, 3, 2};
/**
* 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 );
/**
* Calculates the bit error index of the Hamming(15,11,3) protected word that is contained in the binary message
* starting at the specified offset.
*
* @param message containing a Hamming protected word
* @param offset to the start of the protected word
* @return message index for an error bit or -1 if no errors are detected.
*/
public static int getErrorIndex(BinaryMessage message, int offset)
{
int syndrome = getSyndrome(message, offset);
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;
}
if(syndrome > 0)
{
return offset + ERROR_INDEX[syndrome];
}
/* We'll never get to here */
return 2;
}
return -1;
}
/**
* 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
/**
* Calculates the parity checksum (Parity 8,4,2,1) for data (11 <> 1 ) bits.
*
* @param message containing Hamming(15) protected word
* @param offset to the Hamming protected word
* @return parity value, 0 - 15
*/
private static int calculateChecksum(BinaryMessage message, int offset)
{
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;
}
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(offset); i >= offset && i < offset + 11; i = message.nextSetBit(i + 1))
{
calculated ^= CHECKSUMS[i - offset];
}
/**
* 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 calculated;
}
return ( checksum ^ calculated );
}
/**
* Calculates the syndrome as the xor of the calculated checksum and the actual checksum.
*
* @param message containing a hamming(15,11,3) protected word
* @param offset to bit 0 of the hamming protected word
* @return syndrome that can be used with the ERROR_INDEX error to find the index of the bit position error
*/
private static int getSyndrome(BinaryMessage message, int offset)
{
int calculated = calculateChecksum(message, offset);
int checksum = message.getInt(offset + 11, offset + 14);
return (checksum ^ calculated);
}
}

View File

@@ -0,0 +1,120 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Hamming(16,11,5) Error detection and correction utility.
*/
public class Hamming16
{
private static int[] CHECKSUMS = new int[]{0x13, 0x1A, 0x1F, 0x1C, 0x0E, 0x15, 0x0B, 0x16, 0x19, 0x0D, 0x07};
/**
* Performs error detection and correction of any single-bit errors and detection of any double-bit errors (SECDED)
*
* @param frame - binary frame containing a hamming(16,11,5) protected field
* @param startIndex - offset to the first bit of the field
* @return - 0 = no errors
* 1 = a single-bit error was detected and 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 + 15); //Parity 1
return 1;
case 2:
frame.flip(startIndex + 14); //Parity 2
return 1;
case 3:
frame.flip(startIndex + 10); //Data 1
return 1;
case 4:
frame.flip(startIndex + 13); //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 + 12); //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;
case 16:
frame.flip(startIndex + 11); //Parity 16
return 1;
default:
return 2;
}
}
/**
* Calculates the checksum (Parity 16,8,4,2,1) for data (11 <> 1 ) bits.
*
* @param frame - frame containing hamming protected word
* @param startIndex - start bit index of the hamming protected word
* @return parity value, 0 - 16
*/
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(16,11,5) 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 + 15);
return (checksum ^ calculated);
}
}

View File

@@ -0,0 +1,123 @@
package io.github.dsheirer.edac;
import io.github.dsheirer.bits.BinaryMessage;
/**
* Hamming(17,12,3) Error detection and correction utility.
*/
public class Hamming17
{
private static int[] CHECKSUMS = new int[]{0x1B, 0x1F, 0x1D, 0x1C, 0x0E, 0x07, 0x11, 0x1A, 0x0D, 0x14, 0x0A, 0x05};
/**
* Performs error detection and correction of any single-bit errors and detection of any double-bit errors (SECDED)
*
* @param frame - binary frame containing a hamming(17,12,5) protected field
* @param startIndex - offset to the first bit of the field
* @return - 0 = no errors
* 1 = a single-bit error was detected and 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 + 16); //Parity 1
return 1;
case 2:
frame.flip(startIndex + 15); //Parity 2
return 1;
case 3:
frame.flip(startIndex + 11); //Data 1
return 1;
case 4:
frame.flip(startIndex + 14); //Parity 4
return 1;
case 5:
frame.flip(startIndex + 10); //Data 2
return 1;
case 6:
frame.flip(startIndex + 9); //Data 3
return 1;
case 7:
frame.flip(startIndex + 8); //Data 4
return 1;
case 8:
frame.flip(startIndex + 13); //Parity 8
return 1;
case 9:
frame.flip(startIndex + 7); //Data 5
return 1;
case 10:
frame.flip(startIndex + 6); //Data 6
return 1;
case 11:
frame.flip(startIndex + 5); //Data 7
return 1;
case 12:
frame.flip(startIndex + 4); //Data 8
return 1;
case 13:
frame.flip(startIndex + 3); //Data 9
return 1;
case 14:
frame.flip(startIndex + 2); //Data 10
return 1;
case 15:
frame.flip(startIndex + 1); //Data 11
return 1;
case 16:
frame.flip(startIndex + 12); //Parity 16
return 1;
case 17:
frame.flip(startIndex); //Data 12
return 1;
default:
return 2;
}
}
/**
* Calculates the checksum (Parity 16,8,4,2,1) for data (12 <> 1 ) bits.
*
* @param frame - frame containing hamming protected word
* @param startIndex - start bit index of the hamming protected word
* @return parity value, 0 - 16
*/
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;
}
/**
* Calculates the syndrome - xor of the calculated checksum and the actual
* checksum.
*
* @param frame - binary frame containing a hamming(17,12,5) 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 + 12, startIndex + 16);
return (checksum ^ calculated);
}
}

View File

@@ -0,0 +1,109 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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.CorrectedBinaryMessage;
/**
* Berlecamp-Massey decoder for Reed Solomon RS(12,9,4) protected messages using DMR generator polynomial
*/
public class ReedSolomon_12_9_4_DMR extends ReedSolomon_255_DMR
{
private static final int[] FLC_CODEWORD_0 = {0, 1, 2, 3, 4, 5, 6, 7};
private static final int[] FLC_CODEWORD_1 = {8, 9, 10, 11, 12, 13, 14, 15};
private static final int[] FLC_CODEWORD_2 = {16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] FLC_CODEWORD_3 = {24, 25, 26, 27, 28, 29, 30, 31};
private static final int[] FLC_CODEWORD_4 = {32, 33, 34, 35, 36, 37, 38, 39};
private static final int[] FLC_CODEWORD_5 = {40, 41, 42, 43, 44, 45, 46, 47};
private static final int[] FLC_CODEWORD_6 = {48, 49, 50, 51, 52, 53, 54, 55};
private static final int[] FLC_CODEWORD_7 = {56, 57, 58, 59, 60, 61, 62, 63};
private static final int[] FLC_CODEWORD_8 = {64, 65, 66, 67, 68, 69, 70, 71};
private static final int[] FLC_PARITY_0 = {72, 73, 74, 75, 76, 77, 78, 79};
private static final int[] FLC_PARITY_1 = {80, 81, 82, 83, 84, 85, 86, 87};
private static final int[] FLC_PARITY_2 = {88, 89, 90, 91, 92, 93, 94, 95};
/**
* Constructs an instance
*/
public ReedSolomon_12_9_4_DMR()
{
//Note: the RS(12,9,4) is a shortened form of RS(255,252,4) over GF(8)
super(255, 252);
}
/**
* Attempts to correct the Full Link Control message returning true if there were zero or one correctable errors
* and returning false if there were two or more errors detected.
*
* Note: if one bit error was detected and corrected, the corrected bit count is set to 1 in the message
*
* @param message to be corrected.
* @param crcMask as defined in the TS 102-361-1 Table B.21
* @return true if the message was corrected.
*/
public boolean correctFullLinkControl(CorrectedBinaryMessage message, int crcMask)
{
int[] input = new int[255];
int[] output = new int[255];
input[0] = message.getInt(FLC_PARITY_2) ^ crcMask;
input[1] = message.getInt(FLC_PARITY_1) ^ crcMask;
input[2] = message.getInt(FLC_PARITY_0) ^ crcMask;
input[3] = message.getInt(FLC_CODEWORD_8);
input[4] = message.getInt(FLC_CODEWORD_7);
input[5] = message.getInt(FLC_CODEWORD_6);
input[6] = message.getInt(FLC_CODEWORD_5);
input[7] = message.getInt(FLC_CODEWORD_4);
input[8] = message.getInt(FLC_CODEWORD_3);
input[9] = message.getInt(FLC_CODEWORD_2);
input[10] = message.getInt(FLC_CODEWORD_1);
input[11] = message.getInt(FLC_CODEWORD_0);
try
{
boolean irrecoverableErrors = decode(input, output);
if(irrecoverableErrors)
{
return false;
}
}
catch(Exception e)
{
return false;
}
int pointer = 0;
for(int x = 11; x >= 0; x--)
{
if(output[x] != -1 && (output[x] != input[x]))
{
message.load(pointer, 8, output[x]);
message.setCorrectedBitCount(1);
return true;
}
pointer += 8;
}
return true;
}
}

View File

@@ -20,21 +20,18 @@
package io.github.dsheirer.edac;
public class ReedSolomon_63_47_17 extends BerlekempMassey_63
/**
* Reed Solomon RS(24,12,13) decoder over GF(6)
*/
public class ReedSolomon_24_12_13_P25 extends ReedSolomon_63_P25
{
/**
* Reed-Solomon RS(63,47,17) decoder. This can also be used for error detection
* and correction of the following RS codes:
* Constructs an instance.
*
* 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.
* Note: this is a shortened form of RS(63,51,13)
*/
public ReedSolomon_63_47_17(int maximumCorrectableErrors)
public ReedSolomon_24_12_13_P25()
{
super(maximumCorrectableErrors);
super(63,51);
}
}

View File

@@ -0,0 +1,37 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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;
/**
* Reed Solomon RS(24,16,9) decoder over GF(6)
*/
public class ReedSolomon_24_16_9_P25 extends ReedSolomon_63_P25
{
/**
* Constructs an instance.
*
* Note: this is a shortened form of RS(63,55,9)
*/
public ReedSolomon_24_16_9_P25()
{
super(63,55);
}
}

View File

@@ -0,0 +1,44 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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;
/**
* Berlekemp Massey decoder for GF(8) using DMR Generator Polynomial
*/
public class ReedSolomon_255_DMR extends BerlekempMassey
{
/**
* DMR Reed-Solomon code is generated from a Galois Field(2^8) by the polynomial: a8 + a4 + a3 + a2 + a0.
* In binary, this is expressed as: 100011101 which is reversed to big-endian format for this algorithm
* See: TS 102-361-1 paragraph B.3.6 Reed-Solomon(12,9)
*/
public static final int[] DMR_GENERATOR_POLYNOMIAL = {1, 0, 1, 1, 1, 0, 0, 0, 1};
/**
* Constructs an instance of RS(total symbols, message symbols)
*
* @param n total symbols
* @param k message symbols
*/
public ReedSolomon_255_DMR(int n, int k)
{
super(8, n, k, DMR_GENERATOR_POLYNOMIAL);
}
}

View File

@@ -22,21 +22,17 @@
package io.github.dsheirer.edac;
public class ReedSolomon_63_35_29 extends BerlekempMassey_63
/**
* Reed Solomon Decoder for P25 RS(44,16,29) protected messages.
*/
public class ReedSolomon_44_16_29_P25 extends ReedSolomon_63_P25
{
/**
* Reed-Solomon RS(63,35,29) decoder. This can also be used for error detection and correction of the following
* RS codes:
*
* RS(46,26,21) - max 10 errors = P25P2 IEMI
* RS(45,26,20) - max 9 errors = P25P2 SOEMI (FACCH)
* RS(52,30,23) - max 11 errors = P25P2 IOEMI (SACCH)
* RS(44,16,29) - max 14 errors = P25P2 ESS
*
* The maximum correctable errors is determined by (n-k)/2, or hamming distance divided by 2.
* Constructs an instance
*/
public ReedSolomon_63_35_29(int maximumCorrectableErrors)
public ReedSolomon_44_16_29_P25()
{
super(maximumCorrectableErrors);
//The RS(44,16,29) is shortened from RS(63,35,29)
super(63,35);
}
}

View File

@@ -22,13 +22,16 @@
package io.github.dsheirer.edac;
public class ReedSolomon_44_16_29 extends BerlekempMassey_63
/**
* Reed Solomon decoder for APCO-25 RS(63,35,29) over GF(6)
*/
public class ReedSolomon_63_35_29_P25 extends ReedSolomon_63_P25
{
//Hamming distance = 29, so max correctable errors = 29 / 2 = 14
private static final int MAXIMUM_CORRECTABLE_ERRORS = 14;
public ReedSolomon_44_16_29()
/**
* Reed-Solomon RS(63,35,29) decoder.
*/
public ReedSolomon_63_35_29_P25()
{
super(MAXIMUM_CORRECTABLE_ERRORS);
super(63, 35);
}
}

View File

@@ -0,0 +1,32 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 class ReedSolomon_63_47_17_P25 extends ReedSolomon_63_P25
{
/**
* Reed-Solomon RS(63,47,17) decoder.
*/
public ReedSolomon_63_47_17_P25()
{
super(63,47);
}
}

View File

@@ -0,0 +1,44 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 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;
/**
* Berlekemp Massey decoder using APCO-25 Generator Polynomial over GF(6)
*/
public class ReedSolomon_63_P25 extends BerlekempMassey
{
/**
* APCO-25 Reed-Solomon code is generated from a Galois Field(2^6) by the polynomial: a6 + a1 + a0.
* In binary, this is expressed as: 1000011 which is reversed to big-endian format for this algorithm
* See: TIA 102-BAAA paragraph 5.9 Reed-Solomon Code Generator Matrices
*/
public static final int[] P25_GENERATOR_POLYNOMIAL = { 1, 1, 0, 0, 0, 0, 1 };
/**
* Constructs an instance
*
* @param n total symbols
* @param k message symbols
*/
public ReedSolomon_63_P25(int n, int k)
{
super(6, n, k, P25_GENERATOR_POLYNOMIAL);
}
}

View File

@@ -83,4 +83,10 @@ public class DMR_3_4_Node extends Node
{
return DMR_TRANSITION_MATRIX[precedingNode.getInputValue()][getInputValue()];
}
@Override
public String toString()
{
return "IN:" + getInputValue() + " OUT:" + getTransmittedOutputValue();
}
}

View File

@@ -18,6 +18,8 @@
******************************************************************************/
package io.github.dsheirer.edac.trellis;
import com.google.common.base.Joiner;
import java.util.ArrayList;
import java.util.List;
@@ -128,4 +130,10 @@ public class Path
{
return new Path(this);
}
@Override
public String toString()
{
return "ERR:" + getError() + " " + Joiner.on(",").join(mNodes);
}
}

View File

@@ -18,21 +18,77 @@
******************************************************************************/
package io.github.dsheirer.edac.trellis;
import com.google.common.base.Joiner;
import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Viterbi decoder for 3/4 rate trellis coded modulation (TCM) data blocks
*/
public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
{
private static final int[] DEINTERLEAVE_DIBITS = new int[]{0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81,
88, 89, 96, 97, 2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, 67, 74, 75, 82, 83, 90, 91, 4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45,
52, 53, 60, 61, 68, 69, 76, 77, 84, 85, 92, 93, 6, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 54, 55, 62, 63, 70, 71, 78, 79, 86, 87, 94, 95};
private static final int[] DEINTERLEAVE_INDEXES = new int[]{0, 1, 2, 3, 16, 17, 18, 19, 32, 33, 34, 35, 48, 49, 50,
51, 64, 65, 66, 67, 80, 81, 82, 83, 96, 97, 98, 99, 112, 113, 114, 115, 128, 129, 130, 131, 144, 145, 146, 147,
160, 161, 162, 163, 176, 177, 178, 179, 192, 193, 194, 195, 4, 5, 6, 7, 20, 21, 22, 23, 36, 37, 38, 39, 52, 53,
54, 55, 68, 69, 70, 71, 84, 85, 86, 87, 100, 101, 102, 103, 116, 117, 118, 119, 132, 133, 134, 135, 148, 149,
150, 151, 164, 165, 166, 167, 180, 181, 182, 183, 8, 9, 10, 11, 24, 25, 26, 27, 40, 41, 42, 43, 56, 57, 58, 59,
72, 73, 74, 75, 88, 89, 90, 91, 104, 105, 106, 107, 120, 121, 122, 123, 136, 137, 138, 139, 152, 153, 154, 155,
168, 169, 170, 171, 184, 185, 186, 187, 12, 13, 14, 15, 28, 29, 30, 31, 44, 45, 46, 47, 60, 61, 62, 63, 76, 77,
78, 79, 92, 93, 94, 95, 108, 109, 110, 111, 124, 125, 126, 127, 140, 141, 142, 143, 156, 157, 158, 159, 172,
173, 174, 175, 188, 189, 190, 191};
/**
* Viterbi decoder for Digital Mobile Radio (DMR) 3/4 rate Trellis Coded Modulation (TCM) encoded messages.
*/
public ViterbiDecoder_3_4_DMR()
{
super(3,4);
super(3, 4);
}
public static void main(String[] args)
{
List<Integer> indexes = new ArrayList<>();
for(int i : DEINTERLEAVE_DIBITS)
{
indexes.add(i * 2);
indexes.add(i * 2 + 1);
}
System.out.println(Joiner.on(",").join(indexes));
Collections.sort(indexes);
System.out.println(Joiner.on(",").join(indexes));
}
/**
* Performs deinterleave of transmitted 3/4 TCM encoded payload
* @param interleaved
* @return
*/
private static CorrectedBinaryMessage deinterleave(CorrectedBinaryMessage interleaved)
{
CorrectedBinaryMessage deinterleaved = new CorrectedBinaryMessage(196);
for(int x = 0; x < 196; x++)
{
if(interleaved.get(x))
{
deinterleaved.set(DEINTERLEAVE_INDEXES[x]);
}
}
return deinterleaved;
}
/**
* Decodes a 3/4 rate trellis coded modulation (TCM) encoded DMR binary message containing 196 bits that have
* already been deinterleaved.
@@ -40,12 +96,11 @@ public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
* @param encodedMessage to decode that has already been deinterleaved.
* @return decoded message
*/
public CorrectedBinaryMessage decode(BinaryMessage encodedMessage)
public CorrectedBinaryMessage decode(CorrectedBinaryMessage encodedMessage)
{
int[] symbols = getSymbols(encodedMessage);
CorrectedBinaryMessage deinterleaved = deinterleave(encodedMessage);
int[] symbols = getSymbols(deinterleaved);
Path mostLikelyPath = decode(symbols);
return getMessage(mostLikelyPath);
}
@@ -104,7 +159,7 @@ public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
//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 -" +
throw new IllegalArgumentException("Encoded message must contain an integral number of 4-bit symbols -" +
" message size: " + encodedMessage.size());
}
@@ -125,6 +180,7 @@ public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
/**
* Creates a DMR node
*
* @param inputValue for the specific trellis node
* @param transmittedOutputValue the actual transmitted output value
* @return created node
@@ -154,6 +210,6 @@ public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
@Override
protected Node createStartingNode()
{
return new DMR_3_4_Node(0,0);
return new DMR_3_4_Node(0, 0);
}
}