/* * ***************************************************************************** * 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 * **************************************************************************** */ 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); } }