mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 Dennis Sheirer
|
||||
* Copyright (C) 2014-2025 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
|
||||
@@ -415,8 +415,8 @@ public class BinaryMessage extends BitSet
|
||||
boolean[] returnValue = null;
|
||||
|
||||
if(startIndex >= 0 &&
|
||||
startIndex < endIndex &&
|
||||
endIndex < mSize)
|
||||
startIndex < endIndex &&
|
||||
endIndex < mSize)
|
||||
{
|
||||
returnValue = new boolean[endIndex - startIndex + 1];
|
||||
|
||||
@@ -447,7 +447,7 @@ public class BinaryMessage extends BitSet
|
||||
if(bits.length > 32)
|
||||
{
|
||||
throw new IllegalArgumentException("Overflow - must be 32 bits "
|
||||
+ "or less to fit into a primitive integer value");
|
||||
+ "or less to fit into a primitive integer value");
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
@@ -676,7 +676,7 @@ public class BinaryMessage extends BitSet
|
||||
if(bits.length > 32)
|
||||
{
|
||||
throw new IllegalArgumentException("Overflow - must be 32 bits "
|
||||
+ "or less to fit into a primitive integer value");
|
||||
+ "or less to fit into a primitive integer value");
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
@@ -711,6 +711,28 @@ public class BinaryMessage extends BitSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the integer value to the field described by the argument.
|
||||
* @param value to set
|
||||
* @param intField describing the indices of the field.
|
||||
*/
|
||||
public void setInt(int value, IntField intField)
|
||||
{
|
||||
for(int x = 0; x < intField.width(); x++)
|
||||
{
|
||||
int mask = 1 << (intField.width() - x - 1);
|
||||
|
||||
if((value & mask) == mask)
|
||||
{
|
||||
set(intField.start() + x);
|
||||
}
|
||||
else
|
||||
{
|
||||
clear(intField.start() + x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte value represented by the bit array
|
||||
*
|
||||
@@ -724,7 +746,7 @@ public class BinaryMessage extends BitSet
|
||||
if(bits.length != 8)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid - there must be 8"
|
||||
+ "indexes to form a proper byte");
|
||||
+ "indexes to form a proper byte");
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
@@ -756,7 +778,7 @@ public class BinaryMessage extends BitSet
|
||||
if(bits.length != 8)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid - there must be 8"
|
||||
+ "indexes to form a proper byte");
|
||||
+ "indexes to form a proper byte");
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
@@ -884,7 +906,7 @@ public class BinaryMessage extends BitSet
|
||||
if(bits.length > 64)
|
||||
{
|
||||
throw new IllegalArgumentException("Overflow - must be 64 bits "
|
||||
+ "or less to fit into a primitive long value");
|
||||
+ "or less to fit into a primitive long value");
|
||||
}
|
||||
|
||||
long value = 0;
|
||||
@@ -916,7 +938,7 @@ public class BinaryMessage extends BitSet
|
||||
if(bits.length > 64)
|
||||
{
|
||||
throw new IllegalArgumentException("Overflow - must be 64 bits "
|
||||
+ "or less to fit into a primitive long value");
|
||||
+ "or less to fit into a primitive long value");
|
||||
}
|
||||
|
||||
long value = 0;
|
||||
@@ -1004,7 +1026,7 @@ public class BinaryMessage extends BitSet
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("BitSetBuffer.getHex() "
|
||||
+ "maximum array length is 63 bits");
|
||||
+ "maximum array length is 63 bits");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1025,7 +1047,7 @@ public class BinaryMessage extends BitSet
|
||||
if(FastMath.abs(end - start) > 32)
|
||||
{
|
||||
throw new IllegalArgumentException("Overflow - must be 32 bits "
|
||||
+ "or less to fit into a primitive integer value");
|
||||
+ "or less to fit into a primitive integer value");
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
@@ -1100,7 +1122,7 @@ public class BinaryMessage extends BitSet
|
||||
if(FastMath.abs(end - start) > 64)
|
||||
{
|
||||
throw new IllegalArgumentException("Overflow - must be 64 bits "
|
||||
+ "or less to fit into a primitive long value");
|
||||
+ "or less to fit into a primitive long value");
|
||||
}
|
||||
|
||||
long value = 0;
|
||||
@@ -1255,7 +1277,7 @@ public class BinaryMessage extends BitSet
|
||||
if(!message.matches("[01]*"))
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"Message must contain only zeros and ones");
|
||||
"Message must contain only zeros and ones");
|
||||
}
|
||||
|
||||
BinaryMessage buffer = new BinaryMessage(message.length());
|
||||
|
||||
1099
src/main/java/io/github/dsheirer/edac/bch/BCH.java
Normal file
1099
src/main/java/io/github/dsheirer/edac/bch/BCH.java
Normal file
File diff suppressed because it is too large
Load Diff
38
src/main/java/io/github/dsheirer/edac/bch/BCH_63.java
Normal file
38
src/main/java/io/github/dsheirer/edac/bch/BCH_63.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.bch;
|
||||
|
||||
/**
|
||||
* BCH(63) decoder base implementation that uses GF(2^6) and standard primitive polynomial.
|
||||
*/
|
||||
public abstract class BCH_63 extends BCH
|
||||
{
|
||||
public static final int M = 6;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param k data bits size
|
||||
* @param t error detection and correction capacity of the generator polynomial used to create the codewords.
|
||||
*/
|
||||
public BCH_63(int k, int t)
|
||||
{
|
||||
super(M, k, t, PRIMITIVE_POLYNOMIAL_GF_63);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.bch;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
|
||||
/**
|
||||
* APCO 25 BCH(63,16,23) code with T=11 error bit correction capacity.
|
||||
*/
|
||||
public class BCH_63_16_23_P25 extends BCH_63
|
||||
{
|
||||
public static final int K = 16;
|
||||
private static final int T = 11; // Error-correcting capability
|
||||
public static final IntField NAC_FIELD = IntField.length12(0);
|
||||
public static final IntField DUID_FIELD = IntField.length4(12);
|
||||
|
||||
/**
|
||||
* Constructs a BCH decoder instance for processing APCO25 BCH(63,16,23) protected NID codewords
|
||||
*/
|
||||
public BCH_63_16_23_P25()
|
||||
{
|
||||
super(K, T);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(CorrectedBinaryMessage message)
|
||||
{
|
||||
super.decode(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to error correct the NID message. If the message is uncorrectable, overwrite the message NAC field with
|
||||
* the most frequently/recently observed NAC value and attempt to correct the message a second time.
|
||||
* @param message to correct
|
||||
* @param observedNAC that can be used to attempt a second correction on an uncorrectable message.
|
||||
*/
|
||||
public void decode(CorrectedBinaryMessage message, int observedNAC)
|
||||
{
|
||||
decode(message);
|
||||
|
||||
if(message.getCorrectedBitCount() == BCH.MESSAGE_NOT_CORRECTED && observedNAC > 0)
|
||||
{
|
||||
//Check to see if the message NAC is different than the observed NAC ... overwrite and try again.
|
||||
if(message.getInt(NAC_FIELD) != observedNAC)
|
||||
{
|
||||
message.setInt(observedNAC, NAC_FIELD);
|
||||
decode(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.bch;
|
||||
|
||||
/**
|
||||
* Finds primitive polynomials for a Galois Fields (GF) of size M.
|
||||
*/
|
||||
public class GaloisFieldPrimitiveFinder
|
||||
{
|
||||
private int M;
|
||||
private int N;
|
||||
|
||||
/**
|
||||
* Constructs an instance of primitive finder for m for GF(2 ^ m)
|
||||
* @param m size of galois field
|
||||
*/
|
||||
public GaloisFieldPrimitiveFinder(int m)
|
||||
{
|
||||
M = m;
|
||||
N = ((1 << M) - 1);
|
||||
}
|
||||
|
||||
public boolean isPrimitive(int polynomial)
|
||||
{
|
||||
int x = 1;
|
||||
int k = 1 << M;
|
||||
|
||||
for(int i = 0; i < N; i++)
|
||||
{
|
||||
if(i > 0 && (x == 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
x <<= 1;
|
||||
|
||||
if((x & k) > 0)
|
||||
{
|
||||
x ^= polynomial;
|
||||
}
|
||||
}
|
||||
|
||||
return (x == 1);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
GaloisFieldPrimitiveFinder finder;
|
||||
|
||||
//Search: M=2 to M=9
|
||||
for(int m = 2; m < 10; m++)
|
||||
{
|
||||
finder = new GaloisFieldPrimitiveFinder(m);
|
||||
System.out.println("Searching for M: " + m);
|
||||
for(int i = 0; i < (1 << m); i++)
|
||||
{
|
||||
//Skip polynomials divisible by X or X+1
|
||||
int candidate = i | (1 << m);
|
||||
if(finder.isPrimitive(candidate))
|
||||
{
|
||||
System.out.println("M:" + m + " Primitive: " + String.format("0x%X", candidate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.bch;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.bits.IntField;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for the BCH decoder for APCO25 NID fragments protected by a BCH(63,16,23) code
|
||||
*/
|
||||
public class BCH_63_16_23_P25_Test
|
||||
{
|
||||
/**
|
||||
* NAC: 1, DUID:0 (HDU), line 12 in the FDMA Table 19 NID Generator matrix.
|
||||
*
|
||||
* Octal: 00 0020 1430 2667 7236 1044
|
||||
* Bin: 0 000 000 000 010 000 001 100 011 000 010 110 110 111 111 010 011 110 001 000 100 100
|
||||
* Bin: 0000 0000 0001 0000 0011 0001 1000 0101 1011 0111 1110 1001 1110 0010 0010 0100
|
||||
* Hex: 0 0 1 0 3 1 8 5 B 7 E 9 E 2 2 4
|
||||
* Hex: 00103185B7E9E224
|
||||
*/
|
||||
|
||||
private static final long[] P25_NID_BCH_63_16_GENERATOR_MATRIX = new long[16];
|
||||
|
||||
static
|
||||
{
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[0] = Long.parseLong("6331141367235452", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[1] = Long.parseLong("5265521614723276", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[2] = Long.parseLong("4603711461164164", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[3] = Long.parseLong("2301744630472072", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[4] = Long.parseLong("7271623073000466", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[5] = Long.parseLong("5605650752635660", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[6] = Long.parseLong("2702724365316730", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[7] = Long.parseLong("1341352172547354", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[8] = Long.parseLong("0560565075263566", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[9] = Long.parseLong("6141333751704220", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[10] = Long.parseLong("3060555764742110", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[11] = Long.parseLong("1430266772361044", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[12] = Long.parseLong("0614133375170422", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[13] = Long.parseLong("6037114611641642", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[14] = Long.parseLong("5326507063515373", 8);
|
||||
P25_NID_BCH_63_16_GENERATOR_MATRIX[15] = Long.parseLong("4662302756473127", 8);
|
||||
}
|
||||
|
||||
private static final IntField NAC_FIELD = IntField.length12(0);
|
||||
private static final IntField DUID_FIELD = IntField.length4(12);
|
||||
|
||||
/**
|
||||
* Creates a valid P25 NID 64-bit message containing the NAC and DUID values and calculated parity bits.
|
||||
* @param nac
|
||||
* @param duid
|
||||
* @return
|
||||
*/
|
||||
public static CorrectedBinaryMessage create(int nac, int duid)
|
||||
{
|
||||
CorrectedBinaryMessage cbm = new CorrectedBinaryMessage(64);
|
||||
cbm.setInt(nac, NAC_FIELD);
|
||||
cbm.setInt(duid, DUID_FIELD);
|
||||
|
||||
long parity = 0;
|
||||
for(int x = 0; x < 16; x++)
|
||||
{
|
||||
if(cbm.get(x))
|
||||
{
|
||||
parity ^= P25_NID_BCH_63_16_GENERATOR_MATRIX[x];
|
||||
}
|
||||
}
|
||||
|
||||
cbm.load(16, 48, parity);
|
||||
return cbm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the decoder can correct all 1-bit errors across the message bits 0-62. Note: this test ignores the
|
||||
* unused parity bit at index 63.
|
||||
* @param decoder that can correct one-bit errors.
|
||||
* @param original that is fully formed across indices 0-62 with BCH parity bits
|
||||
* @return true if the test is successful
|
||||
*/
|
||||
public static List<Integer> testOneBitErrors(BCH decoder, CorrectedBinaryMessage original, List<Integer> errors)
|
||||
{
|
||||
//Induce an additional 1 bit error and test across each bit position.
|
||||
for(int error = 0; error < 63; error++)
|
||||
{
|
||||
if(!errors.contains(error))
|
||||
{
|
||||
CorrectedBinaryMessage localCopy = original.getSubMessage(0, 62);
|
||||
|
||||
for(int additionalError: errors)
|
||||
{
|
||||
localCopy.flip(additionalError);
|
||||
}
|
||||
localCopy.flip(error);
|
||||
|
||||
decoder.decode(localCopy);
|
||||
localCopy.xor(original);
|
||||
|
||||
if(localCopy.cardinality() > 0)
|
||||
{
|
||||
errors.add(error);
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static List<Integer> testMultiBitErrors(BCH decoder, CorrectedBinaryMessage original, List<Integer> errors)
|
||||
{
|
||||
System.out.print("\rTesting: " + errors);
|
||||
if(errors.size() + 2 >= decoder.getMaxErrorCorrection())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
for(int error = 0; error < 63; error++)
|
||||
{
|
||||
if(!errors.contains(error))
|
||||
{
|
||||
List<Integer> errorsCopy = new ArrayList<>(errors);
|
||||
errorsCopy.add(error);
|
||||
List<Integer> result = testOneBitErrors(decoder, original, errorsCopy);
|
||||
|
||||
if(!result.isEmpty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
errorsCopy = new ArrayList<>(errors);
|
||||
errorsCopy.add(error);
|
||||
result = testMultiBitErrors(decoder, original, errorsCopy);
|
||||
|
||||
if(!result.isEmpty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
System.out.println("Starting ....");
|
||||
BCH decoder = new BCH_63_16_23_P25();
|
||||
CorrectedBinaryMessage message = create(534, 2);
|
||||
|
||||
//Test correcting 1-11 bit errors
|
||||
message.clear(63); //Clear the parity bit so that it doesn't interfere with the results validation
|
||||
List<Integer> errors = new ArrayList<>();
|
||||
List<Integer> results = testMultiBitErrors(decoder, message, errors);
|
||||
System.out.println("Test Results: " + (results.isEmpty() ? "PASS" : "FAIL") + " Resulse: " + results);
|
||||
|
||||
//Test 11 bit errors.
|
||||
CorrectedBinaryMessage errorMessage = message.getSubMessage(0, 63);
|
||||
int[] errorArray = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
for(int error: errorArray)
|
||||
{
|
||||
errorMessage.flip(error);
|
||||
}
|
||||
|
||||
decoder.decode(errorMessage);
|
||||
|
||||
System.out.println(" Original: " + message);
|
||||
System.out.println("Corrected: " + errorMessage + " Corrected Bit Count: " + errorMessage.getCorrectedBitCount());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user