312 lines
7.6 KiB
C
312 lines
7.6 KiB
C
/*---------------------------------------------------------------------------*\
|
|
|
|
FILE........: golay23.c
|
|
AUTHOR......: Tomas Härdin & David Rowe
|
|
DATE CREATED: 3 March 2013
|
|
|
|
To test:
|
|
|
|
src$ gcc golay23.c -o golay23 -Wall -O3 -DGOLAY23_UNITTEST && ./golay23
|
|
src$ gcc golay23.c -o golay23 -Wall -O3 -DGOLAY23_UNITTEST -DRUN_TIME_TABLES && ./golay23
|
|
src$ gcc golay23.c -o golay23 -Wall -O3 -DGOLAY23_UNITTEST -DNO_TABLES && ./golay23
|
|
|
|
To generate tables:
|
|
src$ gcc golay23.c -o golay23 -Wall -O3 -DGOLAY23_MAKETABLES && ./golay23
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
Copyright (C) 2016 Tomas Härdin & David Rowe
|
|
|
|
All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License version 2.1, as
|
|
published by the Free Software Foundation. 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 Lesser General Public License
|
|
along with this program; if not,see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#ifdef GOLAY23_MAKETABLES
|
|
#define RUN_TIME_TABLES
|
|
#endif
|
|
|
|
#ifndef NO_TABLES
|
|
#ifdef RUN_TIME_TABLES
|
|
int static encoding_table[4096];
|
|
int static decoding_table[2048];
|
|
static int inited = 0;
|
|
#else
|
|
//default is to use precomputed tables
|
|
#include "golayenctable.h"
|
|
#include "golaydectable.h"
|
|
#endif
|
|
#endif
|
|
|
|
//since we want to avoid bit-reversing inside syndrome() we bit-reverse the polynomial instead
|
|
#define GOLAY_POLYNOMIAL 0xC75 //AE3 reversed
|
|
|
|
int golay23_syndrome(int c) {
|
|
//could probably be done slightly smarter, but works
|
|
int x;
|
|
for (x = 11; x >= 0; x--) {
|
|
if (c & ((1<<11) << x)) {
|
|
c ^= GOLAY_POLYNOMIAL << x;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
#define popcount __builtin_popcount
|
|
#elif defined(__MSC_VER)
|
|
#include <intrin.h>
|
|
#define popcount __popcnt
|
|
#else
|
|
static int popcount(unsigned int c) {
|
|
int ret = 0;
|
|
while (c) {
|
|
if (c & 1) {
|
|
ret++;
|
|
}
|
|
c >>= 1;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if defined(NO_TABLES) || defined(RUN_TIME_TABLES)
|
|
static int golay23_encode_no_tables(int c) {
|
|
c <<= 11;
|
|
return golay23_syndrome(c) | c;
|
|
}
|
|
#endif
|
|
|
|
#ifdef NO_TABLES
|
|
static int unrotate(unsigned int c, int x) {
|
|
return ((c << x) & 0x7FFFFF) | (c >> (23 - x));
|
|
}
|
|
|
|
static int golay23_decode_no_tables(int c) {
|
|
//TODO: optimize?
|
|
int x;
|
|
c = unrotate(c, 12);
|
|
|
|
for (x = 0; x < 23; x++) {
|
|
int t;
|
|
int s = golay23_syndrome(c);
|
|
|
|
if (popcount(s) <= 3) {
|
|
return unrotate(c ^ s, x) & 0xFFF;
|
|
}
|
|
|
|
for (t = 0; t < 23; t++) {
|
|
int c2 = c ^ (1 << t);
|
|
int s = golay23_syndrome(c2);
|
|
|
|
if (popcount(s) <= 2) {
|
|
return unrotate(c2 ^ s, x) & 0xFFF;
|
|
}
|
|
}
|
|
|
|
//rotate
|
|
c = (c >> 1) | ((c & 1) << 22);
|
|
}
|
|
|
|
//shouldn't reach here..
|
|
assert("Something is wrong with golay23_decode_no_tables()..");
|
|
return c & 0xFFF;
|
|
}
|
|
#endif
|
|
|
|
void golay23_init(void) {
|
|
#ifdef RUN_TIME_TABLES
|
|
int x, y, z;
|
|
inited = 1;
|
|
for (x = 0; x < 4096; x++) {
|
|
encoding_table[x] = golay23_encode_no_tables(x);
|
|
}
|
|
|
|
decoding_table[0] = 0;
|
|
//1-bit errors
|
|
for (x = 0; x < 23; x++) {
|
|
int d = 1<<x;
|
|
decoding_table[golay23_syndrome(d)] = d;
|
|
}
|
|
//2-bit errors
|
|
for (x = 0; x < 22; x++) {
|
|
for (y = x+1; y < 23; y++) {
|
|
int d = (1<<x) | (1<<y);
|
|
decoding_table[golay23_syndrome(d)] = d;
|
|
}
|
|
}
|
|
//3-bit errors
|
|
for (x = 0; x < 21; x++) {
|
|
for (y = x+1; y < 22; y++) {
|
|
for (z = y+1; z < 23; z++) {
|
|
int d = (1<<x) | (1<<y) | (1<<z);
|
|
decoding_table[golay23_syndrome(d)] = d;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int golay23_encode(int c) {
|
|
assert(c >= 0 && c <= 0xFFF);
|
|
#ifdef RUN_TIME_TABLES
|
|
assert(inited);
|
|
#endif
|
|
|
|
#ifdef NO_TABLES
|
|
return golay23_encode_no_tables(c);
|
|
#else
|
|
return encoding_table[c];
|
|
#endif
|
|
}
|
|
|
|
int golay23_decode(int c) {
|
|
assert(c >= 0 && c <= 0x7FFFFF);
|
|
#ifdef RUN_TIME_TABLES
|
|
assert(inited);
|
|
#endif
|
|
|
|
#ifdef NO_TABLES
|
|
//duplicate old golay23_decode()'s shift
|
|
return unrotate(golay23_decode_no_tables(c), 11);
|
|
#else
|
|
//message is shifted 11 places left in the return value
|
|
return c ^ decoding_table[golay23_syndrome(c)];
|
|
#endif
|
|
}
|
|
|
|
int golay23_count_errors(int recd_codeword, int corrected_codeword) {
|
|
return popcount(recd_codeword ^ corrected_codeword);
|
|
}
|
|
|
|
/**
|
|
* Table generation and testing code below
|
|
*/
|
|
|
|
#ifdef GOLAY23_MAKETABLES
|
|
#include <stdio.h>
|
|
|
|
int main(void) {
|
|
int x;
|
|
//generate and dump
|
|
golay23_init();
|
|
|
|
FILE *enc = fopen("golayenctable.h", "w");
|
|
FILE *dec = fopen("golaydectable.h", "w");
|
|
|
|
fprintf(enc, "/* Generated by golay23.c -DGOLAY23_MAKETABLE */\n\
|
|
\n\
|
|
const int static encoding_table[]={\n");
|
|
for (x = 0; x < 4096; x++) {
|
|
fprintf(enc, x < 4095 ? " 0x%x,\n" : " 0x%x\n", encoding_table[x]);
|
|
}
|
|
fprintf(enc, "};\n");
|
|
|
|
fprintf(dec, "/* Generated by golay23.c -DGOLAY23_MAKETABLE */\n\
|
|
\n\
|
|
const int static decoding_table[]={\n");
|
|
for (x = 0; x < 2048; x++) {
|
|
fprintf(dec, x < 2047 ? " 0x%x,\n" : " 0x%x\n", decoding_table[x]);
|
|
}
|
|
fprintf(dec, "};\n");
|
|
|
|
fclose(enc);
|
|
fclose(dec);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(GOLAY23_UNITTEST)
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <memory.h>
|
|
|
|
int main(void) {
|
|
int c;
|
|
|
|
golay23_init();
|
|
|
|
//keep track of whether every single codeword has been checked
|
|
char *checkmask = malloc(1<<23);
|
|
memset(checkmask, 0, 1<<23);
|
|
|
|
//step through all possible messages
|
|
for (c = 0; c < (1<<12); c++) {
|
|
int g23 = golay23_encode(c);
|
|
int x,y,z;
|
|
checkmask[g23] = 1;
|
|
int c2 = golay23_decode(g23) >> 11;
|
|
|
|
printf("%03x -> %06x %03x\n", c, g23, c2);
|
|
|
|
if (c != c2) {
|
|
printf("Bad!\n");
|
|
exit(1);
|
|
}
|
|
|
|
//test the code by flipping every combination of one, two and three bits
|
|
for (x = 0; x < 23; x++) {
|
|
int flipped = g23 ^ (1<<x);
|
|
checkmask[flipped] = 1;
|
|
int c2 = golay23_decode(flipped) >> 11;
|
|
if (c != c2) {
|
|
printf("Bad!\n");
|
|
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < 22; x++) {
|
|
for (y = x+1; y < 23; y++) {
|
|
int flipped = g23 ^ (1<<x) ^ (1<<y);
|
|
checkmask[flipped] = 1;
|
|
int c2 = golay23_decode(flipped) >> 11;
|
|
if (c != c2) {
|
|
printf("Bad!\n");
|
|
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < 21; x++) {
|
|
for (y = x+1; y < 22; y++) {
|
|
for (z = y+1; z < 23; z++) {
|
|
int flipped = g23 ^ (1<<x) ^ (1<<y) ^ (1<<z);
|
|
checkmask[flipped] = 1;
|
|
int c2 = golay23_decode(flipped) >> 11;
|
|
if (c != c2) {
|
|
printf("Bad!\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//did we check every codeword?
|
|
for (c = 0; c < (1<<23); c++) {
|
|
if (checkmask[c] != 1) {
|
|
printf("%06x unchecked!\n", c);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
printf("Everything checks out\n");
|
|
free(checkmask);
|
|
return 0;
|
|
}
|
|
#endif
|