mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#1633 DMR Decoder redesign. Uses new DQPSK vector/scalar demodulator implementations and soft sync detectors for more accurate detections and symbol timing and symbol period calculations. Includes two new calibrations with scalar and vector implementations for DQPSK demodulator and Interpolator. Enhances DMR error detection and correction algorithms, specifically the block product turbo code algorithms. (#2115)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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;
|
||||
|
||||
/**
|
||||
* Block Product Turbo Code 16/2 for decoding a DMR Voice Frame F payload from the 32-bit EMB field.
|
||||
* <p>
|
||||
* See ETSI TS 102 361-1 B.2.2.1 and B.2.2.2
|
||||
*/
|
||||
public class BPTC_16_2
|
||||
{
|
||||
private static final int[] DEINTERLEAVE = new int[]{0, 24, 1, 25, 2, 26, 3, 27, 4, 28, 5, 29, 6, 30, 7, 31, 8, 16,
|
||||
9, 17, 10, 18, 11, 19, 12, 20, 13, 21, 14, 22, 15, 23};
|
||||
|
||||
/**
|
||||
* Unscramble and perform FEC checks per paragraph B.2.2.1 for Non-Reverse Channel Single Burst
|
||||
*
|
||||
* @param message with 32 interleaved bits.
|
||||
* @return descrambled and error checked message or null if the process fails or there are too many errors.
|
||||
*/
|
||||
public static CorrectedBinaryMessage decodeShortBurst(CorrectedBinaryMessage message)
|
||||
{
|
||||
CorrectedBinaryMessage deinterleaved = deinterleave(message);
|
||||
int fec = Hamming16.checkAndCorrect(deinterleaved, 0);
|
||||
|
||||
if(fec == 2) //0 or 1 is good, 2 = uncorrectable errors
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Check for even parity. Bits 0-15 should be the same as bits 16-31.
|
||||
for(int x = 0; x < 16; x++)
|
||||
{
|
||||
if(deinterleaved.get(x) ^ deinterleaved.get(x + 16))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return deinterleaved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unscramble and perform FEC checks per paragraph B.2.2.2 for Reverse Channel Single Burst
|
||||
*
|
||||
* @param binaryMessage with 32 interleaved bits.
|
||||
* @return descrambled and error checked message or null if the process fails or there are too many errors.
|
||||
*/
|
||||
public static CorrectedBinaryMessage decodeReverseChannel(CorrectedBinaryMessage message)
|
||||
{
|
||||
CorrectedBinaryMessage deinterleaved = deinterleave(message);
|
||||
System.out.println(" DEINTER: " + deinterleaved.toHexString());
|
||||
int fec = Hamming16.checkAndCorrect(deinterleaved, 0);
|
||||
System.out.println(" DECODED: " + deinterleaved.toHexString());
|
||||
System.out.println("FEC:" + fec);
|
||||
if(fec == 2) //0 or 1 is good, 2 = uncorrectable errors
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Check for odd parity. Bits 0-15 should be opposite of bits 16-31.
|
||||
for(int x = 0; x < 16; x++)
|
||||
{
|
||||
if(deinterleaved.get(x) == deinterleaved.get(x + 16))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return deinterleaved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs deinterleave of the interleaved message.
|
||||
*
|
||||
* @param original to deinterleave
|
||||
* @return deinterleaved message
|
||||
*/
|
||||
public static CorrectedBinaryMessage deinterleave(CorrectedBinaryMessage original)
|
||||
{
|
||||
CorrectedBinaryMessage delinterleaved = new CorrectedBinaryMessage(32);
|
||||
for(int x = 0; x < 32; x++)
|
||||
{
|
||||
if(original.get(x))
|
||||
{
|
||||
delinterleaved.set(DEINTERLEAVE[x]);
|
||||
}
|
||||
}
|
||||
|
||||
return delinterleaved;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
String[] msgs = new String[]{"05030A03", "35003A00", "1C6D2C9E"};
|
||||
|
||||
for(String msg : msgs)
|
||||
{
|
||||
CorrectedBinaryMessage original = new CorrectedBinaryMessage(BinaryMessage.loadHex(msg));
|
||||
System.out.println("ORIGINAL: " + original.toHexString());
|
||||
CorrectedBinaryMessage decoded = decodeReverseChannel(original);
|
||||
System.out.println("-------------------------");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.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; //However, we set the 3x reserved bits in 96, 97, and 98 making the length 99
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//Transfer bits R2, R1, and R0 to the end - RAS bits
|
||||
extracted.set(96, message.get(0));
|
||||
extracted.set(97, message.get(1));
|
||||
extracted.set(98, message.get(2));
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* Copyright (C) 2014-2024 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
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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;
|
||||
|
||||
/**
|
||||
* Implements Hamming(13,9,3) Error Detection algorithm
|
||||
@@ -8,8 +28,8 @@ import io.github.dsheirer.bits.BinaryMessage;
|
||||
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};
|
||||
private static int[] CHECKSUMS = new int[]{0xF, 0xE, 0x7, 0xA, 0x5, 0xB, 0xC, 0x6, 0x3, 0x8, 0x4, 0x2, 0x1};
|
||||
private static int[] ERROR_INDEX = new int[]{-1, 12, 11, 8, 10, 4, 7, 2, 9, -1, 3, 5, 6, -1, 1, 0, -1, -1};
|
||||
|
||||
/**
|
||||
* Calculates the bit error index of the Hamming(13,9,3) protected word that is contained in the binary message
|
||||
@@ -28,7 +48,7 @@ public class Hamming13
|
||||
return offset + ERROR_INDEX[syndrome];
|
||||
}
|
||||
|
||||
return -1;
|
||||
return IHamming.NO_ERRORS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,11 +73,11 @@ public class Hamming13
|
||||
}
|
||||
else
|
||||
{
|
||||
return 10000 + errorIndex;
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return IHamming.NO_ERRORS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +115,7 @@ public class Hamming13
|
||||
return calculated;
|
||||
}
|
||||
|
||||
private static int getSyndrome(BinaryMessage message, int[] indices)
|
||||
public static int getSyndrome(BinaryMessage message, int[] indices)
|
||||
{
|
||||
int calculated = calculateChecksum(message, indices);
|
||||
|
||||
@@ -126,4 +146,16 @@ public class Hamming13
|
||||
int checksum = message.getInt(offset + 9, offset + 12);
|
||||
return (checksum ^ calculated);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
String transmitted = "0000010000001";
|
||||
String correct = "0000000000000";
|
||||
String recommended = "0001000000001";
|
||||
// CorrectedBinaryMessage cbm = new CorrectedBinaryMessage(CorrectedBinaryMessage.load(transmitted));
|
||||
CorrectedBinaryMessage cbm = new CorrectedBinaryMessage(CorrectedBinaryMessage.load(correct));
|
||||
// CorrectedBinaryMessage cbm = new CorrectedBinaryMessage(CorrectedBinaryMessage.load(recommended));
|
||||
int syndrome = getSyndrome(cbm, 0);
|
||||
System.out.println("Syndrome " + syndrome);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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;
|
||||
@@ -8,7 +27,7 @@ import io.github.dsheirer.bits.BinaryMessage;
|
||||
public class Hamming15
|
||||
{
|
||||
//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[] CHECKSUMS = new int[]{0x9, 0xD, 0xF, 0xE, 0x7, 0xA, 0x5, 0xB, 0xC, 0x6, 0x3, 0x8, 0x4, 0x2, 0x1};
|
||||
private static int[] ERROR_INDEX = new int[]{-1, 14, 13, 10, 12, 6, 9, 4, 11, 0, 5, 7, 8, 1, 3, 2};
|
||||
|
||||
/**
|
||||
@@ -28,7 +47,7 @@ public class Hamming15
|
||||
return offset + ERROR_INDEX[syndrome];
|
||||
}
|
||||
|
||||
return -1;
|
||||
return IHamming.NO_ERRORS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +77,7 @@ public class Hamming15
|
||||
* @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)
|
||||
public static int getSyndrome(BinaryMessage message, int offset)
|
||||
{
|
||||
int calculated = calculateChecksum(message, offset);
|
||||
int checksum = message.getInt(offset + 11, offset + 14);
|
||||
|
||||
@@ -1,13 +1,63 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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;
|
||||
|
||||
/**
|
||||
* Hamming(16,11,5) Error detection and correction utility.
|
||||
* Hamming(16,11,4) Error detection and correction utility. This code is capable of correcting single-bit errors and
|
||||
* detecting (but not correcting) double-bit errors.
|
||||
*/
|
||||
public class Hamming16
|
||||
public class Hamming16 implements IHamming
|
||||
{
|
||||
private static int[] CHECKSUMS = new int[]{0x13, 0x1A, 0x1F, 0x1C, 0x0E, 0x15, 0x0B, 0x16, 0x19, 0x0D, 0x07};
|
||||
private static int[] CHECKSUMS = new int[]{0x13, 0x1A, 0x1F, 0x1C, 0x0E, 0x15, 0x0B, 0x16, 0x19, 0x0D, 0x07, 0x10,
|
||||
0x08, 0x04, 0x02, 0x01};
|
||||
|
||||
/**
|
||||
* Calculates the bit error index of the Hamming(16,11,4) 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 or 1000 if two bit errors are detected.
|
||||
*/
|
||||
public int getErrorIndex(BinaryMessage message, int offset)
|
||||
{
|
||||
int syndrome = getSyndrome(message, offset);
|
||||
|
||||
if(syndrome == 0)
|
||||
{
|
||||
return NO_ERRORS;
|
||||
}
|
||||
|
||||
for(int index = 0; index < CHECKSUMS.length; index++)
|
||||
{
|
||||
if(syndrome == CHECKSUMS[index])
|
||||
{
|
||||
return index + offset;
|
||||
}
|
||||
}
|
||||
|
||||
return MULTIPLE_ERRORS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs error detection and correction of any single-bit errors and detection of any double-bit errors (SECDED)
|
||||
@@ -22,78 +72,36 @@ public class Hamming16
|
||||
{
|
||||
int syndrome = getSyndrome(frame, startIndex);
|
||||
|
||||
switch(syndrome)
|
||||
if (syndrome == 0)
|
||||
{
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(int index = 0; index < CHECKSUMS.length; index++)
|
||||
{
|
||||
if(CHECKSUMS[index] == syndrome)
|
||||
{
|
||||
frame.flip(startIndex + index);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the checksum (Parity 16,8,4,2,1) for data (11 <> 1 ) bits.
|
||||
* Calculates the checksum from the transmitted data bits.
|
||||
*
|
||||
* @param frame - frame containing hamming protected word
|
||||
* @param startIndex - start bit index of the hamming protected word
|
||||
* @return parity value, 0 - 16
|
||||
* @return calculated checksum value
|
||||
*/
|
||||
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))
|
||||
for(int i = frame.nextSetBit(startIndex); i >= startIndex && i < startIndex + 11; i = frame.nextSetBit(i + 1))
|
||||
{
|
||||
calculated ^= CHECKSUMS[i - startIndex];
|
||||
}
|
||||
@@ -109,12 +117,10 @@ public class Hamming16
|
||||
* @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)
|
||||
public static int getSyndrome(BinaryMessage frame, int startIndex)
|
||||
{
|
||||
int calculated = calculateChecksum(frame, startIndex);
|
||||
|
||||
int checksum = frame.getInt(startIndex + 11, startIndex + 15);
|
||||
|
||||
return (checksum ^ calculated);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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;
|
||||
@@ -5,81 +24,37 @@ import io.github.dsheirer.bits.BinaryMessage;
|
||||
/**
|
||||
* Hamming(17,12,3) Error detection and correction utility.
|
||||
*/
|
||||
public class Hamming17
|
||||
public class Hamming17 implements IHamming
|
||||
{
|
||||
private static int[] CHECKSUMS = new int[]{0x1B, 0x1F, 0x1D, 0x1C, 0x0E, 0x07, 0x11, 0x1A, 0x0D, 0x14, 0x0A, 0x05};
|
||||
private static int[] CHECKSUMS = new int[]{0x1B, 0x1F, 0x1D, 0x1C, 0x0E, 0x07, 0x11, 0x1A, 0x0D, 0x14, 0x0A, 0x05,
|
||||
0x10, 0x08, 0x04, 0x02, 0x01};
|
||||
|
||||
/**
|
||||
* Performs error detection and correction of any single-bit errors and detection of any double-bit errors (SECDED)
|
||||
* Calculates the bit error index of the Hamming(17,12,3) protected word that is contained in the binary message
|
||||
* starting at the specified offset.
|
||||
*
|
||||
* @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
|
||||
* @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 or 1000 if more than one bit error detected.
|
||||
*/
|
||||
public static int checkAndCorrect(BinaryMessage frame, int startIndex)
|
||||
public int getErrorIndex(BinaryMessage message, int offset)
|
||||
{
|
||||
int syndrome = getSyndrome(frame, startIndex);
|
||||
int syndrome = getSyndrome(message, offset);
|
||||
|
||||
switch(syndrome)
|
||||
if(syndrome == 0)
|
||||
{
|
||||
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;
|
||||
return NO_ERRORS;
|
||||
}
|
||||
|
||||
for(int index = 0; index < CHECKSUMS.length; index++)
|
||||
{
|
||||
if(syndrome == CHECKSUMS[index])
|
||||
{
|
||||
return index + offset;
|
||||
}
|
||||
}
|
||||
|
||||
return MULTIPLE_ERRORS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,16 +62,14 @@ public class Hamming17
|
||||
*
|
||||
* @param frame - frame containing hamming protected word
|
||||
* @param startIndex - start bit index of the hamming protected word
|
||||
* @return parity value, 0 - 16
|
||||
* @return checksum value
|
||||
*/
|
||||
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))
|
||||
for(int i = frame.nextSetBit(startIndex); i >= startIndex && i < startIndex + 12; i = frame.nextSetBit(i + 1))
|
||||
{
|
||||
calculated ^= CHECKSUMS[i - startIndex];
|
||||
}
|
||||
@@ -112,12 +85,10 @@ public class Hamming17
|
||||
* @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)
|
||||
public static int getSyndrome(BinaryMessage frame, int startIndex)
|
||||
{
|
||||
int calculated = calculateChecksum(frame, startIndex);
|
||||
|
||||
int checksum = frame.getInt(startIndex + 12, startIndex + 16);
|
||||
|
||||
return (checksum ^ calculated);
|
||||
}
|
||||
}
|
||||
|
||||
39
src/main/java/io/github/dsheirer/edac/IHamming.java
Normal file
39
src/main/java/io/github/dsheirer/edac/IHamming.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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;
|
||||
|
||||
/**
|
||||
* Interface for Hamming error index calculator.
|
||||
*/
|
||||
public interface IHamming
|
||||
{
|
||||
int NO_ERRORS = -1;
|
||||
int MULTIPLE_ERRORS = 1000;
|
||||
|
||||
/**
|
||||
* Calculate the error index for the Hamming protected binary sequence
|
||||
* @param message containing the binary sequence
|
||||
* @param offset to the start of the binary sequence
|
||||
* @return -1 if no errors, single-bit error index, or 1000 if there is more than a single-bit error detected.
|
||||
*/
|
||||
int getErrorIndex(BinaryMessage message, int offset);
|
||||
}
|
||||
Reference in New Issue
Block a user