Files
sdrtrunk/src/main/java/io/github/dsheirer/edac/Golay24.java
Steve Pinkham 7fcf035337 edac: correct Golay(24,12) words that are not at the start of the message (#2631)
Golay24.checkAndCorrect indexed the word from bit 0 (getInt(0, 22), xor(12, ...),
flip(index)) and took the parity over the whole message, so it was only right for a
message that is exactly one 24-bit word. P25 TDULC words 2-12 and the DMR voice
superframe words at 24 and 48 were "corrected" in the wrong bits. Decode any other
word in a 24-bit copy and write it back.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 01:29:50 -05:00

198 lines
6.2 KiB
Java

/*
* *****************************************************************************
* 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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Galois 24/12/7 decoder
*/
public class Golay24
{
private final static Logger mLog = LoggerFactory.getLogger(Golay24.class);
/**
* Galois 24/12 checksums generated by:
*
* CRCUtil.generate( 12, 11, 0xC75, 0x0, true );
*/
public static final int[] CHECKSUMS = new int[]
{
0x63A, 0x31D, 0x7B4, 0x3DA, 0x1ED, 0x6CC, 0x366, 0x1B3,
0x6E3, 0x54B, 0x49F, 0x475, 0x400, 0x200, 0x100, 0x080,
0x040, 0x020, 0x010, 0x008, 0x004, 0x002, 0x001
};
private static int calculateChecksum(BinaryMessage message, int startIndex)
{
int calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(startIndex);
i >= startIndex && i < startIndex + 12;
i = message.nextSetBit(i + 1))
{
calculated ^= CHECKSUMS[i - startIndex];
}
return calculated;
}
/**
* Performs error detection and returns a corrected copy of the 24-bit
* message that starts at the start index.
*
* @param message - source message containing startIndex + 24 bits length
* @param startIndex - start of the 24-bit galois 24 protected bit set
* @return - corrected 24-bit galois value
*/
public static int checkAndCorrect(CorrectedBinaryMessage message, int startIndex)
{
//The correction below indexes the word from bit 0 and takes the parity over the whole message, so it is only
//correct for a message that is exactly the 24-bit word. Decode any other word in a 24-bit copy.
if(startIndex != 0 || message.size() != 24)
{
CorrectedBinaryMessage word = new CorrectedBinaryMessage(24);
int original = message.getInt(startIndex, startIndex + 23);
word.load(0, 24, original);
int result = checkAndCorrectWord(word);
int corrected = word.getInt(0, 23);
message.load(startIndex, 24, corrected);
message.incrementCorrectedBitCount(Integer.bitCount(original ^ corrected));
return result;
}
return checkAndCorrectWord(message);
}
/**
* Error detection and correction of a message that is exactly one 24-bit word.
*/
private static int checkAndCorrectWord(CorrectedBinaryMessage message)
{
int startIndex = 0;
boolean parityError = message.cardinality() % 2 != 0;
int syndrome = getSyndrome(message, startIndex);
/* No errors */
if(syndrome == 0)
{
if(parityError)
{
message.flip(startIndex + 23);
message.incrementCorrectedBitCount(1);
return 1;
}
return 0;
}
/* Get original message value */
int original = message.getInt(0, 22);
int index = -1;
int syndromeWeight = 3;
int errors = 0;
while(index < 23)
{
if(index != -1)
{
/* restore the previous flipped bit */
if(index > 0)
{
message.flip(index - 1);
}
message.flip(index);
syndromeWeight = 2;
}
syndrome = getSyndrome(message, startIndex);
if(syndrome > 0)
{
for(int i = 0; i < 23; i++)
{
errors = Integer.bitCount(syndrome);
if(errors <= syndromeWeight)
{
message.xor(12, 11, syndrome);
message.rotateRight(i, startIndex, startIndex + 22);
if(index >= 0)
{
errors++;
}
int corrected = message.getInt(0, 22);
if(Integer.bitCount(original ^ corrected) > 3)
{
return 2;
}
return 1;
}
else
{
message.rotateLeft(startIndex, startIndex + 22);
syndrome = getSyndrome(message, startIndex);
}
}
index++;
}
}
return 2;
}
private static int getSyndrome(BinaryMessage message, int startIndex)
{
int calculated = calculateChecksum(message, startIndex);
int checksum = message.getInt(startIndex + 12, startIndex + 22);
return (checksum ^ calculated);
}
public static void main(String[] args)
{
// CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("F3BB20"));
// CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("F0C5C0"));
CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("AFAC00"));
System.out.println("M:" + bm.toHexString());
int a = Golay24.checkAndCorrect(bm, 0);
System.out.println("M:" + bm.toHexString());
System.out.println("A:" + a);
}
}