mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
* #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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
62
src/main/java/io/github/dsheirer/edac/BPTC_17_12_3.java
Normal file
62
src/main/java/io/github/dsheirer/edac/BPTC_17_12_3.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
284
src/main/java/io/github/dsheirer/edac/BPTC_196_96.java
Normal file
284
src/main/java/io/github/dsheirer/edac/BPTC_196_96.java
Normal 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());
|
||||
}
|
||||
}
|
||||
559
src/main/java/io/github/dsheirer/edac/BerlekempMassey.java
Normal file
559
src/main/java/io/github/dsheirer/edac/BerlekempMassey.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
290
src/main/java/io/github/dsheirer/edac/CRCDMR.java
Normal file
290
src/main/java/io/github/dsheirer/edac/CRCDMR.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
129
src/main/java/io/github/dsheirer/edac/Hamming13.java
Normal file
129
src/main/java/io/github/dsheirer/edac/Hamming13.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
120
src/main/java/io/github/dsheirer/edac/Hamming16.java
Normal file
120
src/main/java/io/github/dsheirer/edac/Hamming16.java
Normal 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);
|
||||
}
|
||||
}
|
||||
123
src/main/java/io/github/dsheirer/edac/Hamming17.java
Normal file
123
src/main/java/io/github/dsheirer/edac/Hamming17.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user